如何更好的用原生 js 实现 jquery 的 animate

2019-04-17 16:16:52 +08:00
 leaveeel

先说我现在的方法吧

背景:项目是 react 环境,里面有个滚动数据的功能,没有引入 jquery,个人感觉单独为了一个animate多引一个 jquery 不划算。

需求:展示获取到的 N 条数据,首屏展示 x 条,每过 t 秒滚动到下一屏。

实现:主要通过两个定时器,animate控制滚动间隔。Interval控制从滚动开始到滚动结束时长,代码如下。

目标:实现类似 jqueryanimate的平滑效果,目前在内层的定时器设置 0.001 秒移动步长±1 像素,在小区域内还行,但是半屏或者全屏的滚动就显得速度慢了,修改增加步长又不平滑,有很明显的段落感。而 jqueryanimate中就很流畅,所有动画都在 time 内完成。所以想请教下有没有更好的办法实现,或者有什么轻便的 animate 包。

代码:

    ……
    
    //底部回滚到顶部
    scroll = () => {
        let dom = document.getElementById('scroll'),
            //获取精确值
            full = +window.getComputedStyle(dom).height.slice(0, -2),
            view = +window.getComputedStyle(dom.parentNode).height.slice(0, -2),
            //储存预设 top
            top = 0,
            //滚动值
            scrollTop = 0,
            //是否到底部
            toTop = false;
        function animate(){
            top = toTop ? 0 : top - view
            let Interval = setInterval(function(){
                scrollTop = toTop ? scrollTop + 10 : scrollTop - 1
                dom.style.marginTop = scrollTop + 'px'
                if(toTop && scrollTop >= top){
                    toTop = false
                    dom.style.marginTop = top + 'px'
                    clearInterval(Interval)
                }
                if(!toTop && scrollTop <= top){
                    if(scrollTop - view <= -full) {
                        toTop = true
                    }
                    dom.style.marginTop = top + 'px'
                    clearInterval(Interval)
                }
            }, 1);
        }
        setInterval(animate, 4000)
    }
    //首尾循环
    //    scroll = () => {
    //        let dom = document.getElementById('scroll'),
    //            //获取精确值
    //            full = +window.getComputedStyle(dom).height.slice(0, -2),
    //            view = +window.getComputedStyle(dom.parentNode).height.slice(0, -2),
    //            li = +window.getComputedStyle(dom.firstChild).height.slice(0, -2),
    //            //滚动值
    //            scrollTop = 0,
    //            //储存 node
    //            liArr = []
    //        function animate(){
    //            //清空
    //            liArr = []
    //            //获取一屏的 node
    //            for (let i = 0; i < Math.ceil(view / li); i++){
    //                liArr.push(dom.childNodes[i])
    //            }
    //            //滚动事件
    //            let Interval = setInterval(function(){
    //                scrollTop -= 1
    //                dom.style.marginTop = scrollTop + 'px'
    //                //滚动到第二屏
    //                if(scrollTop <= -view){
    //                    clearInterval(Interval)
    //                    //重置
    //                    scrollTop = 0
    //                    //移动 node
    //                    liArr.map(item => {
    //                        dom.removeChild(item)
    //                        dom.appendChild(item)
    //                    })
    //                    //复位
    //                    dom.style.marginTop = '0px'
    //                }
    //            }, 1);
    //        }
    //        setInterval(animate, 4000)
    //    }
    
    ……

题外:顺便也可以指导下以上有哪些格式或者代码可以优化的

2962 次点击
所在节点    前端开发
5 条回复
ChefIsAwesome
2019-04-17 16:28:20 +08:00
两个尝试的方向:
1 时间不对,应该用 raf
2 滚动的时候用 translate y
kingsleydon
2019-04-17 16:39:59 +08:00
建议直接用库,滚动动画是很多动画库都有的基础功能,react-spring react-motion
leaveeel
2019-04-17 16:40:42 +08:00
@ChefIsAwesome
谢谢提供思路,raf 还没用过,我去试一下。
用 margin 来做偏移也是习惯了,我再优化一下,感谢建议
leaveeel
2019-04-19 10:45:49 +08:00
@kingsleydon 谢谢,后面会尝试一下。一直都不习惯找库,看完需求就直接撸:XD。有的东西写出来挺爽的就是花时间


@ChefIsAwesome 尝试用了 rAF 基本都解决了,就是当页面在后台的时候再切回来会一直执行动画到当前时间,试了用 visibilityState 控制也没用,就是 `if(this.state.tabVisible)` 这段。是不是方法写的有问题,主要代码在附言里,有空的话能再帮我看看吗。
ChefIsAwesome
2019-04-19 23:40:57 +08:00
@leaveeel 现在的浏览器为了省电,定时器在后台是停止的,恢复的时候一下子就把累计的定时器跑完。
你可以:
1.尝试不用 setInterval,用 setTimeout,类似递归的方式来写。
loop = () => { if(shouldLoop) setTimeout(loop, delay) };
2.尝试 debounce。这种短时间运行一堆东西,其实只有最后一个是有用的情况需要做 debounce。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/556088

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX