稀土掘金技术社区

数据可视化大屏列表循环滚动动画Vue指令封装(requestAnimationFrame)

关注更多AI编程资讯请去AI Coding专区:https://juejin.cn/aicoding

背景及需求

背景: 在大屏项目中,经常会存在列表循环滚动的动画。 当存在很多个列表都需要动画的时候, 重复代码既不够优雅,也不便于维护(当甲方觉得速度快慢调整或其他调整时候,需要重复修改)。

需求:

  1. 列表上下滚动动画循环播放
  2. 当鼠标点击列表时候,滚动暂停
  3. 当鼠标移出列表元素时候,重新触发滚动
  4. 可调整列表滚动的速度(甲方屁事多的情况下)

代码效果展示

Image列表滚动动画vue指令网址:

https://code.juejin.cn/pen/7400691681794162728

详解

  1. 通过requestAnimationFrame进行列表动画滚动动画,并通过回调接口的timeStamp时间戳来控制动画进行的速度。
  2. 通过vue指令进行封装,并且在指令mounted生命周期定义变量isUsing来辨别当前用户是否正在与元素交互中(点击,鼠标滚动, 移入等,根据业务需求), 为元素增添监听鼠标点击、移入移出事件来控制交互期间的动画状态.

一些思考

为什么使用requestAnimationFrame而不是使用setTimeout或setInterval实现? 在数据可视化需要定制动画的情况下,requestAnimationFrame的稳定性、交互性、流畅度和易用性都是优优于其他二者的,是与浏览器刷新都挂钩的

在当前浏览器的支持度也十分良好

image.png

当然在极限的情况下,也需要额外适配,在下面代码中我也有预留当浏览器不支持时候可以使用setTimeout或setInterval实现。

核心代码

vue 指令实现

{    arr: {},    mounted(el, binding = { value: 200 }, vnode, prevVnode) {        const gap = 1000 / 45;  // 帧数差距, 浏览器大概1s 60帧, 数字 > 60 效果应该一致,数字越小越慢        if (window.requestAnimationFrame) {            let isUsing = false;    // 是否介入操作;介入操作时暂停动画;            // 鼠标移出时,继续动画            el.addEventListener('mouseleave', () => {                isUsing && scrollAnimation(); // 如果动画已暂停,重置动画                isUsing = false;            }, { passive: true } )            // 鼠标滚动时,继续动画            el.addEventListener('wheel', () => {                isUsing = true;            }, { passive: true } )            // 鼠标点击时, 暂停动画            el.addEventListener('click', () => {                isUsing = true;            }, { passive: true } )            // 动画方法            const scrollAnimation = () => {                if (window.requestAnimationFrame) {                    /**                     * @Author: Damon Liu                     * @Date: 2024-04-29 10:28:37                     * @LastEditors: Damon Liu                     * @LastEditTime:                      * @Description:                      * @param {*} timeStamp 当前时间戳                     * @param {*} preTimeStamp 上一帧时间戳                     * @param {*} diff  累计的时间差                     */                    let animationFun = (timeStamp, preTimeStamp = 0, diff = 0) => {                        if (isUsing) {                            return                        }                        let currentDiff = preTimeStamp === 0 ? 0 : timeStamp - preTimeStamp;    // 当前时间差                        let n_diff = currentDiff + diff;    // 总时间差                        // 当总时间差比小于帧数差距时,不执行动画,申请下一帧执行                        if(n_diff < gap) {                            window.requestAnimationFrame((_timeStamp) => animationFun(_timeStamp, timeStamp, n_diff))                            return ;                        }                        let scrollTop = el.scrollTop;   // 滚动条顶部                        let clientHeight = el.clientHeight; // 内容高度                        let scrollHeight = el.scrollHeight; // 滚动内容高度                        // 当没有滚动至底部时                        if (scrollTop + clientHeight < scrollHeight) {                            el.scrollTop = scrollTop + 1;                            window.requestAnimationFrame((_timeStamp) => animationFun(_timeStamp, timeStamp, 0));                        }                        // 滚动至底部重置动画                        else {                            el.scrollTop = 0;                            scrollAnimation();                        }                    }                    // 开始动画                    window.requestAnimationFrame(animationFun);                }            }            scrollAnimation()        }        else {        }    },    unmounted(el, binding, vnode, prevVnode) {    }}

额外拓展

存在额外需求, 如需要手动去控制列表是否滚动时候,可通过指令传入额外变量去进行控制。

列表滚动动画也可以更改为其他动画,如颜色的变化以及形状,位移等。总体的思路一致。

图片

💰即日起至5.11发文瓜分万元奖金
📃主赛道推好文,AI命题叠福利
🔎点击上方活动图了解详情