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

基于vue.js實(shí)現(xiàn)無縫滾動(dòng)的兩種方法

 更新時(shí)間:2026年02月27日 09:25:12   作者:Summer.Xia6552  
在現(xiàn)代的網(wǎng)頁設(shè)計(jì)中,無縫滾動(dòng)廣告特效已經(jīng)變得非常流行,這種特效能夠吸引用戶的注意力,同時(shí)展示多個(gè)廣告內(nèi)容,這篇文章主要介紹了基于vue.js實(shí)現(xiàn)無縫滾動(dòng)的兩種方法,需要的朋友可以參考下

方法一:基于requestAnimationFrame

demo

<template>
  <h-page-container class="hoem-page">
    <h1>無縫滾動(dòng)</h1>
    <h2>垂直方向</h2>
    <div class="container1">
      <AutoScroll :data="list" :item-height="110" :limit-move-num="3" :is-rem="false">
        <template #item="{ keySuffix }">
          <div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card">
            <div>{{ item.title }}</div>
            <div>{{ item.content }}</div>
          </div>
        </template>
      </AutoScroll>
    </div>

    <h2>水平方向</h2>
    <div class="container2">
      <AutoScroll :data="list" :direction="2" :item-width="210" :limit-move-num="3" :is-rem="false">
        <template #item="{ keySuffix }">
          <div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card">
            <div>{{ item.title }}</div>
            <div>{{ item.content }}</div>
          </div>
        </template>
      </AutoScroll>
    </div>
  </h-page-container>
</template>

<script setup>
import { ref } from 'vue'
import AutoScroll from '@/components/AutoScroll.vue'

const list = ref([
  {
    id: 1,
    title: '卡片1',
    content: '111'
  },
  {
    id: 2,
    title: '卡片2',
    content: '222'
  },
  {
    id: 3,
    title: '卡片3',
    content: '333'
  },
  {
    id: 4,
    title: '卡片4',
    content: '444'
  }
])


</script>

<style lang="scss" scoped>
.hoem-page {
  width: 100%;
  height: 100vh;
  padding: 10px;
}

.container1 {
  width: 200px;
  height: 300px;
  margin: 20px;
  overflow: hidden;

  .card {
    width: 100%;
    height: 100px;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    margin-bottom: 10px;
    padding: 10px;
  }
}


.container2 {
  width: 500px;
  height: 150px;
  margin: 20px;
  overflow: hidden;

  .card {
    width: 200px;
    height: 100%;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    margin-right: 10px;
    padding: 10px;
  }
}
</style>

AutoScroll.vue

<template>
  <div class="scroll-list" :style="listStyle" @mouseover="pauseAnimation" @mouseout="animate">
    <slot name="item" key-suffix=""></slot> <!-- 原始內(nèi)容 -->
    <template v-if="shouldDuplicate">
      <slot name="item" key-suffix="_copy"></slot> <!-- 復(fù)制內(nèi)容,添加后綴 -->
    </template>
  </div>
</template>

<script setup>
import { ref, onBeforeUnmount, watch, computed } from 'vue'
// 定義滾動(dòng)方向枚舉
const Direction = {
  DOWN: 0,
  UP: 1,
  LEFT: 2,
  RIGHT: 3
}

const props = defineProps({
  // 列表
  data: {
    type: Array,
    default: () => []
  },
  // 方向: 0 往下 1 往上 2 向左 3 向右
  direction: {
    type: Number,
    default: 1
  },
  /**
   * 一個(gè)列表元素的高度(包含外邊距)
   * direction為0 往下 1 往上時(shí)
   */
  itemHeight: {
    type: Number,
    default: null
  },
  /**
   * 一個(gè)列表元素的寬度(包含外邊距)
   * direction為2 向左 3 向右時(shí)
   */
  itemWidth: {
    type: Number,
    default: null
  },
  // itemHeight的單位是否是rem
  isRem: {
    type: Boolean,
    default: true
  },
  // 開啟無縫滾動(dòng)的數(shù)據(jù)量。
  limitMoveNum: {
    type: Number,
    default: 5
  },
  // 幾列
  columns: {
    type: Number,
    default: 1
  }
})

// 是否需要復(fù)制內(nèi)容
const shouldDuplicate = computed(() => props.data.length >= props.limitMoveNum)

const requestId = ref(null)
const offset = ref(0)

// 判斷是否為垂直方向
const isVertical = computed(() => props.direction === Direction.DOWN || props.direction === Direction.UP)

// 計(jì)算列表樣式
const listStyle = computed(() => ({
  transform: `${isVertical.value ? 'translateY' : 'translateX'}(${offset.value}${props.isRem ? 'rem' : 'px'})`,
  display: isVertical.value ? 'block' : 'flex'
}))

// 計(jì)算最大偏移量
const maxOffset = computed(() => {
  return Math.ceil(props.data.length / props.columns) *
    (isVertical.value ? props.itemHeight : props.itemWidth)
})

// 開始動(dòng)畫
const animate = () => {
  if (props.data?.length < props.limitMoveNum) return

  requestId.value = requestAnimationFrame(() => {
    animate()
    offset.value += (props.direction === Direction.UP || props.direction === Direction.LEFT) ? -0.3 : 0.3

    // 當(dāng)滾動(dòng)完一輪后重置位置
    if (Math.abs(offset.value) >= maxOffset.value) {
      offset.value = 0
    }
  })
}

// 暫停動(dòng)畫
const pauseAnimation = () => {
  if (requestId.value) {
    cancelAnimationFrame(requestId.value)
    requestId.value = null
  }
}

watch(() => props.data, (val) => {
  if (val?.length >= props.limitMoveNum) {
    pauseAnimation()
    animate()
  }
}, {
  immediate: true
})

onBeforeUnmount(() => {
  pauseAnimation()
})

</script>

<style lang="scss" scoped>
.scroll-list {
  width: 100%;
  height: 100%;
  /* 確保動(dòng)畫在合成層運(yùn)行 */
  backface-visibility: hidden;

  &>* {
    flex-grow: 0;
    flex-shrink: 0;
  }
}
</style>

**

方法二:基于animation動(dòng)畫

**
demo

<template>
  <h-page-container class="hoem-page">
    <h1>無縫滾動(dòng)</h1>
    <h2>垂直方向</h2>
    <div class="container1">
      <AutoScroll :data="list" :item-height="110" :limit-move-num="3">
        <template #item="{ keySuffix }">
          <div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card">
            <div>{{ item.title }}</div>
            <div>{{ item.content }}</div>
          </div>
        </template>
      </AutoScroll>
    </div>

    <h2>水平方向</h2>
    <div class="container2">
      <AutoScroll :data="list" :direction="2" :item-width="210" :limit-move-num="3">
        <template #item="{ keySuffix }">
          <div v-for="(item, index) in list" :key="`${item.id || index}${keySuffix || ''}`" class="card">
            <div>{{ item.title }}</div>
            <div>{{ item.content }}</div>
          </div>
        </template>
      </AutoScroll>
    </div>
  </h-page-container>
</template>

<script setup>
import { ref } from 'vue'
import AutoScroll from '@/components/AutoScroll.vue'

const list = ref([
  {
    id: 1,
    title: '卡片1',
    content: '111'
  },
  {
    id: 2,
    title: '卡片2',
    content: '222'
  },
  {
    id: 3,
    title: '卡片3',
    content: '333'
  },
  {
    id: 4,
    title: '卡片4',
    content: '444'
  }
])


</script>

<style lang="scss" scoped>
.hoem-page {
  width: 100%;
  height: 100vh;
  padding: 10px;
}

.container1 {
  width: 200px;
  height: 300px;
  margin: 20px;
  overflow: hidden;

  .card {
    width: 100%;
    height: 100px;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    margin-bottom: 10px;
    padding: 10px;
  }
}


.container2 {
  width: 500px;
  height: 150px;
  margin: 20px;
  overflow: hidden;

  .card {
    width: 200px;
    height: 100%;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    margin-right: 10px;
    padding: 10px;
  }
}
</style>

AutoScroll.vue

<template>
  <div class="auto-scroll" :class="scrollClass" @mouseenter="setScrollPause(true)" @mouseleave="setScrollPause(false)">
    <div ref="scrollContent" class="scroll-content" :style="contentStyle">
      <slot name="item" :key-suffix="''"></slot>
      <template v-if="shouldDuplicate">
        <slot name="item" :key-suffix="'_copy'"></slot>
      </template>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'

const props = defineProps({
  // 列表
  data: {
    type: Array,
    default: () => []
  },
  // 方向: 0 往下 1 往上 2 向左 3 向右
  direction: {
    type: Number,
    default: 0
  },
  /**
 * 一個(gè)列表元素的高度(包含外邊距)
 * direction為0 往下 1 往上時(shí)
 */
  itemHeight: {
    type: Number,
    default: 40
  },
  /**
 * 一個(gè)列表元素的寬度(包含外邊距)
 * direction為2 向左 3 向右時(shí)
 */
  itemWidth: {
    type: Number,
    default: 100
  },
  // 開啟無縫滾動(dòng)的數(shù)據(jù)量。
  limitMoveNum: {
    type: Number,
    default: 5
  },
  // 完整滾動(dòng)周期(秒)
  duration: {
    type: Number,
    default: 10
  },
  // 鼠標(biāo)懸浮暫停
  hoverPause: {
    type: Boolean,
    default: true
  }
})

const scrollContent = ref(null)
const isPaused = ref(false)

// 判斷是否為垂直方向
const isVertical = computed(() => props.direction <= 1)

// 是否需要復(fù)制內(nèi)容
const shouldDuplicate = computed(() => props.data.length >= props.limitMoveNum)

// 滾動(dòng)方向
const scrollClass = computed(() => [
  `direction-${props.direction}`,
  isVertical.value ? 'vertical' : 'horizontal'
])

// 內(nèi)容樣式計(jì)算
const contentStyle = computed(() => {
  const sizeProp = isVertical.value ? 'height' : 'width'
  const itemSize = isVertical.value ? props.itemHeight : props.itemWidth
  const contentSize = props.data.length * itemSize

  return {
    [sizeProp]: shouldDuplicate.value ? `${contentSize * 2}px` : `${contentSize}px`,
    'animation-duration': `${props.duration}s`,
    'animation-play-state': isPaused.value ? 'paused' : 'running'
  }
})

// 動(dòng)畫控制
const setScrollPause = (paused) => {
  if (props.hoverPause) {
    isPaused.value = paused
  }
}


// 生命周期
onMounted(() => {
  if (shouldDuplicate.value) {
    setScrollPause(false)
  }
})

onBeforeUnmount(() => {
  setScrollPause(true)
})
</script>

<style scoped>
.auto-scroll {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

.scroll-content {
  position: absolute;
  will-change: transform;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* 垂直方向樣式 */
.vertical .scroll-content {
  width: 100%;
}

/* 水平方向樣式 */
.horizontal .scroll-content {
  height: 100%;
  display: flex;
}

/* 方向0: 往下 */
.direction-0 .scroll-content {
  top: 0;
  animation-name: scrollDown;
}

/* 方向1: 往上 */
.direction-1 .scroll-content {
  bottom: 0;
  animation-name: scrollUp;
}

/* 方向2: 向左 */
.direction-2 .scroll-content {
  left: 0;
  animation-name: scrollLeft;
}

/* 方向3: 向右 */
.direction-3 .scroll-content {
  right: 0;
  animation-name: scrollRight;
}

@keyframes scrollDown {
  0% {
    transform: translateY(0);
  }

  100% {
    transform: translateY(-50%);
  }
}

@keyframes scrollUp {
  0% {
    transform: translateY(0);
  }

  100% {
    transform: translateY(50%);
  }
}

@keyframes scrollLeft {
  0% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(-50%);
  }
}

@keyframes scrollRight {
  0% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(50%);
  }
}
</style>

總結(jié) 

到此這篇關(guān)于基于vue.js實(shí)現(xiàn)無縫滾動(dòng)兩種方法的文章就介紹到這了,更多相關(guān)vue.js無縫滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue對(duì)象的組成和掛載方式詳解

    Vue對(duì)象的組成和掛載方式詳解

    這篇文章主要介紹了Vue對(duì)象的基本組成和Vue對(duì)象掛載的幾種方式,文中通過代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-07-07
  • Vue組件為什么data必須是一個(gè)函數(shù)

    Vue組件為什么data必須是一個(gè)函數(shù)

    這篇文章主要介紹了Vue組件為什么data必須是一個(gè)函數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • vue使用Print.js打印頁面樣式不出現(xiàn)的解決

    vue使用Print.js打印頁面樣式不出現(xiàn)的解決

    這篇文章主要介紹了vue使用Print.js打印頁面樣式不出現(xiàn)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Vue監(jiān)聽localstorage變化的方法詳解

    Vue監(jiān)聽localstorage變化的方法詳解

    在日常開發(fā)中,我們經(jīng)常使用localStorage來存儲(chǔ)一些變量,這些變量會(huì)存儲(chǔ)在瀏覽中,對(duì)于localStorage來說,即使關(guān)閉瀏覽器,這些變量依然存儲(chǔ)著,方便我們開發(fā)的時(shí)候在別的地方使用,本文就給大家介紹Vue如何監(jiān)聽localstorage的變化,需要的朋友可以參考下
    2023-10-10
  • vue cli4.0項(xiàng)目引入typescript的方法

    vue cli4.0項(xiàng)目引入typescript的方法

    這篇文章主要介紹了vue cli4.0項(xiàng)目引入typescript的方法,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • vue3+ts前端封裝EventSource并在請(qǐng)求頭添加token的方法

    vue3+ts前端封裝EventSource并在請(qǐng)求頭添加token的方法

    這篇文章主要介紹了vue3+ts前端封裝EventSource并在請(qǐng)求頭添加token,本文將介紹如何使用 event-source-polyfill 來解決這個(gè)問題,需要的朋友可以參考下
    2024-12-12
  • vue.js實(shí)現(xiàn)仿原生ios時(shí)間選擇組件實(shí)例代碼

    vue.js實(shí)現(xiàn)仿原生ios時(shí)間選擇組件實(shí)例代碼

    本篇文章主要介紹了vue.js實(shí)現(xiàn)仿原生ios時(shí)間選擇組件實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2016-12-12
  • vue3獲取ref實(shí)例結(jié)合ts的InstanceType問題

    vue3獲取ref實(shí)例結(jié)合ts的InstanceType問題

    這篇文章主要介紹了vue3獲取ref實(shí)例結(jié)合ts的InstanceType問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Vue3+script setup+ts+Vite+Volar搭建項(xiàng)目

    Vue3+script setup+ts+Vite+Volar搭建項(xiàng)目

    本文主要介紹了Vue3+script setup+ts+Vite+Volar搭建項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Vue中el-table條件渲染防止樣式亂掉(解決方法)

    Vue中el-table條件渲染防止樣式亂掉(解決方法)

    這篇文章主要介紹了Vue中el-table條件渲染防止樣式亂掉問題,通過使用:key="Math.random()" 可解決樣式錯(cuò)亂問題,此key屬性是vue自帶的特殊屬性,主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對(duì)比時(shí)辨識(shí) VNodes,依次來提升頁面渲染性能,感興趣的朋友一起看看吧
    2023-11-11

最新評(píng)論

泰宁县| 克山县| 富裕县| 龙川县| 凯里市| 康乐县| 都匀市| 九台市| 同心县| 锦州市| 那曲县| 淳安县| 青州市| 勃利县| 休宁县| 团风县| 大悟县| 南皮县| 辉南县| 永善县| 宿州市| 垫江县| 山丹县| 喜德县| 安新县| 绥芬河市| 泾源县| 鄂伦春自治旗| 敖汉旗| 沭阳县| 仁寿县| 体育| 凤阳县| 泊头市| 丹江口市| 宁武县| 信宜市| 申扎县| 昌平区| 兴隆县| 宣汉县|