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

news2025/1/12 18:39:29

效果

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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1349229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AIGC-图片生成视频系列-5】I2V-Adapter:一种用于视频扩散模型的通用图像生成视频适配器

目录 一. 项目与贡献概述 二. 方法详解 a. 整体框架图 b. 帧相似性先验 三. 一般化图像生成动画结果 四. 基于个性化 T2I 模型的动画结果 五. 结合ControlNet动画结果 六. 项目论文和代码 七. 个人思考与总结 在快速发展的数字内容生成领域&#xff0c;焦点已从文本到…

C#中的Attribute详解(下)

C#中的Attribute详解&#xff08;下&#xff09; 一、Attribute本质二、Attribute实例化三、Attribute实例化的独特之处四、元数据的作用五、自定义Attribute实例六、Attribute的附着目标七、附加问题 一、Attribute本质 从上篇里我们可以看到&#xff0c;Attribute似乎总跟pu…

Linux基础知识点(五-信号)

一、信号的基本概念 1.1 信号的概念 信号&#xff08;signal&#xff09;&#xff0c;又称为软中断信号&#xff0c;用于通知进程发生了异步事件&#xff0c;它是Linux系统响应某些条件而产生的一个事件&#xff0c;它是在软件层次上对中断机制的一种模拟&#xff0c;是一种异…

12.29最小生成数K算法复习(注意输入输出格式),校园最短路径(通过PRE实现路径输出,以及输入输出格式注意)

7-2 最小生成树-kruskal算法 分数 15 const int maxn 1000; struct edge {int u, v, w; }e[maxn]; int n, m, f[30]; bool cmp(edge a, edge b) {return a.w < b.w; } int find(int x) {if (f[x] x) {return x;}else {f[x] find(f[x]);return f[x];} } //int arr[100…

简单FTP客户端软件开发——VMware安装Linux虚拟机(命令行版)

VMware安装包和Linux系统镜像&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1UwF4DT8hNXp_cV0NpSfTww?pwdxnoh 提取码&#xff1a;xnoh 这个学期做计网课程设计【简单FTP客户端软件开发】需要在Linux上配置 ftp服务器&#xff0c;故此用VMware安装了Linux虚拟机&…

数据结构模拟实现LinkedList双向不循环链表

目录 一、双向不循环链表的概念 二、链表的接口 三、链表的方法实现 &#xff08;1&#xff09;display方法 &#xff08;2&#xff09;size方法 &#xff08;3&#xff09;contains方法 &#xff08;4&#xff09;addFirst方法 &#xff08;5&#xff09;addLast方法 …

python开发的app有哪些,python如何开发小软件

这篇文章主要介绍了python开发的app有哪些&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 Python 无处不在&#xff0c;可以说是现代的 C 编程语言&#xff0c;你可以在任何地…

JavaScript的三种引入的方式

目录 (一).什么是JS1.1JS的特点1.2JS的组成 (二).JS引用的三种方式2.1标签引用&#xff08;或嵌入式)2.2文件引用&#xff08;外链式&#xff09;2.3行内式 (三).JS三种引用方式的优缺点1.行内方式&#xff1a;2.标签引用&#xff08;或嵌入式&#xff09;&#xff1a;3.文件引…

Codeforces Round 900 (Div. 3)(A-F)

比赛链接 : Dashboard - Codeforces Round 900 (Div. 3) - Codeforces A. How Much Does Daytona Cost? 题面 : 思路 : 在序列中只要找到k&#xff0c;就返回true ; 代码 : #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)…

[Redis实战]分布式锁

四、分布式锁 4.1 基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xf…

机械过滤器(石英砂过滤器)和多介质过滤器的区别 工作原理动画

​ 1&#xff1a;机械过滤器多介质石英砂过滤器介绍 机械过滤器&#xff1a;预处理水质的关键设备 机械过滤器&#xff0c;也被称为压力式过滤器&#xff0c;是纯水制备过程中不可或缺的预处理设备。它在水处理系统中扮演着重要的角色&#xff0c;能够有效地去除水中的悬浮物…

Ubuntu(WSL)创建用户并赋予权限

查看当前用户 创建新用户 1&#xff09;执行如下命令创建新用户 sudo adduser mysql57 // mysql57 为新用户名&#xff0c;可自定义 2&#xff09;若待创建用户已存在&#xff0c;则先删除已存在用户&#xff0c;然后创建新用户 // 删除用户名 userdel mysql57// 删除组…

lambda表达式和包装器

正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 我们在使用库里的排序算法时如果排序的是自定义类型或者库里默认的排序不能满足我们则需求&…

2.2 设计FMEA步骤二:结构分析

2.2.1 目的 设计结构分析的目的是将设计识别和分解为系统、子系统、组件和零件,以便进行技术风险分析。其主要目标包括: 可视化分析范围结构化表示:方块图、边界图、数字模型、实体零件识别设计接口、交互作用和间隙促进顾客和供应商工程团队之间的协作(接口责任)为功能分…

PyTorch官网demo解读——第一个神经网络(4)

上一篇&#xff1a;PyTorch官网demo解读——第一个神经网络&#xff08;3&#xff09;-CSDN博客 上一篇我们聊了手写数字识别神经网络的损失函数和梯度下降算法&#xff0c;这一篇我们来聊聊激活函数。 大佬说激活函数的作用是让神经网络产生非线性&#xff0c;类似人脑神经元…

Delphi6函数大全4-SysUtils.pas

Delphi6函数大全4-SysUtils.pas首部 function FormatFloat(const Format: string; Value: Extended): string; $[SysUtils.pas功能 返回浮点数类型以指定格式字符串Format转换成字符串说明 FormatFloat(,.00, 1234567890) 1,234,567,890.00参考 function …

[SSD 测试 1.3] 消费级SSD全生命周期测试

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解SSD》 <<<< 返回总目录 <<<< 构建消费级SSD全生命周期测试,开展性能测试、兼容性测试、功能测试、环境应力测试、可靠性测试、电器检测。 以忆联消费级存储实验室为例,消费级存储实验室面积…

思维链COT原理探究

要进行因果分析&#xff0c;需要把思维链中的不同元素拆解开来&#xff0c;然后通过控制变量实验&#xff0c;来研究不同元素对COT效果的影响。以下两篇论文的核心差异就在于: COT的变量拆解&#xff0c;以及控制变量的实验方式。 结合两篇论文的实验结论&#xff0c;可能导致…

Java技术栈 —— Redis的雪崩、穿透与击穿

Java技术栈 —— Redis的雪崩、穿透与击穿 〇、实验的先导条件&#xff08;NginxJmeter&#xff09;一、Redis缓存雪崩、缓存穿透、缓存击穿1.1 雪崩1.2 穿透1.3 击穿 二、Redis应用场景——高并发2.1 单机部署的高并发问题与解决&#xff08;JVM级别锁&#xff09;2.2 集群部署…

RK3568平台 Android13 GKI架构开发方式

一.GKI简介 GKI&#xff1a;Generic Kernel Image 通用内核映像。 Android13 GMS和EDLA认证的一个难点是google强制要求要支持GKI。GKI通用内核映像&#xff0c;是google为了解决内核碎片化的问题&#xff0c;而设计的通过提供统一核心内核并将SoC和板级驱动从核心内核移至可加…