JavaScript中的该如何[更好的]做动效

在用js写动画的时候,无非使用 setTimeout/setInterval 或者 requestAnimationFrame 来处理动画(在jquery的代码里也是这么干的),本文主要为了记录下两者的区别及使用两者来实现动过程。

以实现一个简单的滚动到顶部为例

setInterval

setInterval() 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。返回一个intervalID,可用于 cancelInterval 达到结束循环的效果。

setTimeout 和 setInterval 的实现基本没区别,一个是定时执行,一个是定时循环执行,前者加个自己调用自己就是后者了,下面主要以 setInterval 为代表

实现过程:

1.写个方法,该方法需要传入一个代表动画所需执行的时间的参数(如:滚动到顶部需要1000毫秒)

function doAnimate(duration){
    return function(){
        // do something
    }
}

2.取当前页面距顶部高度、滚动速度(以匀速为例)、写个开始动画的函数(为了给addEventListener绑事件传参,其实也可直接 dom.onclick = fn )

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        var scrollSpeed = start/duration*(1000/60); // 以大多浏览器的刷新频率60帧(60Hz)为准  1秒60次的刷新
        var timer;
        var startTime = +new Date(); // 标记时间,仅供后面测效果用而已
        function startAnimate(){
            timer = setInterval(function () {
                // do something
            },1000/60)
        }
    }
}

3.写滚动动画

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新
        var timer;
        function startAnimate(){
            timer = setInterval(function () {
                document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减
                if(start === 0){
                    clearInterval(timer);
                }
            },1000/60)
        }
    }
}

4.写个很高的页面、给个div、加个click事件触发滚动回顶部

html

<!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />

<div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

js

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新
        var timer;
        function startAnimate(){
            timer = setInterval(function () {
                document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减
                if(start === 0){
                    clearInterval(timer);
                }
            },1000/60)
        }
    }
}
document.getElementById(‘scrollTop_1000‘).addEventListener(‘click‘,doAnimate(1000),!1)
document.getElementById(‘scrollTop_3000‘).addEventListener(‘click‘,doAnimate(3000),!1)

效果如图:

截图用了霸气十足的面子果实能力者,分别测了设置 duration 为1000和3000的滚动效果 -。- 嗯...他是个好人

requestAnimationFrame

requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。返回一个 requestID ,可用于 cancelAnimationFrame 达到取消 requestAnimationFrame 动画的效果。

实现思路如上,代码如下:

html

<!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />

<div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

js

function doAnimate(duration){
    return function(){
        var start = document.documentElement.scrollTop;
        // 获取实时时间
        var nowTime = function(){
            return +new Date
        }
        // 开始时间 用于计算动画运行时间和动画规定时间的百分比
        var startTime = nowTime();
        var animateId;
        var startAnimate = function() {
            animateId = requestAnimationFrame(toTop);
        }
        var stopAnimate = function() {
            cancelAnimationFrame(animateId)
        }
        function toTop(){
            // 剩下时间
            var restTime = Math.max(0, duration - ( nowTime() - startTime))
            var percent = restTime / duration || 0;
            var changeStyle = function(value){
                document.documentElement.scrollTop = value;
            }
            // 根本比例获取剩下的距离,也就是实时距离顶部的距离
            var distance = start * percent;
            if(!distance){
                changeStyle(distance)
                stopAnimate();
            }else{
                changeStyle(distance)
                startAnimate(toTop);
            }
        }
        startAnimate(toTop);
    }
}

document.getElementById(‘scrollTop_1000‘).addEventListener(‘click‘,doAnimate(1000),!1)
document.getElementById(‘scrollTop_3000‘).addEventListener(‘click‘,doAnimate(3000),!1)

效果如图:

没区别,没毛病,然而并没有和上面用同一张图...(其实打印下时间,会发现 setInterval 会是1000毫秒以内,大致在960-980毫秒之间,这个梗哪位大神可知???求解!!!)

两者的区别

requestAnimationFrame 会请求浏览器调用指定的函数在下一次重绘之前更新动画,所以开发者不用考虑频率/丢帧问题

setInterval 中,会因为浏览器显示频率和 JavaScript 单线程可能会引发阻塞的问题而导致丢帧(视觉应为动画不流畅)

requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,性能方面更出色

对于隐藏或者不可见的元素,requestAnimationFrame 将不会进行重绘或回流,这点可减少cpu,gpu及内存的负荷

setInterval 兼容一些老版本的浏览器(jquery保留这个应该也是为了兼容老版本浏览器...)

requestAnimationFrame 兼容图

顺便扔上jquery里animate的部分代码:

jQuery.fx.start = function() {
    if ( !timerId ) {
        timerId = window.requestAnimationFrame ?
            window.requestAnimationFrame( raf ) :
            window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
    }
};

jQuery.fx.stop = function() {
    if ( window.cancelAnimationFrame ) {
        window.cancelAnimationFrame( timerId );
    } else {
        window.clearInterval( timerId );
    }

    timerId = null;
};

这里的 jQuery.fx.interval 默认值为 13 ,也是因为显示频率的问题~

略显尴尬... 在我windows上和mac上也保留一些老版本的浏览器,测效果的结果简直蛋疼...看来兼容方面还是需要做处理的,天将降大任于 setInterval 啊    :-D

面子果实都登场了,小伙伴们是不是也该顶一顶了呢 [偷笑][偷笑][偷笑] 欢迎交流 欢迎指出各个问题~

时间: 2024-12-22 03:49:48

JavaScript中的该如何[更好的]做动效的相关文章

javascript中的this与函数讲解

前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码,都存放在Window函数内(这是个假设),也就是说javascript中只有函数作用域(前面假设做前提下). 作用域是什么 作用域是一个盒子,盒子内部的变量只能在当前盒子中使用,作用域盒子是可以嵌套的,内部盒子的变量对父级盒子是不可见的,因为盒子封闭了他们并且盒子不透明,但是盒子可以看到父级盒子内部

理清javascript中prototype、__proto__、Object、Function的关系,更好地理解原型继承

本文参考了http://www.blogjava.net/heavensay/archive/2013/10/20/405440.html这篇文章,对其内容作了个简单总结,形成了几条简单的结论,让读者更容易记住prototype.__proto__.Object.Function之间的关系. 结论1:Object.prototype只是一个普通对象,它是js原型链的最顶端. (typeof Object.prototype) === object;//true Object.prototype.

【转】十个JavaScript中易犯的小错误,你中了几枪?

在今天,JavaScript已经成为了网页编辑的核心.尤其是过去的几年,互联网见证了在SPA开发.图形处理.交互等方面大量JS库的出现. 如果初次打交道,很多人会觉得js很简单.确实,对于很多有经验的工程师,或者甚至是初学者而言,实现基本的js功能几乎毫无障碍.但是JS的真实功能却比很多人想象的要更加多样.复杂.JavaScript的许多细节规定会让你的网页出现很多意想不到的bug,搞懂这些bug,对于成为一位有经验的JS开发者很重要. 常见错误一:对于this关键词的不正确引用 我曾经听一位喜

JavaScript中的三种弹出对话框

JavaScript中的三种弹出对话框 *****本文来自互联网****** 学习过js的小伙伴会发现,我们在一些实例中用到了alert()方法.prompt()方法.prompt()方法,他们都是在屏幕上弹出一个对话框,并且在上面显示括号内的内容,使用这种方法使得页面的交互性更精彩,实际上我们经常会在进行网页浏览时简单这种类型的对话框,在用户与应用程序进行双向交流时,经常要用到对话框.avascript的三种对话框是通过调用window对象的三个方法alert(),confirm()和prom

转---深入浅出妙用 Javascript 中 apply、call、bind

作者:伯乐在线专栏作者 - chokcoco 如有好文章投稿,请点击 → 这里了解详情 如需转载,发送「转载」二字查看说明 这篇文章实在是很难下笔,因为网上相关文章不胜枚举. 巧合的是前些天看到阮老师的一篇文章的一句话: "对我来说,博客首先是一种知识管理工具,其次才是传播工具.我的技术文章,主要用来整理我还不懂的知识.我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写.炫耀从来不是我的动机,好奇才是." 对于这句话,不能赞同更多,也让我下决心好好写这篇,网上文章虽多,

深入浅出 妙用Javascript中apply、call、bind

apply.call.bind的认识,并且列出一些它们的妙用加深记忆. apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念. 先来一个栗子: 1 2 3 4 5 6 7 8 9 10 11 function fruits() {} fruit

javascript中defer的作用

javascript中defer的作用 <script src="../CGI-bin/delscript.js" defer></script>中的defer作用是文档加载完毕了再执行脚本,这样回避免找不到对象的问题 加上 defer 等于在页面完全在入后再执行,相当于 window.onload ,但应用上比 window.onload 更灵活!defer是脚本程序强大功能中的一个"无名英雄".它告诉浏览器Script段包含了无需立即执行

深入理解JavaScript中创建对象模式的演变(原型)

创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Object构造函数和对象字面量方法 工厂模式 自定义构造函数模式 原型模式 组合使用自定义构造函数模式和原型模式 动态原型模式.寄生构造函数模式.稳妥构造函数模式 第一部分:Object构造函数和对象字面量方法 我之前在博文<javascript中对象字面量的理解>中讲到过这两种方法,如何大家不熟悉,可以点进去看一看回顾一下.它们的优点是用来创建单个的对象非常方

【转】深入浅出 JavaScript 中的 this

Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象.一般在编译期确定下来,或称为编译期绑定.而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑.本文仅就这一问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What ’s this 问题,作为作者,我就会觉得花费这么多功夫,撰写这样一篇文章是值得的. Java