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

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

 更新時(shí)間:2023年10月25日 08:35:56   作者:通往自由之路  
虛擬滾動(dòng)或者移動(dòng)是指禁止原生滾動(dòng),之后通過(guò)監(jiān)聽(tīng)瀏覽器的相關(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)聽(tīng)瀏覽器的相關(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)聽(tīng)鼠標(biāo)的wheel事件,調(diào)整內(nèi)容位置,從而形成滾動(dòng)效果;通過(guò)監(jiān)聽(tīng)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為后面開(kāi)發(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),開(kāi)頭說(shuō)了模擬滾動(dòng)原理:通過(guò)監(jiān)聽(tīng)鼠標(biāo)的wheel事件,調(diào)整內(nèi)容位置,從而形成滾動(dòng)效果;通過(guò)監(jiān)聽(tīng)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)聽(tīng)鼠標(biāo)滾輪或觸屏版實(shí)現(xiàn)內(nèi)容區(qū)滾動(dòng)

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

監(jiān)聽(tīng)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)聽(tīng)虛擬滾動(dòng)條事件實(shí)現(xiàn)內(nèi)容區(qū)滾動(dòng)

監(jiān)聽(tīng)虛擬滾動(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)聽(tīng)模擬內(nèi)容的移動(dòng);通過(guò)對(duì)onmousedown、onmousemoveonmouseup的監(jiān)聽(tīng)實(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流式接口的使用解讀(EventSource)

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

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

    vue實(shí)現(xiàn)一個(gè)移動(dòng)端屏蔽滑動(dòng)的遮罩層實(shí)例

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

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

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

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

    在我們用vue時(shí),我們經(jīng)常用到一個(gè)方法是this.$nextTick,相信你也用過(guò),我常用的場(chǎng)景是在進(jìn)行獲取數(shù)據(jù)后,需要對(duì)新視圖進(jìn)行下一步操作或者其他操作時(shí),發(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的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Vue項(xiàng)目pdf(base64)轉(zhuǎn)圖片遇到的問(wèn)題及解決方法

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

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

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

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

    解決vue中使用proxy配置不同端口和ip接口問(wèn)題

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

    Vue實(shí)現(xiàn)文字上下滾動(dòng)動(dòng)畫(huà)的示例代碼

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

    vue基于better-scroll仿京東分類(lèi)列表

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

最新評(píng)論

澄迈县| 株洲县| 南平市| 蒙山县| 吴川市| 巴彦淖尔市| 工布江达县| 瓮安县| 乳山市| 台中市| 重庆市| 铜鼓县| 乐至县| 西乡县| 两当县| 旬邑县| 云林县| 娄烦县| 迁西县| 观塘区| 和龙市| 嘉定区| 秦皇岛市| 武陟县| 颍上县| 连平县| 宁阳县| 额尔古纳市| 勃利县| 西盟| 博乐市| 车致| 龙门县| 云霄县| 嘉义县| 鱼台县| 塘沽区| 景德镇市| 屏南县| 灵武市| 富民县|