Vue中使用JavaScript中的requestAnimationFrame动画循环实现循环滚动效果-demo

2024-01-01 22:30:51

效果

requestAnimationFrame是一个由浏览器提供的 JavaScript 方法,用于在下一次浏览器重绘之前执行指定的回调函数。它接受一个回调函数作为参数,并返回一个整数值,可以用于取消动画循环。

使用 requestAnimationFrame 可以创建平滑的动画效果,并且能够有效地控制动画的帧率,以适应不同的设备和浏览器性能。

与使用定时器(如 setTimeout 或 setInterval)相比,requestAnimationFrame 具有以下优势:

自动适应设备刷新率:requestAnimationFrame 会自动根据当前设备的刷新率来调整动画的帧率,以提供更平滑的动画效果。

避免资源浪费:当页面处于非激活状态时,requestAnimationFrame 会暂停动画循环,从而避免不必要的资源浪费。

与浏览器的绘制周期同步:requestAnimationFrame 会在浏览器的绘制周期内执行回调函数,以确保动画的更新和渲染在最佳时机进行。

function animate() {
  // 动画逻辑...

  requestAnimationFrame(animate);
}

// 开始动画循环
requestAnimationFrame(animate);

const animationId = requestAnimationFrame(animate);

// 取消动画循环
cancelAnimationFrame(animationId);

实现代码

循环滚动

<template>
  <div class="wrap" ref="wrapRef">
    <div v-for="item in items" :key="item" class="item">${{ item }}</div>
    <template v-if="isShowItems">
      <div v-for="item in items" :key="item" class="item">${{ item }}</div>
    </template>
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';

const items = ref(Array.from({ length: 26 }, (_, index) => index + 1));

const wrapRef = ref(null);
const isShowItems = ref(false);
const animationId = ref(null);

// scrollWidth 总宽度,包括被隐藏的部分
// clientWidth 可视宽度,不包括被隐藏的部分 window.innerWidth
// scrollLeft 滚动距离

const playAnimate = () => {
  const wrapElement = wrapRef.value;
  if (wrapElement === null) return;

  const maxScrollLeft = wrapElement.scrollWidth - wrapElement.clientWidth;
  if (maxScrollLeft) {
    console.log(
      maxScrollLeft,
      wrapElement.scrollWidth,
      wrapElement.clientWidth
    );
    isShowItems.value = true;
    if (wrapElement.scrollLeft >= maxScrollLeft - 2) {
      wrapElement.scrollLeft -= maxScrollLeft;
    }
    wrapElement.scrollLeft += 1;
    // 动画循环
    animationId.value = requestAnimationFrame(playAnimate);
  }
};

onMounted(() => {
  console.log('总宽度', wrapRef.value.scrollWidth);
  console.log('可视宽度', wrapRef.value.clientWidth);
  //   playAnimate();
  // 开始动画循环
  requestAnimationFrame(playAnimate);
});
onUnmounted(() => {
  //   wrapRef.value = null;
  console.log('取消动画循环', animationId.value);
  // 在需要取消动画循环时
  cancelAnimationFrame(animationId);
});
</script>
<style lang="scss" scoped>
.wrap {
  background: linear-gradient(90deg, #9da0a0, #818181 138.82%);
  align-items: center;
  display: flex;
  height: 50px;
  white-space: nowrap;
  width: 100%;
  overflow: hidden;
  overflow: auto;
  &::-webkit-scrollbar {
    display: none;
  }
  .item {
    font-size: 18px;
    color: #fff;
    padding: 0 24px;
  }
}
</style>

来回滚动

<template>
  <div class="wrap" ref="wrapRef">
    <div v-for="item in items" :key="item" class="item">${{ item }}</div>
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';

const items = ref(Array.from({ length: 26 }, (_, index) => index + 1));

const wrapRef = ref(null);
const isShowItems = ref(false);
const scrollLeftEnd = ref(false);
const animationId = ref(null);

// scrollWidth 总宽度,包括被隐藏的部分
// clientWidth 可视宽度,不包括被隐藏的部分 window.innerWidth
// scrollLeft 滚动距离

const playAnimate = () => {
  const wrapElement = wrapRef.value;
  if (wrapElement === null) return;

  const maxScrollLeft = wrapElement.scrollWidth - wrapElement.clientWidth;
  if (maxScrollLeft) {
    console.log(
      maxScrollLeft,
      wrapElement.scrollWidth,
      wrapElement.clientWidth
    );
    isShowItems.value = true;
    // 右到左
    // if (wrapElement.scrollLeft >= maxScrollLeft - 2) {
    //   wrapElement.scrollLeft -= maxScrollLeft;
    // }
    // wrapElement.scrollLeft += 1;

    // 来回滚动
    if (wrapElement.scrollLeft >= maxScrollLeft - 2) {
      scrollLeftEnd.value = true;
    }
    if (wrapElement.scrollLeft <= 1) {
      scrollLeftEnd.value = false;
    }
    if (scrollLeftEnd.value) {
      wrapElement.scrollLeft -= 1;
    } else {
      wrapElement.scrollLeft += 1;
    }

    // 动画循环
    animationId.value = requestAnimationFrame(playAnimate);
  }
};

onMounted(() => {
  console.log('总宽度', wrapRef.value.scrollWidth);
  console.log('可视宽度', wrapRef.value.clientWidth);
  //   playAnimate();
  // 开始动画循环
  requestAnimationFrame(playAnimate);
});
onUnmounted(() => {
  //   wrapRef.value = null;
  console.log('取消动画循环', animationId.value);
  // 在需要取消动画循环时
  cancelAnimationFrame(animationId);
});
</script>
<style lang="scss" scoped>
.wrap {
  background: linear-gradient(90deg, #9da0a0, #818181 138.82%);
  align-items: center;
  display: flex;
  height: 50px;
  white-space: nowrap;
  width: 100%;
  overflow: hidden;
  overflow: auto;
  &::-webkit-scrollbar {
    display: none;
  }
  .item {
    font-size: 18px;
    color: #fff;
    padding: 0 24px;
  }
}
</style>

文章来源:https://blog.csdn.net/JackieDYH/article/details/135273333
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。