亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue實(shí)現(xiàn)虛擬滾動(dòng)的示例詳解

 更新時(shí)間:2023年10月25日 08:35:56   作者:通往自由之路  
虛擬滾動(dòng)或者移動(dòng)是指禁止原生滾動(dòng),之后通過(guò)監(jiān)聽瀏覽器的相關(guān)事件實(shí)現(xiàn)模擬滾動(dòng),下面小編就來(lái)和大家詳細(xì)介紹一下vue實(shí)現(xiàn)虛擬滾動(dòng)的示例代碼,需要的可以參考下

虛擬滾動(dòng)或者移動(dòng)是指禁止原生滾動(dòng),之后通過(guò)監(jiān)聽瀏覽器的相關(guān)事件實(shí)現(xiàn)模擬滾動(dòng)。所以虛擬滾動(dòng)包含兩部分內(nèi)容

1.禁止原生滾動(dòng):將cssoverfow屬性設(shè)置為hidden。這樣即便是內(nèi)容高度或者寬度超過(guò)了盒子的寬度或者高度也無(wú)法進(jìn)行滾動(dòng)了

<div id="vs-container">
  <div id="vs-content">
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
    <p>內(nèi)容</p>
  </div>
</div>
<style>
#vs-container {
  overflow:hidden;
  height:100px;
}
#vs-content {
  height:200px;
}
</style>

2.模擬滾動(dòng):通過(guò)監(jiān)聽鼠標(biāo)的wheel事件,調(diào)整內(nèi)容位置,從而形成滾動(dòng)效果;通過(guò)監(jiān)聽onmousedown、onmousemove、onmouseup實(shí)現(xiàn)虛擬滾動(dòng)條的移動(dòng)

解決什么問(wèn)題

  • 服務(wù)虛擬列表,尤其不定高度內(nèi)容的虛擬列表實(shí)現(xiàn);不定高內(nèi)容虛擬列表在滑動(dòng)過(guò)程中由于滾動(dòng)速度大于渲染速度導(dǎo)致過(guò)快滑動(dòng)時(shí)出現(xiàn)白屏現(xiàn)象。如果有虛擬滾動(dòng),則可以先進(jìn)行數(shù)據(jù)渲染待渲染完畢再進(jìn)行滾動(dòng),這樣就徹底解決了白屏問(wèn)題。
  • 在我工作中遇到使用虛擬列表實(shí)現(xiàn)不定高數(shù)據(jù)渲染問(wèn)題,正好也出現(xiàn)了白屏問(wèn)題

Dom結(jié)構(gòu)

本文使用vue2實(shí)現(xiàn)虛擬滾動(dòng),DOM結(jié)構(gòu)以及一些初始化數(shù)據(jù)如下

內(nèi)容和盒子

<template>
  <div id="vs-container" ref="container">
    <div id="vs-content" :style="{ transform: contentTransform }">
      <p :key="num" v-for="num in list">{{ num }}</p>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      list: 1000,
      contentOffset: 0
    }
  },
  computed: {
    contentTransform () {
      return `translate3d(${this.contentOffset}px)`
    }
  }
}
</script>
<style lang="scss" scoped>
#vs-container {
  margin-top: 200px;
  margin-left: 20px;
  height: 200px;
  border: 1px solid #333;
  overflow: hidden;
  width: 500px;
  position: relative;
  box-sizing: border-box;
}
</style>

上述代碼內(nèi)容id為vs-content,盒子id為vs-container,盒子高度200px,并且禁止盒子的原生滾動(dòng),設(shè)置盒子overflowhidden。contentTransform用來(lái)動(dòng)態(tài)變化滾動(dòng)位置。給盒子增加ref,標(biāo)記container為后面開發(fā)使用。

虛擬滾動(dòng)條

在上述代碼中添加虛擬滾動(dòng)條,虛擬滾動(dòng)條包括滑道,其ref設(shè)置為slider;還包括手柄,手柄ref為handle

<template>
  <div id="vs-container" ref="container">
    <div id="vs-content" :style="{ transform: contentTransform }">
      <p :key="num" v-for="num in list">{{ num }}</p>
    </div>
    <div id="vs-slider" ref="slider">
      <div
        id="vs-handle"
        :style="{ transform: handleTransformt }"
        ref="handle"
      ></div>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      ...
      handleOffset: 0
    }
  },
  computed: {
    ...
    handleTransform () {
      return `translateY(${this.handleOffset}px)`
    }
  }
}
</script>
<style lang="scss" scoped>
#vs-container {
  ...
  #vs-slider {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 10px;
    height:20px;
    box-sizing: border-box;
    background-color: #6b6b6b;
    #vs-handle {
      background-color: #f1f2f3;
      cursor: pointer;
      border-radius: 10px;
    }
  }
}
</style>

contentTransform用來(lái)動(dòng)態(tài)變化虛擬滾動(dòng)條的滾動(dòng)位置,設(shè)置滾動(dòng)條高度20px。到此處整個(gè)虛擬滾動(dòng)示例長(zhǎng)這樣

虛擬滾動(dòng)實(shí)現(xiàn)

實(shí)現(xiàn)虛擬滾動(dòng),開頭說(shuō)了模擬滾動(dòng)原理:通過(guò)監(jiān)聽鼠標(biāo)的wheel事件,調(diào)整內(nèi)容位置,從而形成滾動(dòng)效果;通過(guò)監(jiān)聽onmousedown、onmousemove、onmouseup實(shí)現(xiàn)虛擬滾動(dòng)條的移動(dòng)。

本文使用translateY值的變化實(shí)現(xiàn)內(nèi)容區(qū)或虛擬滾動(dòng)條的滾動(dòng)。本文只實(shí)現(xiàn)垂直方向上的滾動(dòng),水平方向上的滾動(dòng)原理基本一致。

監(jiān)聽鼠標(biāo)滾輪或觸屏版實(shí)現(xiàn)內(nèi)容區(qū)滾動(dòng)

使用上文中ref獲取相應(yīng)的dom元素,然后給內(nèi)容區(qū)盒子container綁定wheel事件。

監(jiān)聽wheel事件獲取事件對(duì)象的wheelDeltaY,其含義為

返回一個(gè)整型數(shù),表示垂直滾動(dòng)量。

在谷歌瀏覽器下,如果是觸屏版滑動(dòng)返回0、1、2、3……或者0、-1、-2、-3……,如果是鼠標(biāo)滾輪滾動(dòng)返回150或-150。具體實(shí)現(xiàn)內(nèi)容區(qū)滾動(dòng)

<template>
  <div id="vs-container" ref="container">
    <div id="vs-content" :style="{ transform: contentTransform }">
      <p :key="num" v-for="num in list">{{ num }}</p>
    </div>
    <div id="vs-slider" ref="slider">
      <div
        id="vs-handle"
        :style="{ transform: handleTransform, height: handleStyleHeight }"
        ref="handle"
      ></div>
    </div>
  </div>
</template>
<script>
export default {
  methods: {
    bindContainerEvent () {
      const { $container } = this.$element
      const contentSpace = $container.scrollHeight - $container.offsetHeight
      const bindContainerOffset = (event) => {
        event.preventDefault()
        this.contentOffset += event.wheelDeltaY
        if (this.contentOffset < 0) {
          this.contentOffset = Math.max(this.contentOffset, -contentSpace)
        } else {
          this.contentOffset = 0
        }
      }
      $container.addEventListener('wheel', bindContainerOffset)
      this.unbindContainerEvent = () => {
        $container.removeEventListener('wheel', bindContainerOffset)
      }
    },
     // 獲取dom元素
    saveHtmlElementById () {
      const { container, slider, handle } = this.$refs
      this.$element = {
        $container: container,
        $slider: slider,
        $handle: handle
      }
      this.bindContainerEvent()
    }
  },
  created () {
    this.$nextTick(() => {
      this.saveHtmlElementById()
    })
  },
  beforeDestroy () {
    this.unbindContainerEvent()
  }
}
</script>

event.wheelDeltaY值為負(fù)值,表示內(nèi)容區(qū)向上滾動(dòng),反之內(nèi)容區(qū)向下滾動(dòng)。之后需要限制滾動(dòng)區(qū)間

if (this.contentOffset < 0) {
   this.contentOffset = Math.max(this.contentOffset, -contentSpace)
} else {
   this.contentOffset = 0
}

內(nèi)容區(qū)向上移動(dòng)的最大距離為contentSpace,向下滾動(dòng)的最大距離為0。

監(jiān)聽虛擬滾動(dòng)條事件實(shí)現(xiàn)內(nèi)容區(qū)滾動(dòng)

監(jiān)聽虛擬滾動(dòng)條的onmousedown事件,之后使用手柄偏移量handleOffset以及計(jì)算屬性handleTransform實(shí)現(xiàn)手柄的上下滑動(dòng)

export default {
  data () {
    return {
      ...
      handleOffset: 0,
    }
  },
  computed: {
    handleTransform () {
      return `translateY(${this.handleOffset}px)`
    }
  },
  methods: {
    bindHandleEvent () {
      const { $slider, $handle } = this.$element
      const handleSpace = $slider.offsetHeight - this.handleHeight
      $handle.onmousedown = (e) => {
        const startY = e.clientY
        const startTop = this.handleOffset
        window.onmousemove = (e) => {
          const deltaX = e.clientY - startY
          this.handleOffset =
            startTop + deltaX < 0
              ? 0
              : Math.min(startTop + deltaX, handleSpace)
        }

        window.onmouseup = function () {
          window.onmousemove = null
          window.onmouseup = null
        }
      }
    },
    saveHtmlElementById () {
      ...
      this.bindHandleEvent()
    }
  },
  created () {
    this.$nextTick(() => {
      this.saveHtmlElementById()
    })
  }
}

基本實(shí)現(xiàn)邏輯:在鼠標(biāo)按下時(shí)記錄當(dāng)前位置,鼠標(biāo)移動(dòng)則將移動(dòng)值通過(guò)一定的轉(zhuǎn)換邏輯賦給手柄偏移量,同時(shí)限制手柄移動(dòng)上下邊界

this.handleOffset =
            startTop + deltaX < 0
              ? 0
              : Math.min(startTop + deltaX, handleSpace)

最小為0,最大為handleSpace

關(guān)聯(lián)手柄移動(dòng)與內(nèi)容區(qū)移動(dòng)

到此處已經(jīng)實(shí)現(xiàn)了滾動(dòng)條的移動(dòng)和內(nèi)容區(qū)的移動(dòng)。但二者還是各自為戰(zhàn)的,需要關(guān)聯(lián)起來(lái)。具體關(guān)聯(lián)邏輯是關(guān)聯(lián)內(nèi)容區(qū)最大滾動(dòng)距離和虛擬滾動(dòng)條最大移動(dòng)距離。二者比例就是移動(dòng)距離的數(shù)值關(guān)系。

增加關(guān)聯(lián)方法transferOffset

  methods: {
    transferOffset (to = 'handle') {
      const { $container, $slider } = this.$element
      const contentSpace = $container.scrollHeight - $container.offsetHeight
      const handleSpace = $slider.offsetHeight - this.handleHeight
      const assistRatio = handleSpace / contentSpace // 小于1
      const _this = this
      const computedOffset = {
        handle () {
          return -_this.contentOffset * assistRatio
        },
        content () {
          return -_this.handleOffset / assistRatio
        }
      }
      return computedOffset[to]()
    }
  }

contentSpace為內(nèi)容最大滾動(dòng)距離,handleSpace為手柄最大移動(dòng)距離。assistRatio為二者比例。轉(zhuǎn)換對(duì)象computedOffset包含兩個(gè)方法,分別是通過(guò)內(nèi)容移動(dòng)距離轉(zhuǎn)為手柄移動(dòng)距離和通過(guò)手柄移動(dòng)距離轉(zhuǎn)為內(nèi)容移動(dòng)距離。使用轉(zhuǎn)換方法

  methods: {
    bindContainerEvent () {
      ...
      const updateHandleOffset = () => {
        // 使用關(guān)聯(lián)方法
        this.handleOffset = this.transferOffset()
      }
      $container.addEventListener('wheel', bindContainerOffset)
      // 給手柄事件在增加一個(gè)訂閱方法
      $container.addEventListener('wheel', updateHandleOffset)
      this.unbindContainerEvent = () => {
        $container.removeEventListener('wheel', bindContainerOffset)
        $container.removeEventListener('wheel', updateHandleOffset)
      }
    },
    bindHandleEvent () {
      const { $slider, $handle } = this.$element
      const handleSpace = $slider.offsetHeight - this.handleHeight
      $handle.onmousedown = (e) => {
        const startY = e.clientY
        const startTop = this.handleOffset
        window.onmousemove = (e) => {
          ...
          // 使用關(guān)聯(lián)方法
          this.contentOffset = this.transferOffset('content')
        }

        window.onmouseup = function () {
          window.onmousemove = null
          window.onmouseup = null
        }
      }
    }
  },
  beforeDestroy () {
    this.unbindContainerEvent()
  }

到此虛擬滾動(dòng)基本實(shí)現(xiàn),看下效果

優(yōu)化

動(dòng)態(tài)設(shè)置手柄高度

默認(rèn)將手柄高度設(shè)置為20px,這實(shí)際是不符合實(shí)際滾動(dòng)條高度變化規(guī)則的。實(shí)際內(nèi)容區(qū)高度和內(nèi)容區(qū)盒子高度相差越大則手柄高度越小反之越大。本文虛擬滾動(dòng)為了方便操作可以人為限制手柄最小高度。

優(yōu)化手柄的高度邏輯,增加手柄高度屬性,以及計(jì)算屬性handleStyleHeight,限制手柄最小尺寸為20px,同時(shí)再增加手柄高度的初始化方法initHandleHeight

<template>
  <div id="vs-container" ref="container">
    <div id="vs-content" :style="{ transform: contentTransform }">
      <p :key="num" v-for="num in list">{{ num }}</p>
    </div>
    <div id="vs-slider" ref="slider">
      <div
        id="vs-handle"
        :style="{ transform: handleTransform, height: handleStyleHeight }"
        ref="handle"
      ></div>
    </div>
  </div>
</template>
<script>
const HandleMixHeight = 20
export default {
  data () {
    return {
      ...
      handleHeight: HandleMixHeight
    }
  },
  computed: {
    ...
    handleStyleHeight () {
      return `${this.handleHeight}px`
    }
  },
  methods: {
    ...
    initHandleHeight () {
      const { $container, $slider } = this.$element
      // 根據(jù)比例變化
      this.handleHeight =
        ($slider.offsetHeight * $container.offsetHeight) /
        $container.scrollHeight
      // 最小值為HandleMixHeight
      if (this.handleHeight < HandleMixHeight) {
        this.handleHeight = HandleMixHeight
      }
    }
  },
  created () {
    this.$nextTick(() => {
      this.saveHtmlElementById()
    })
  }
}
</script>

禁止選中文本

在上文中的效果圖中也可以看出,當(dāng)鼠標(biāo)拖動(dòng)滾動(dòng)條時(shí),內(nèi)容區(qū)文本被選中了。這樣體驗(yàn)很不好,對(duì)手柄和滑道添加禁止選中,使用css實(shí)現(xiàn)

<style lang="scss" scoped>
#vs-container {
  ...
  #vs-slider {
    ...
    -webkit-user-select: none; /* Safari/Chrome */
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* Internet Explorer/Edge */
    user-select: none; /* Standard */
    #vs-handle {
      ...
      -webkit-user-select: none; /* Safari/Chrome */
      -moz-user-select: none; /* Firefox */
      -ms-user-select: none; /* Internet Explorer/Edge */
      user-select: none; /* Standard */
    }
  }
}
</style>

總結(jié)

本文是對(duì)虛擬滾動(dòng)的一種實(shí)現(xiàn)。具體是通過(guò)對(duì)wheel事件的監(jiān)聽模擬內(nèi)容的移動(dòng);通過(guò)對(duì)onmousedown、onmousemove、onmouseup的監(jiān)聽實(shí)現(xiàn)虛擬滾動(dòng)條的移動(dòng)。當(dāng)然不管是內(nèi)容的移動(dòng)還是虛擬滾動(dòng)條的移動(dòng)都需要在一個(gè)閉區(qū)間內(nèi)。

本文有2個(gè)沒(méi)有處理的點(diǎn)

  • 不需要滾動(dòng)條的情況
  • 滾動(dòng)條手柄的上下部分

感興趣可以進(jìn)一步完善。本文的重點(diǎn)是垂直方向虛擬滾動(dòng)的基本實(shí)現(xiàn),是為后面不定高虛擬列表服務(wù)。

以上就是vue實(shí)現(xiàn)虛擬滾動(dòng)的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于vue虛擬滾動(dòng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue.js修改el-popover組件顯示位置

    vue.js修改el-popover組件顯示位置

    el-popover是一個(gè)基于element-ui框架設(shè)計(jì)的,用于浮動(dòng)展示提示或菜單的UI組件,下面這篇文章主要給大家介紹了關(guān)于vue.js修改el-popover組件顯示位置的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • 項(xiàng)目開發(fā)中husky的使用詳解

    項(xiàng)目開發(fā)中husky的使用詳解

    這篇文章主要為大家介紹了項(xiàng)目開發(fā)中husky的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Vue-router 中hash模式和history模式的區(qū)別

    Vue-router 中hash模式和history模式的區(qū)別

    這篇文章主要介紹了Vue-router 中hash模式和history模式的區(qū)別,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Vue中$nextTick實(shí)現(xiàn)源碼解析

    Vue中$nextTick實(shí)現(xiàn)源碼解析

    這篇文章主要為大家介紹了Vue中$nextTick實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 詳解vue-Resource(與后端數(shù)據(jù)交互)

    詳解vue-Resource(與后端數(shù)據(jù)交互)

    本篇文章主要介紹了vue-Resource(與后端數(shù)據(jù)交互),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • vue實(shí)現(xiàn)接口封裝的實(shí)現(xiàn)示例

    vue實(shí)現(xiàn)接口封裝的實(shí)現(xiàn)示例

    本文主要介紹了vue實(shí)現(xiàn)接口封裝的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • 基于Vue3和Element Plus實(shí)現(xiàn)自動(dòng)導(dǎo)入功能

    基于Vue3和Element Plus實(shí)現(xiàn)自動(dòng)導(dǎo)入功能

    在 Vue 3 項(xiàng)目中,結(jié)合  Element Plus  實(shí)現(xiàn)自動(dòng)導(dǎo)入可以顯著減少代碼量,提升開發(fā)效率,Element Plus 提供了官方的自動(dòng)導(dǎo)入插件  unplugin-vue-components  和  unplugin-auto-import,以下是如何配置和使用的詳細(xì)步驟,需要的朋友可以參考下
    2025-03-03
  • vue中添加與刪除關(guān)鍵字搜索功能

    vue中添加與刪除關(guān)鍵字搜索功能

    這篇文章主要介紹了vue中添加與刪除,關(guān)鍵字搜索功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Vue使用vue-pdf實(shí)現(xiàn)PDF文件預(yù)覽

    Vue使用vue-pdf實(shí)現(xiàn)PDF文件預(yù)覽

    這篇文章主要為大家詳細(xì)介紹了Vue如何使用vue-pdf實(shí)現(xiàn)PDF文件預(yù)覽的功能,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-03-03
  • 基于Vue3制作簡(jiǎn)單的消消樂(lè)游戲

    基于Vue3制作簡(jiǎn)單的消消樂(lè)游戲

    這篇文章主要為大家介紹了如何利用Vue3制作簡(jiǎn)單的消消樂(lè)游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起動(dòng)手試一試
    2022-05-05

最新評(píng)論