最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue實現(xiàn)虛擬滾動的示例詳解

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

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

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

<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.模擬滾動:通過監(jiān)聽鼠標的wheel事件,調(diào)整內(nèi)容位置,從而形成滾動效果;通過監(jiān)聽onmousedown、onmousemove、onmouseup實現(xiàn)虛擬滾動條的移動

解決什么問題

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

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

本文使用vue2實現(xiàn)虛擬滾動,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,并且禁止盒子的原生滾動,設(shè)置盒子overflowhidden。contentTransform用來動態(tài)變化滾動位置。給盒子增加ref,標記container為后面開發(fā)使用。

虛擬滾動條

在上述代碼中添加虛擬滾動條,虛擬滾動條包括滑道,其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用來動態(tài)變化虛擬滾動條的滾動位置,設(shè)置滾動條高度20px。到此處整個虛擬滾動示例長這樣

虛擬滾動實現(xiàn)

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

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

監(jiān)聽鼠標滾輪或觸屏版實現(xiàn)內(nèi)容區(qū)滾動

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

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

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

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

<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值為負值,表示內(nèi)容區(qū)向上滾動,反之內(nèi)容區(qū)向下滾動。之后需要限制滾動區(qū)間

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

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

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

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

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()
    })
  }
}

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

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

最小為0,最大為handleSpace。

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

到此處已經(jīng)實現(xiàn)了滾動條的移動和內(nèi)容區(qū)的移動。但二者還是各自為戰(zhàn)的,需要關(guān)聯(lián)起來。具體關(guān)聯(lián)邏輯是關(guān)聯(lián)內(nèi)容區(qū)最大滾動距離和虛擬滾動條最大移動距離。二者比例就是移動距離的數(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)容最大滾動距離,handleSpace為手柄最大移動距離。assistRatio為二者比例。轉(zhuǎn)換對象computedOffset包含兩個方法,分別是通過內(nèi)容移動距離轉(zhuǎn)為手柄移動距離和通過手柄移動距離轉(zhuǎn)為內(nèi)容移動距離。使用轉(zhuǎn)換方法

  methods: {
    bindContainerEvent () {
      ...
      const updateHandleOffset = () => {
        // 使用關(guān)聯(lián)方法
        this.handleOffset = this.transferOffset()
      }
      $container.addEventListener('wheel', bindContainerOffset)
      // 給手柄事件在增加一個訂閱方法
      $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()
  }

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

優(yōu)化

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

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

優(yōu)化手柄的高度邏輯,增加手柄高度屬性,以及計算屬性handleStyleHeight,限制手柄最小尺寸為20px,同時再增加手柄高度的初始化方法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>

禁止選中文本

在上文中的效果圖中也可以看出,當鼠標拖動滾動條時,內(nèi)容區(qū)文本被選中了。這樣體驗很不好,對手柄和滑道添加禁止選中,使用css實現(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é)

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

本文有2個沒有處理的點

  • 不需要滾動條的情況
  • 滾動條手柄的上下部分

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

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

相關(guān)文章

  • vue流式接口的使用解讀(EventSource)

    vue流式接口的使用解讀(EventSource)

    文章主要介紹了如何使用EventSource和WebSocket進行流式請求,以及如何主動關(guān)閉流式請求連接,同時,文章還提到了流式接口的要求,包括攜帶請求頭的方法
    2025-12-12
  • vue實現(xiàn)一個移動端屏蔽滑動的遮罩層實例

    vue實現(xiàn)一個移動端屏蔽滑動的遮罩層實例

    本篇文章主要介紹了vue實現(xiàn)一個移動端屏蔽滑動的遮罩層實例,具有一定的參考價值,有興趣的可以了解一下
    2017-06-06
  • vue?文件切片上傳的項目實現(xiàn)

    vue?文件切片上傳的項目實現(xiàn)

    本文主要介紹了vue?文件切片上傳的項目實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • 讓你一看就明白的$nextTick講解

    讓你一看就明白的$nextTick講解

    在我們用vue時,我們經(jīng)常用到一個方法是this.$nextTick,相信你也用過,我常用的場景是在進行獲取數(shù)據(jù)后,需要對新視圖進行下一步操作或者其他操作時,發(fā)現(xiàn)獲取不到dom,這篇文章主要給大家介紹了關(guān)于$nextTick的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • 使用VueCli3+TypeScript+Vuex一步步構(gòu)建todoList的方法

    使用VueCli3+TypeScript+Vuex一步步構(gòu)建todoList的方法

    這篇文章主要介紹了使用VueCli3+TypeScript+Vuex一步步構(gòu)建todoList的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • Vue項目pdf(base64)轉(zhuǎn)圖片遇到的問題及解決方法

    Vue項目pdf(base64)轉(zhuǎn)圖片遇到的問題及解決方法

    這篇文章主要介紹了Vue項目pdf(base64)轉(zhuǎn)圖片遇到的問題及解決方法,需要的朋友可以參考下
    2018-10-10
  • 深入了解Vue2中的的雙端diff算法

    深入了解Vue2中的的雙端diff算法

    雙端Diff在可以解決更多簡單Diff算法處理不了的場景,且比簡單Diff算法性能更好。本文主要來和大家詳細講講Vue2中的雙端diff算法的實現(xiàn)與使用,需要的可以參考一下
    2023-02-02
  • 解決vue中使用proxy配置不同端口和ip接口問題

    解決vue中使用proxy配置不同端口和ip接口問題

    這篇文章主要介紹了解決vue中使用proxy配置不同端口和ip接口問題 ,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08
  • Vue實現(xiàn)文字上下滾動動畫的示例代碼

    Vue實現(xiàn)文字上下滾動動畫的示例代碼

    這篇文章主要為大家詳細介紹了如何使用Vue實現(xiàn)超酷文字上下滾動動畫,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考下
    2024-03-03
  • vue基于better-scroll仿京東分類列表

    vue基于better-scroll仿京東分類列表

    這篇文章主要為大家詳細介紹了vue基于better-scroll仿京東分類列表,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06

最新評論

姚安县| 林甸县| 横峰县| 丁青县| 五河县| 中超| 凤城市| 龙州县| 清镇市| 申扎县| 湘阴县| 民丰县| 化德县| 岢岚县| 庆云县| 额尔古纳市| 灵石县| 林州市| 历史| 嘉黎县| 孟津县| 大足县| 普兰店市| 乐陵市| 敦化市| 乐平市| 兴山县| 莲花县| 五莲县| 夏邑县| 本溪| 揭阳市| 醴陵市| 台南县| 十堰市| 云安县| 孟州市| 和龙市| 府谷县| 铜山县| 梁平县|