我们平时在开发前端页面的时候,经常会播放一段帧序列。这段帧序列就像gif图片那样,反复循环播放。那大家可能会说,直接用gif图片就好了,干嘛还去模拟呢?那是因为要做得更加灵活,我们要做到以下几点:

1、我们希望这段帧动画只循环播放所指定的次数。

2、我们希望帧动画结束的瞬间执行某种操作。这个在游戏中大量存在。

3、我们想自如的控制播放的速度。

4、我们想尽可能让这个帧动画的实现方式兼容大部分浏览器,在移动和pc端都能运行良好。

有了以上四点要求,那就不是gif图片所能完成的了。下面,我们先探讨有哪些技术可以实现我所说的功能。首先我们先准备好一张帧序列图片。如下图所示:

一、使用CSS3动画。

CSS3动画的timing-function里有一个step-end方式,可以不用缓慢过渡,而是直接以跳帧的方式实现变化。这个方式我认为是 最省事的办法了,然而在CSS3还未完全兼容的时代,step-end的兼容性更加差。就不说IE,我在智能机的几种基于webkit的低版本浏览器中测 试时也发现一些不兼容现象。考虑到css3的普及速度,此种方式依然值得大家学习。具体代码实现参考如下(篇幅考虑仅列出webkit的写法):

<html>
<head>
<style type="text/css">
#anim{
background-image: url(anim.png);
width:120px;
height:120px;
-webkit-animation: auto-circle 0.5s step-end infinite;
} @-webkit-keyframes auto-circle{
0%{
background-position-x: 0;
}
20%{
background-position-x: 120px;
}
40%{
background-position-x: 240px;
}
60%{
background-position-x: 360px;
}
80%{
background-position-x: 480px;
}
100%{
background-position-x: 600px;
}
}
</style>
</head>
<body>
<div id="anim">
</div>
</body>
</html>

以上代码可以在chrome浏览器中正常运行,然而,不知大家注意到一个问题没有。从0%到100%,其实只播放了帧动画的5帧,第6帧没有播放。 这是因为100/6无法得到整数值,所以无法均等分割。这也是这种方式的局限之一。由于苹果谷歌对translate2d和translate3d都有较 好的支持甚至硬件加速,为了得到更好的性能,我们可以不用background-position,而使用CSS3中的Transforms。当然,这需 要外层套一个overflow:hidden;的div。改善后的代码如下所示:

<html>
<head>
<style type="text/css">
#animbg{
width:120px;
height:120px;
overflow: hidden;
}
#anim{
-webkit-animation: auto-circle 0.5s step-end 0 5;
} @-webkit-keyframes auto-circle{
0%{
-webkit-transform:translate3d(0,0,0);
}
20%{
-webkit-transform:translate3d(-120px,0,0);
}
40%{
-webkit-transform:translate3d(-240px,0,0);
}
60%{
-webkit-transform:translate3d(-360px,0,0);
}
80%{
-webkit-transform:translate3d(-480px,0,0);
}
100%{
-webkit-transform:translate3d(-600px,0,0);
}
}
</style>
</head>
<body>
<div id="animbg">
<img id="anim" src="anim.png"></div>
</div>
</body>
<script type="text/javascript">
document.addEventListener('webkitAnimationEnd',function(){
document.getElementById('animbg').style.display = 'none';
});
</script>
</html>

最后的js代码中的webkitAnimationEnd就是用来绑定当css动画结束后的动作。

二、使用canvas

说道帧动画,很容易就联想到canvas。用drawImage方法将图片绘制到canvas上面,不断重绘就能得到我们想要的效果。使用 canvas的好处是,只要有一个基于canvas模拟帧动画的类库,就可以随意使用。操作js比操作css要灵活,可以传递各种参数实现不同的要求,还 可以使用回调函数来设置动画结束时的操作。缺点是老式浏览器不兼容canvas,而且如果需求简单的话,使用canvas有些大材小用。一个最简单的循环 动画类如下所示:

function MovieClip(x, y, img, width, height, totalFrame, fps)
{
this.x = x;
this.y = y;
this.img = document.getElementById(img);
this.time = 0;
this.frame = 0;
this.width = width;
this.height = height;
this.totalFrame = totalFrame;
this.fps = fps || 1;
} MovieClip.prototype.draw = function()
{
this.time ++;
if(this.time % this.fps == 0)
{
this.frame ++;
if(this.frame == this.totalFrame) this.frame = 0;
} var frame = this.frame;
context.drawImage(this.img, this.frame * 120, 0, 120, 120, this.x, this.y, this.width, this.height);
}

这段代码来自于王劲的html5小游戏billd源码,对canvas感兴趣的人可以去看看http://06wjin.sinaapp.com/html5/billd/

我也写了一个简单的操作工具库,可以用来操作canvas元素。代码如下:

(function(){
window['canvasgif'] = {
canvas:null,//canvas元素
context:null,//canvas环境
fps:30,//帧频
loopCount:1,//循环次数
tempCount:0,//当前的循环次数,用来计数 img_obj : {//图片信息保存变量
'source': null,
'current': 0,
'total_frames': 0,
'width': 0,
'height': 0
}, init:function(canvas,imgsrc,frames,fps,loopCount,fn){//初始化canvas和图片信息
var me = this;
me.canvas = canvas;
me.context = canvas.getContext("2d");
me.fps = fps || 30;
me.loopCount = loopCount || 1;
var img = new Image();
img.src = imgsrc || 'anim.png';
img.onload = function () {
me.img_obj.source = img;
me.img_obj.total_frames = frames;
me.img_obj.width = this.width/frames;
me.img_obj.height = this.height;
me.loopDraw(fn);
}
},
draw_anim:function(context,iobj){//绘制单张图片
if (iobj.source != null){
context.drawImage(iobj.source, iobj.current * iobj.width, 0,
iobj.width, iobj.height,0, 0, iobj.width, iobj.height);
iobj.current = (iobj.current + 1) % iobj.total_frames;
//如果不是无限循环,则需要计算当前循环次数
if(this.loopCount != -1 && iobj.current == iobj.total_frames - 1){
this.tempCount++;
}
}
}, /**
* 入口执行方法,对外的接口
* @param canvas canvas的dom对象,可通过getElementById得到,或jquery方法$('')[0]
* @param imgsrc 所绘制图片的路径,如'media/event/altar.png'
* @param frames 图片的帧数
* @param fps 绘制的帧频,如5,10,30等,数字越大,绘制速度越快
* @param loopCount 绘制的循环次数,-1为无限循环
* @param fn 如果不是无限循环则绘制结束时的回调函数
*
* 使用示例:window.canvasgif.render(canvas,imgsrc,12,5,1,function(){});
*
*/
render:function(canvas,imgsrc,frames,fps,loopCount,fn){
this.init(canvas,imgsrc,frames,fps,loopCount,fn);
}, loopDraw:function(fn){//循环绘制图片
var me = this;
var ctx = me.context;
var pic = me.img_obj;
var width = me.canvas.width,height = me.canvas.height;
var intervalid = setInterval((function(){
ctx.clearRect(0,0,width,height);
me.draw_anim(ctx,pic);
if(me.loopCount != -1 && me.tempCount == me.loopCount){
me.tempCount = 0;
clearInterval(intervalid);
ctx.clearRect(0,0,width,height);
typeof fn == "function" && fn();
}
}),1000/this.fps);
}
}
})();

三、使用javascript操作css属性

第三种方法是最保险的方法,因为既不使用css3,也不使用canvas,保证兼容了大部分的浏览器。思路很简单,就是靠javascript不断的改变帧图片的background-position。这里为了方便起见,使用的jquery。代码如下:

<html>
<head>
<style type="text/css">
#animbg{
background-image: url(anim.png);
width:120px;
height:120px;
background-repeat: no-repeat;
}
</style>
<script type="text/javascript" src="jquery-1.4.4.min.js"></script>
<script type="text/javascript">
(function(window){
window.frameAnimation = {
anims:(function(){
/*
obj=>需要执行背景动画的对象;
width:图片的总宽度
steps=>需要的帧数;
eachtime=>一次完整动画需要的时间;
times=>动画执行的次数 0表示无限反复
*/
return function(obj,width,steps,eachtime,times, callback){
var runing = false;
var handler = null; //obj,width,steps,eachtime,times定时器
var step = 0; //当前帧
var time = 0; //当前第几轮
var speed = eachtime*1000/steps; //间隔时间
var oneStepWidth = width/steps; function _play(){
if(step >= steps){
step = 0;
time++;
}
if(0 == times || time <times){
obj.css('background-position', -oneStepWidth * step + 'px 0px');
step++;
}else{
control.stop();
callback && callback();
}
} var control = {
start:function(){
if(!runing){
runing = true;
step = time = 0;
handler = setInterval(_play, speed);
}
return this;
} ,stop:function(restart){
if(runing){
runing = false;
if(handler){
clearInterval(handler);
handler = null;
}
if(restart){
obj.css('background-position', '0 0');
step = 0;
time = 0;
}
}
}
,dispose:function(){
this.stop();
//console.log('anim dispose');
}
};
return control;
}
})()
}
})(window); function play(){
var anim = frameAnimation.anims($('#animbg'),720,6,1,0);
anim.start();
} </script>
</head>
<body onload="play()">
<div id="animbg"></div>
</body>
</html>

总结:

三种方法各有优势和劣势。如果明确了浏览器的型号和版本支持css3时,推荐使用第一种方法。如果是为了广泛使用,推荐使用第三种方法。当序列帧很简单的时候,不建议使用canvas来实现功能。

原文链接

使用javascript和css模拟帧动画的几种方法浅析的更多相关文章

  1. cocos2d-x JS 加载播放Studio帧动画的两种方法

    昨天懵逼的搞了两个多小时(百度无果/没看出什么矛头),自己琢磨总算搞出来了   1.  var levelUpJson = ccs.load("res/LevelUp.json") ...

  2. Android使用帧动画内存溢出解决方法

    Android使用帧动画内存溢出解决方法https://blog.csdn.net/daitu_liang/article/details/52336015https://blog.csdn.net/ ...

  3. 用 CSS 隐藏页面元素的 5 种方法

    原文链接:用 CSS 隐藏页面元素的 5 种方法,转载请注明来源! 用 CSS 隐藏页面元素有许多种方法.你可以将 opacity 设为 0.将 visibility 设为 hidden.将 disp ...

  4. CSS 隐藏页面元素的 几 种方法总结

    用 CSS 隐藏页面元素有许多种方法.你可以将 opacity 设为 0.将 visibility 设为 hidden.将 display 设为 none 或者将 position 设为 absolu ...

  5. Javascript定义类(class)的三种方法

    将近20年前,Javascript诞生的时候,只是一种简单的网页脚本语言.如果你忘了填写用户名,它就跳出一个警告. 如今,它变得几乎无所不能,从前端到后端,有着各种匪夷所思的用途.程序员用它完成越来越 ...

  6. CSS中隐藏内容的3种方法及属性值

    CSS中隐藏内容的3种方法及属性值 (2011-02-11 13:33:59)   在制作网页时,隐藏内容也是一种比较常用的手法,它的作用一般有:隐藏文本/图片.隐藏链接.隐藏超出范围的内容.隐藏弹出 ...

  7. VB模拟键盘输入的N种方法

    VB模拟键盘输入的N种方法http://bbs.csdn.net/topics/90509805hd378发表于: 2006-12-24 14:35:39用VB模拟键盘事件的N种方法 键盘是我们使用计 ...

  8. css划隔横线的两种方法

    css划隔横线的两种方法  方法一:用DIV,代码如下:(推荐此方法)    <div style="width:800px;height:1px;margin:0px auto;pa ...

  9. 【转】css清除浮动float的三种方法总结,为什么清浮动?浮动会有那些影响?

    摘要: css清除浮动float的三种方法总结,为什么清浮动?浮动会有那些影响?     一.抛一块问题砖(display: block)先看现象: 分析HTML代码结构: <div class ...

随机推荐

  1. 基于USB网卡适配器劫持DHCP Server嗅探Windows NTLM Hash密码

    catalogue . DHCP.WPAD工作过程 . python Responder . USB host/client adapter(USB Armory): 包含DHCP Server . ...

  2. Ceph集群搭建及Kubernetes上实现动态存储(StorageClass)

    集群准备 ceph集群配置说明   节点名称 IP地址 配置 作用 ceph-moni-0 10.10.3.150 centos7.5 4C,16G,200Disk 管理节点,监视器 monitor ...

  3. Oracle Database 10g安装

    前言 oracle_10g_32位的安装包 链接:https://pan.baidu.com/s/1v1ZWYSjWLzKo3GnDuV5nrg 密码:88yd PLSQL Developer 32位 ...

  4. DirectX11 With Windows SDK--11 混合状态与光栅化状态

    前言 虽然这一部分的内容主要偏向于混合(Blending),但这里还需提及一下,关于渲染管线可以绑定的状态主要有如下四种: 光栅化状态(光栅化阶段) 采样器状态(像素着色阶段) 混合状态(输出合并阶段 ...

  5. java(9)类和对象

    一.理解什么是类和对象 万事万物皆对象 1.1.属性——对象具有的特征(特点) 1.2.方法——对象可执行的操作(能干什么事) 1.3.对象的定义: 是一个客观存在的,看的见或摸得着的实体,由属性和方 ...

  6. 电子医疗设备创新研发应该用i.MX6Q开发板吗?为医疗设备提供解决方案

    伴随医疗信息化.移动化.智能化的不断发展,居民对自身健康的关注度持续加强,全球医疗电子产业近年来持续保持快速增长态势.同时,源于庞大的人口基数以及迅速增长的老龄化人口带来持续增长的医疗服务需求,加之全 ...

  7. 手机端调用app导航

    因为是在微信端中操作,所以只能使用腾讯地图才能调起手机的腾讯地图app <!doctype html> <html> <head> <meta charset ...

  8. H - 栀子花开

    这是一个栀子花开的季节,也是一个离别的季节,四年一千多个日日夜夜,那校园的角角落落,留下了我们沉思的身影:那上百次的成绩排名表,印证了我们深深浅浅不断进步的轨迹,但是这些进步都离不开老师的谆谆教诲. ...

  9. sublime代码整理

    1. 2.输入: { "keys":["ctrl+shift+z"], (快捷键自定义,只要不冲突就可以了) "command":" ...

  10. 使用virtualenvwrapper模块管理python虚拟环境

    1.pip安装virtualenvwrapper [root@localhost ~]# pip install virtualenvwrapper # 安装virtualenvwrapper [ro ...