所谓帧动画就是在连续的关键帧中分解动画动作,也就是在时间轴的每一帧上面逐帧绘制出不同的内容,使得其能够连续播放而成为动画
常见的帧动画
- Gif
- CSS animation
- Javascript
前两者实现帧动画存在着不足:
- 不能灵活控制动画的暂停⏸️和播放
- 不能对帧动画做更加灵活的扩展
- Gif不能捕捉到动画完成事件
实现原理
使用JS实现帧动画的原理主要有两个:
- 多图:借助
<imgae>
标签承载图片,定时改变src
属性( 需发送多个http请求💔) - 雪碧图:把关键帧绘制在同一张图片,作为元素的
background-image
,定时改变元素的background-position
属性(推荐🙆)
运动框架实现思路
- 速度(left/right/width/height/opacity)
- 缓冲运动
- 多物体运动(所有的东西都不能公用,如timer和opacity)
- 任意值变化
- 链式运动
- 同时运动
设计通用的帧动画库
- 支持图片预加载
- 支持动画播放方式,以及自定义每一帧动画
- 支持单组动画控制循环次数(可支持无限次)
- 支持一组动画完成,再进行下一组动画
- 支持每个动画完成后有等待时间
- 支持动画暂停和继续播放
- 支持动画完成后执行回调
任务链: 图像预加载 ➡️ 动画执行 ➡️ 动画结束
任务链有两种类型的任务:同时执行完毕;异步定时执行
getStyle
方法——可以获取到某些样式的属性值
currentStyle
针对IE浏览器,getComputedStyle
针对firefox浏览器
function getStyle(obj, attr){
if(obj.currenrStyle){
return obj.currentStyle[attr];
}
return getComputedStyle(obj,false)[attr];
}
实现原生js动画的方法
- DOM对象
- 目标属性,目标值(json文件形式)
- 回调函数(可选)
支持传入多个属性值
支持同时运动的动画
支持链式动画(回调)
function startMove(obj, attrs, fn){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
let flag = true; // 是否清空计时器的标志
for(let a in attrs){
// 1. 获取当前值
let iTarget = attrs[a];
let curStyle = 0;
if(a === 'opacity'){
curStyle = Math.round(parseFloat(getStyle(obj, a))*100);
} else{
curStyle = parseInt(getStyle(obj, a));
}
// 2. 计算速度
let speed = (iTarget - curStyle) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
// 3. 检测停止(只要有属性未达到目标值,就继续运动)
if(curStyle !== iTarget){
flag = false;
}
if(a === 'opacity'){
obj.style.filter = 'opacity(' + (curStyle+speed) + ')';
obj.style.opacity = (curStyle + speed) / 100
} else {
obj.style[a] = curStyle + speed + 'px';
}
}
if(flag){
clearInterval(obj.timer);
if(fn){
fn();
}
}
}, 30)
}
设计一个通用的帧动画库
- loadImage(imglist) //预加载图片
- changePosition(ele,positions,imageUrl) //通过改变元素的backgroun-position实现动画
- changeSrc(ele,imglist) //通过改变image元素的src
- enterFrame(callback) //每一帧动画执行的函数,相当于用户可以自定义,每一帧动画的callback
- repeat(times) //动画重复执行的次数,times为空时表示无限次
- repeatForever() //无限重复上一次动画,相当于repeat(),更友好的一个接口吧
- wait(time) //每个动画执行完成等待的时间
- then(callback) //动画执行完成后的回调函数
- start(interval) //动画开始执行,interval表示动画执行的间隔
- pause() //动画暂停
- restart() //动画从上一次暂停处重新执行
- dispose() //释放资源
帧动画类库设计在Animation.js
中
对比jquery
使用原生的js实现动画需要挂在window.onload()
上
window.onload = function(){
var oDiv = document.getElementById('move');
var aList = oDiv.getElementsByTagName('a');
for(let i in aList){
aList[i].onmouseover = function(){
var _this = this.getElementsByTagName('i')[0];
startMove(_this, {top: -25, opacity: 0 }, function(){
_this.style.top = 30 + 'px';
startMove(_this, {top: 20, opacity: 100});
});
}
}
}
使用jquery也可以实现以上,并且非常方便
$(function(){
$('#move a').mouseenter(function(){
$(this).find('i').animate({top:"-25px", opacity: "0"}, 300, function(){
$(this).css({top: "30px"});
$(this).css({top: "20px", opacity: "1"}, 200)
})
})
})
更多完整的原生的js实现动画的实例放到了个人仓库上 JS Animation