javascript幀動(dòng)畫(huà)(實(shí)例講解)
前面的話(huà)
幀動(dòng)畫(huà)就是在“連續(xù)的關(guān)鍵幀”中分解動(dòng)畫(huà)動(dòng)作,也就是在時(shí)間軸的每幀上逐幀繪制不同的內(nèi)容,使其連續(xù)播放而成的動(dòng)畫(huà)。由于是一幀一幀的畫(huà),所以幀動(dòng)畫(huà)具有非常大的靈活性,幾乎可以表現(xiàn)任何想表現(xiàn)的內(nèi)容。本文將詳細(xì)介紹javascript幀動(dòng)畫(huà)
概述
【分類(lèi)】
常見(jiàn)的幀動(dòng)畫(huà)的方式有三種,包括gif、CSS3 animation和javascript
git和CSS3 animation不能靈活地控制動(dòng)畫(huà)的暫停和播放、不能對(duì)幀動(dòng)畫(huà)做更加靈活地?cái)U(kuò)展。另外,gif圖不能捕捉動(dòng)畫(huà)完成的事件。所以,一般地,使用javascript來(lái)實(shí)現(xiàn)幀動(dòng)畫(huà)
【原理】
js實(shí)現(xiàn)幀動(dòng)畫(huà)有兩種實(shí)現(xiàn)方式
1、如果有多張幀動(dòng)畫(huà)圖片,可以用一個(gè)image標(biāo)簽去承載圖片,定時(shí)改變image的src屬性(不推薦)
2、把所有的動(dòng)畫(huà)關(guān)鍵幀都繪制在一張圖片里,把圖片作為元素的background-image,定時(shí)改變?cè)氐腷ackground-position屬性(推薦)
因?yàn)榈谝环N方式需要使用多個(gè)HTTP請(qǐng)求,所以一般地推薦使用第二種方式
【實(shí)例】
下面是使用幀動(dòng)畫(huà)制作的一個(gè)實(shí)例
<div id="rabbit" ></div>
<button id="btn">暫停運(yùn)動(dòng)</button>
<script>
var url = 'rabbit-big.png';
var positions = ['0,-854','-174 -852','-349 -852','-524 -852','-698 -852','-873 -848'];
var ele = document.getElementById('rabbit');
var oTimer = null;
btn.onclick = function(){
if(btn.innerHTML == '開(kāi)始運(yùn)動(dòng)'){
frameAnimation(ele,positions,url);
btn.innerHTML = '暫停運(yùn)動(dòng)';
}else{
clearTimeout(oTimer);
btn.innerHTML = '開(kāi)始運(yùn)動(dòng)';
}
}
frameAnimation(ele,positions,url);
function frameAnimation(ele,positions,url){
ele.style.backgroundImage = 'url(' + url + ')';
ele.style.backgroundRepeat = 'no-repeat';
var index = 0;
function run(){
var pos = positions[index].split(' ');
ele.style.backgroundPosition = pos[0] + 'px ' + pos[1] + 'px';
index++;
if(index >= positions.length){
index = 0;
}
oTimer = setTimeout(run,80);
}
run();
}
</script>
通用幀動(dòng)畫(huà)
下面來(lái)設(shè)計(jì)一個(gè)通用的幀動(dòng)畫(huà)庫(kù)
【需求分析】
1、支持圖片預(yù)加載
2、支持兩種動(dòng)畫(huà)播放方式,及自定義每幀動(dòng)畫(huà)
3、支持單組動(dòng)畫(huà)控制循環(huán)次數(shù)(可支持無(wú)限次)
4、支持一組動(dòng)畫(huà)完成,進(jìn)行下一組動(dòng)畫(huà)
5、支持每個(gè)動(dòng)畫(huà)完成后有等待時(shí)間
6、支持動(dòng)畫(huà)暫停和繼續(xù)播放
7、支持動(dòng)畫(huà)完成后執(zhí)行回調(diào)函數(shù)
【編程接口】
1、loadImage(imglist)//預(yù)加載圖片
2、changePosition(ele,positions,imageUrl)//通過(guò)改變?cè)氐腷ackground-position實(shí)現(xiàn)動(dòng)畫(huà)
3、changeSrc(ele,imglist)//通過(guò)改變image元素的src
4、enterFrame(callback)//每一幀動(dòng)畫(huà)執(zhí)行的函數(shù),相當(dāng)于用戶(hù)可以自定義每一幀動(dòng)畫(huà)的callback
5、repeat(times)//動(dòng)畫(huà)重復(fù)執(zhí)行的次數(shù),times為空時(shí)表示無(wú)限次
6、repeatForever()//無(wú)限重復(fù)上一次動(dòng)畫(huà),相當(dāng)于repeat()
7、wait(time)//每個(gè)動(dòng)畫(huà)執(zhí)行完成后等待的時(shí)間
8、then(callback)//動(dòng)畫(huà)執(zhí)行完成后的回調(diào)函數(shù)
9、start(interval)//動(dòng)畫(huà)開(kāi)始執(zhí)行,interval表示動(dòng)畫(huà)執(zhí)行的間隔
10、pause()//動(dòng)畫(huà)暫停
11、restart()//動(dòng)畫(huà)從上一交暫停處重新執(zhí)行
12、dispose()//釋放資源
【調(diào)用方式】
支持鏈?zhǔn)秸{(diào)用,用動(dòng)詞的方式描述接口
【代碼設(shè)計(jì)】
1、把圖片預(yù)加載 -> 動(dòng)畫(huà)執(zhí)行 -> 動(dòng)畫(huà)結(jié)束等一系列操作看成一條任務(wù)鏈。任務(wù)鏈包括同步執(zhí)行和異步定時(shí)執(zhí)行兩種任務(wù)
2、記錄當(dāng)前任務(wù)鏈的索引
3、每個(gè)任務(wù)執(zhí)行完畢后,通過(guò)調(diào)用next方法,執(zhí)行下一個(gè)任務(wù),同時(shí)更新任務(wù)鏈索引值

【接口定義】
'use strict';
/* 幀動(dòng)畫(huà)庫(kù)類(lèi)
* @constructor
*/
function FrameAnimation(){}
/* 添加一個(gè)同步任務(wù),去預(yù)加載圖片
* @param imglist 圖片數(shù)組
*/
FrameAnimation.prototype.loadImage = function(imglist){}
/* 添加一個(gè)異步定時(shí)任務(wù),通過(guò)定時(shí)改變圖片背景位置,實(shí)現(xiàn)幀動(dòng)畫(huà)
* @param ele dom對(duì)象
* @param positions 背景位置數(shù)組
* @param imageUrl 圖片URL地址
*/
FrameAnimation.prototype.changePosition = function(ele,positions,imageUrl){}
/* 添加一個(gè)異步定時(shí)任務(wù),通過(guò)定時(shí)改變image標(biāo)簽的src屬性,實(shí)現(xiàn)幀動(dòng)畫(huà)
* @param ele dom對(duì)象
* @param imglist 圖片數(shù)組
*/
FrameAnimation.prototype.changeSrc = function(ele,imglist){}
/* 添加一個(gè)異步定時(shí)任務(wù),自定義動(dòng)畫(huà)每幀執(zhí)行的任務(wù)函數(shù)
* @param tastFn 自定義每幀執(zhí)行的任務(wù)函數(shù)
*/
FrameAnimation.prototype.enterFrame = function(taskFn){}
/* 添加一個(gè)同步任務(wù),在上一個(gè)任務(wù)完成后執(zhí)行回調(diào)函數(shù)
* @param callback 回調(diào)函數(shù)
*/
FrameAnimation.prototype.then = function(callback){}
/* 開(kāi)始執(zhí)行任務(wù),異步定時(shí)任務(wù)執(zhí)行的間隔
* @param interval
*/
FrameAnimation.prototype.start = function(interval){}
/* 添加一個(gè)同步任務(wù),回退到上一個(gè)任務(wù),實(shí)現(xiàn)重復(fù)上一個(gè)任務(wù)的效果,可以定義重復(fù)的次數(shù)
* @param times 重復(fù)次數(shù)
*/
FrameAnimation.prototype.repeat = function(times){}
/* 添加一個(gè)同步任務(wù),相當(dāng)于repeat(),無(wú)限循環(huán)上一次任務(wù)
*
*/
FrameAnimation.prototype.repeatForever = function(){}
/* 設(shè)置當(dāng)前任務(wù)執(zhí)行結(jié)束后到下一個(gè)任務(wù)開(kāi)始前的等待時(shí)間
* @param time 等待時(shí)長(zhǎng)
*/
FrameAnimation.prototype.wait = function(time){}
/* 暫停當(dāng)前異步定時(shí)任務(wù)
*
*/
FrameAnimation.prototype.pause = function(){}
/* 重新執(zhí)行上一次暫停的異步定時(shí)任務(wù)
*
*/
FrameAnimation.prototype.restart = function(){}
/* 釋放資源
*
*/
FrameAnimation.prototype.dispose = function(){}
圖片預(yù)加載
圖片預(yù)加載是一個(gè)相對(duì)獨(dú)立的功能,可以將其封裝為一個(gè)模塊imageloader.js
'use strict';
/**
* 預(yù)加載圖片函數(shù)
* @param images 加載圖片的數(shù)組或者對(duì)象
* @param callback 全部圖片加載完畢后調(diào)用的回調(diào)函數(shù)
* @param timeout 加載超時(shí)的時(shí)長(zhǎng)
*/
function loadImage(images,callback,timeout){
//加載完成圖片的計(jì)數(shù)器
var count = 0;
//全部圖片加載成功的標(biāo)志位
var success = true;
//超時(shí)timer的id
var timeoutId = 0;
//是否加載超時(shí)的標(biāo)志位
var isTimeout = false;
//對(duì)圖片數(shù)組(或?qū)ο?進(jìn)行遍歷
for(var key in images){
//過(guò)濾prototype上的屬性
if(!images.hasOwnProperty(key)){
continue;
}
//獲得每個(gè)圖片元素
//期望格式是object:{src:xxx}
var item = images[key];
if(typeof item === 'string'){
item = images[key] = {
src:item
};
}
//如果格式不滿(mǎn)足期望,則丟棄此條數(shù)據(jù),進(jìn)行下一次遍歷
if(!item || !item.src){
continue;
}
//計(jì)數(shù)+1
count++;
//設(shè)置圖片元素的id
item.id = '__img__' + key + getId();
//設(shè)置圖片元素的img,它是一個(gè)Image對(duì)象
item.img = window[item.id] = new Image();
doLoad(item);
}
//遍歷完成如果計(jì)數(shù)為0,則直接調(diào)用callback
if(!count){
callback(success);
}else if(timeout){
timeoutId = setTimeout(onTimeout,timeout);
}
/**
* 真正進(jìn)行圖片加載的函數(shù)
* @param item 圖片元素對(duì)象
*/
function doLoad(item){
item.status = 'loading';
var img = item.img;
//定義圖片加載成功的回調(diào)函數(shù)
img.onload = function(){
success = success && true;
item.status = 'loaded';
done();
}
//定義圖片加載失敗的回調(diào)函數(shù)
img.onerror = function(){
success = false;
item.status = 'error';
done();
}
//發(fā)起一個(gè)http(s)請(qǐng)求
img.src = item.src;
/**
* 每張圖片加載完成的回調(diào)函數(shù)
*/
function done(){
img.onload = img.onerror = null;
try{
delete window[item.id];
}catch(e){
}
//每張圖片加載完成,計(jì)數(shù)器減1,當(dāng)所有圖片加載完成,且沒(méi)有超時(shí)的情況,清除超時(shí)計(jì)時(shí)器,且執(zhí)行回調(diào)函數(shù)
if(!--count && !isTimeout){
clearTimeout(timeoutId);
callback(success);
}
}
}
/**
* 超時(shí)函數(shù)
*/
function onTimeout(){
isTimeout = true;
callback(false);
}
}
var __id = 0;
function getId(){
return ++__id;
}
module.exports = loadImage;
時(shí)間軸
在動(dòng)畫(huà)處理中,是通過(guò)迭代使用setTimeout()實(shí)現(xiàn)的,但是這個(gè)間隔時(shí)間并不準(zhǔn)確。下面,來(lái)實(shí)現(xiàn)一個(gè)時(shí)間軸類(lèi)timeline.js
'use strict';
var DEFAULT_INTERVAL = 1000/60;
//初始化狀態(tài)
var STATE_INITIAL = 0;
//開(kāi)始狀態(tài)
var STATE_START = 1;
//停止?fàn)顟B(tài)
var STATE_STOP = 2;
var requestAnimationFrame = (function(){
return window.requestAnimationFrame || window.webkitRequestAnimationFrame|| window.mozRequestAnimationFrame || window.oRequestAnimationFrame || function(callback){
return window.setTimeout(callback,(callback.interval || DEFAULT_INTERVAL));
}
})();
var cancelAnimationFrame = (function(){
return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || function(id){
return window.clearTimeout(id);
}
})();
/**
* 時(shí)間軸類(lèi)
* @constructor
*/
function Timeline(){
this.animationHandler = 0;
this.state = STATE_INITIAL;
}
/**
* 時(shí)間軸上每一次回調(diào)執(zhí)行的函數(shù)
* @param time 從動(dòng)畫(huà)開(kāi)始到當(dāng)前執(zhí)行的時(shí)間
*/
Timeline.prototype.onenterframe = function(time){
}
/**
* 動(dòng)畫(huà)開(kāi)始
* @param interval 每一次回調(diào)的間隔時(shí)間
*/
Timeline.prototype.start = function(interval){
if(this.state === STATE_START){
return;
}
this.state = STATE_START;
this.interval = interval || DEFAULT_INTERVAL;
startTimeline(this,+new Date());
}
/**
* 動(dòng)畫(huà)停止
*/
Timeline.prototype.stop = function(){
if(this.state !== STATE_START){
return;
}
this.state = STATE_STOP;
//如果動(dòng)畫(huà)開(kāi)始過(guò),則記錄動(dòng)畫(huà)從開(kāi)始到現(xiàn)在所經(jīng)歷的時(shí)間
if(this.startTime){
this.dur = +new Date() - this.startTime;
}
cancelAnimationFrame(this.animationHandler);
}
/**
* 重新開(kāi)始動(dòng)畫(huà)
*/
Timeline.prototype.restart = function(){
if(this.state === STATE_START){
return;
}
if(!this.dur || !this.interval){
return;
}
this.state = STATE_START;
//無(wú)縫連接動(dòng)畫(huà)
startTimeline(this,+new Date()-this.dur);
}
/**
* 時(shí)間軸動(dòng)畫(huà)啟動(dòng)函數(shù)
* @param timeline 時(shí)間軸的實(shí)例
* @param startTime 動(dòng)畫(huà)開(kāi)始時(shí)間戳
*/
function startTimeline(timeline,startTime){
//記錄上一次回調(diào)的時(shí)間戳
var lastTick = +new Date();
timeline.startTime = startTime;
nextTick.interval = timeline.interval;
nextTick();
/**
* 每一幀執(zhí)行的函數(shù)
*/
function nextTick(){
var now = +new Date();
timeline.animationHandler = requestAnimationFrame(nextTick);
//如果當(dāng)前時(shí)間與上一次回調(diào)的時(shí)間戳大于設(shè)置的時(shí)間間隔,表示這一次可以執(zhí)行回調(diào)函數(shù)
if(now - lastTick >= timeline.interval){
timeline.onenterframe(now - startTime);
lastTick = now;
}
}
}
module.exports = Timeline;
動(dòng)畫(huà)類(lèi)實(shí)現(xiàn)
下面是動(dòng)畫(huà)類(lèi)animation.js實(shí)現(xiàn)的完整代碼
'use strict';
var loadImage = require('./imageloader');
var Timeline = require('./timeline');
//初始化狀態(tài)
var STATE_INITIAL = 0;
//開(kāi)始狀態(tài)
var STATE_START = 1;
//停止?fàn)顟B(tài)
var STATE_STOP = 2;
//同步任務(wù)
var TASK_SYNC = 0;
//異步任務(wù)
var TASK_ASYNC = 1;
/**
* 簡(jiǎn)單的函數(shù)封裝,執(zhí)行callback
* @param callback 執(zhí)行函數(shù)
*/
function next(callback){
callback && callback();
}
/* 幀動(dòng)畫(huà)庫(kù)類(lèi)
* @constructor
*/
function FrameAnimation(){
this.taskQueue = [];
this.index = 0;
this.timeline = new Timeline();
this.state = STATE_INITIAL;
}
/* 添加一個(gè)同步任務(wù),去預(yù)加載圖片
* @param imglist 圖片數(shù)組
*/
FrameAnimation.prototype.loadImage = function(imglist){
var taskFn = function(next){
loadImage(imglist.slice(),next);
};
var type = TASK_SYNC;
return this._add(taskFn,type);
}
/* 添加一個(gè)異步定時(shí)任務(wù),通過(guò)定時(shí)改變圖片背景位置,實(shí)現(xiàn)幀動(dòng)畫(huà)
* @param ele dom對(duì)象
* @param positions 背景位置數(shù)組
* @param imageUrl 圖片URL地址
*/
FrameAnimation.prototype.changePosition = function(ele,positions,imageUrl){
var len = positions.length;
var taskFn;
var type;
if(len){
var me = this;
taskFn = function(next,time){
if(imageUrl){
ele.style.backgroundImage = 'url(' + imageUrl + ')';
}
//獲得當(dāng)前背景圖片位置索引
var index = Math.min(time/me.interval|0,len);
var position = positions[index-1].split(' ');
//改變dom對(duì)象的背景圖片位置
ele.style.backgroundPosition = position[0] + 'px ' + position[1] + 'px';
if(index === len){
next();
}
}
type = TASK_ASYNC;
}else{
taskFn = next;
type = TASK_SYNC;
}
return this._add(taskFn,type);
}
/* 添加一個(gè)異步定時(shí)任務(wù),通過(guò)定時(shí)改變image標(biāo)簽的src屬性,實(shí)現(xiàn)幀動(dòng)畫(huà)
* @param ele dom對(duì)象
* @param imglist 圖片數(shù)組
*/
FrameAnimation.prototype.changeSrc = function(ele,imglist){
var len = imglist.length;
var taskFn;
var type;
if(len){
var me = this;
taskFn = function(next,time){
//獲得當(dāng)前背景圖片位置索引
var index = Math.min(time/me.interval|0,len);
//改變image對(duì)象的背景圖片位置
ele.src = imglist[index-1];
if(index === len){
next();
}
}
type = TASK_ASYNC;
}else{
taskFn = next;
type = TASK_SYNC;
}
return this._add(taskFn,type);
}
/* 添加一個(gè)異步定時(shí)任務(wù),自定義動(dòng)畫(huà)每幀執(zhí)行的任務(wù)函數(shù)
* @param tastFn 自定義每幀執(zhí)行的任務(wù)函數(shù)
*/
FrameAnimation.prototype.enterFrame = function(taskFn){
return this._add(taskFn,TASK_ASYNC);
}
/* 添加一個(gè)同步任務(wù),在上一個(gè)任務(wù)完成后執(zhí)行回調(diào)函數(shù)
* @param callback 回調(diào)函數(shù)
*/
FrameAnimation.prototype.then = function(callback){
var taskFn = function(next){
callback(this);
next();
};
var type = TASK_SYNC;
return this._add(taskFn,type);
}
/* 開(kāi)始執(zhí)行任務(wù),異步定義任務(wù)執(zhí)行的間隔
* @param interval
*/
FrameAnimation.prototype.start = function(interval){
if(this.state === STATE_START){
return this;
}
//如果任務(wù)鏈中沒(méi)有任務(wù),則返回
if(!this.taskQueue.length){
return this;
}
this.state = STATE_START;
this.interval = interval;
this._runTask();
return this;
}
/* 添加一個(gè)同步任務(wù),回退到上一個(gè)任務(wù),實(shí)現(xiàn)重復(fù)上一個(gè)任務(wù)的效果,可以定義重復(fù)的次數(shù)
* @param times 重復(fù)次數(shù)
*/
FrameAnimation.prototype.repeat = function(times){
var me = this;
var taskFn = function(){
if(typeof times === 'undefined'){
//無(wú)限回退到上一個(gè)任務(wù)
me.index--;
me._runTask();
return;
}
if(times){
times--;
//回退
me.index--;
me._runTask();
}else{
//達(dá)到重復(fù)次數(shù),跳轉(zhuǎn)到下一個(gè)任務(wù)
var task = me.taskQueue[me.index];
me._next(task);
}
}
var type = TASK_SYNC;
return this._add(taskFn,type);
}
/* 添加一個(gè)同步任務(wù),相當(dāng)于repeat(),無(wú)限循環(huán)上一次任務(wù)
*
*/
FrameAnimation.prototype.repeatForever = function(){
return this.repeat();
}
/* 設(shè)置當(dāng)前任務(wù)執(zhí)行結(jié)束后到下一個(gè)任務(wù)開(kāi)始前的等待時(shí)間
* @param time 等待時(shí)長(zhǎng)
*/
FrameAnimation.prototype.wait = function(time){
if(this.taskQueue && this.taskQueue.length > 0){
this.taskQueue[this.taskQueue.length - 1].wait = time;
}
return this;
}
/* 暫停當(dāng)前異步定時(shí)任務(wù)
*
*/
FrameAnimation.prototype.pause = function(){
if(this.state === STATE_START){
this.state = STATE_STOP;
this.timeline.stop();
return this;
}
return this;
}
/* 重新執(zhí)行上一次暫停的異步定時(shí)任務(wù)
*
*/
FrameAnimation.prototype.restart = function(){
if(this.state === STATE_STOP){
this.state = STATE_START;
this.timeline.restart();
return this;
}
return this;
}
/* 釋放資源
*
*/
FrameAnimation.prototype.dispose = function(){
if(this.state !== STATE_INITIAL){
this.state = STATE_INITIAL;
this.taskQueue = null;
this.timeline.stop();
this.timeline = null;
return this;
}
return this;
}
/**
* 添加一個(gè)任務(wù)到任務(wù)隊(duì)列
* @param taskFn 任務(wù)方法
* @param type 任務(wù)類(lèi)型
* @private
*/
FrameAnimation.prototype._add = function(taskFn,type){
this.taskQueue.push({
taskFn:taskFn,
type:type
});
return this;
}
/**
* 執(zhí)行任務(wù)
* @private
*/
FrameAnimation.prototype._runTask = function(){
if(!this.taskQueue || this.state !== STATE_START){
return;
}
//任務(wù)執(zhí)行完畢
if(this.index === this.taskQueue.length){
this.dispose();
return;
}
//獲得任務(wù)鏈上的當(dāng)前任務(wù)
var task = this.taskQueue[this.index];
if(task.type === TASK_SYNC){
this._syncTask(task);
}else{
this._asyncTask(task);
}
}
/**
* 同步任務(wù)
* @param task 執(zhí)行的任務(wù)對(duì)象
* @private
*/
FrameAnimation.prototype._syncTask = function(task){
var me = this;
var next = function(){
//切換到下一個(gè)任務(wù)
me._next(task);
}
var taskFn = task.taskFn;
taskFn(next);
}
/**
* 異步任務(wù)
* @param task 執(zhí)行的任務(wù)對(duì)象
* @private
*/
FrameAnimation.prototype._asyncTask = function(task){
var me = this;
//定義每一幀執(zhí)行的回調(diào)函數(shù)
var enterframe = function(time){
var taskFn = task.taskFn;
var next = function(){
//停止當(dāng)前任務(wù)
me.timeline.stop();
//執(zhí)行下一個(gè)任務(wù)
me._next(task);
};
taskFn(next,time);
}
this.timeline.onenterframe = enterframe;
this.timeline.start(this.interval);
}
/**
* 切換到下一個(gè)任務(wù),支持如果當(dāng)前任務(wù)需要等待,則延時(shí)執(zhí)行
* @private
*/
FrameAnimation.prototype._next = function(task){
this.index++;
var me = this;
task.wait ? setTimeout(function(){
me._runTask();
},task.wait) : this._runTask();
}
module.exports = function(){
return new FrameAnimation();
}
webpack配置
由于animation幀動(dòng)畫(huà)庫(kù)的制作中應(yīng)用了AMD模塊規(guī)范,但由于瀏覽器層面不支持,需要使用webpack進(jìn)行模塊化管理,將animation.js、imageloader.js和timeline.js打包為一個(gè)文件
module.exports = {
entry:{
animation:"./src/animation.js"
},
output:{
path:__dirname + "/build",
filename:"[name].js",
library:"animation",
libraryTarget:"umd",
}
}
下面是一個(gè)代碼實(shí)例,通過(guò)創(chuàng)建的幀動(dòng)畫(huà)庫(kù)實(shí)現(xiàn)博客開(kāi)始的動(dòng)畫(huà)效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="rabbit" ></div>
<script src="../build/animation.js"></script>
<script>var imgUrl = 'rabbit-big.png';
var positions = ['0,-854','-174 -852','-349 -852','-524 -852','-698 -852','-873 -848'];
var ele = document.getElementById('rabbit');
var animation = window.animation;
var repeatAnimation = animation().loadImage([imgUrl]).changePosition(ele,positions,imgUrl).repeatForever();
repeatAnimation.start(80);
</script>
</body>
</html>
更多實(shí)例
除了可以實(shí)現(xiàn)兔子推車(chē)的效果,還可以使用幀動(dòng)畫(huà)實(shí)現(xiàn)兔子勝利和兔子失敗的效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div{position:absolute;width:102px;height:80px;background-repeat:no-repeat;}
</style>
</head>
<body>
<div id="rabbit1" ></div>
<div id="rabbit2" ></div>
<div id="rabbit3" ></div>
<script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/animation.js"></script>
<script>
var baseUrl = 'http://7xpdkf.com1.z0.glb.clouddn.com/runjs/img/';
var images = ['rabbit-big.png','rabbit-lose.png','rabbit-win.png'];
for(var i = 0; i < images.length; i++){
images[i] = baseUrl + images[i];
}
var rightRunningMap = ["0 -854", "-174 -852", "-349 -852", "-524 -852", "-698 -851", "-873 -848"];
var leftRunningMap = ["0 -373", "-175 -376", "-350 -377", "-524 -377", "-699 -377", "-873 -379"];
var rabbitWinMap = ["0 0", "-198 0", "-401 0", "-609 0", "-816 0", "0 -96", "-208 -97", "-415 -97", "-623 -97", "-831 -97", "0 -203", "-207 -203", "-415 -203", "-623 -203", "-831 -203", "0 -307", "-206 -307", "-414 -307", "-623 -307"];
var rabbitLoseMap = ["0 0", "-163 0", "-327 0", "-491 0", "-655 0", "-819 0", "0 -135", "-166 -135", "-333 -135", "-500 -135", "-668 -135", "-835 -135", "0 -262"];
var animation = window.animation;
function repeat(){
var repeatAnimation = animation().loadImage(images).changePosition(rabbit1, rightRunningMap, images[0]).repeatForever();
repeatAnimation.start(80);
}
function win() {
var winAnimation = animation().loadImage(images).changePosition(rabbit2, rabbitWinMap, images[2]).repeatForever();
winAnimation.start(200);
}
function lose() {
var loseAnimation = animation().loadImage(images).changePosition(rabbit3, rabbitLoseMap, images[1]).repeatForever();
loseAnimation.start(200);
}
repeat();
win();
lose();
</script>
</body>
</html>
以上這篇javascript幀動(dòng)畫(huà)(實(shí)例講解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- nodejs實(shí)現(xiàn)截取上傳視頻中一幀作為預(yù)覽圖片
- 復(fù)雜的javascript窗口分幀解析
- 詳解關(guān)于JSON.parse()和JSON.stringify()的性能小測(cè)試
- JS使用JSON.parse(),JSON.stringify()實(shí)現(xiàn)對(duì)對(duì)象的深拷貝功能分析
- 使用JS和canvas實(shí)現(xiàn)gif動(dòng)圖的停止和播放代碼
- 關(guān)于JSON.parse(),JSON.stringify(),jQuery.parseJSON()的用法
- js獲取 gif 的幀數(shù)的代碼實(shí)例
相關(guān)文章
關(guān)于javascript DOM事件模型的兩件事
DOM事件模型的兩件事:事件捕捉(Event Capture)的實(shí)現(xiàn)問(wèn)題以及IE的高級(jí)事件處理模型的問(wèn)題。2010-07-07
利用Js的console對(duì)象,在控制臺(tái)打印調(diào)式信息測(cè)試Js的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇利用Js的console對(duì)象,在控制臺(tái)打印調(diào)式信息測(cè)試Js的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
論壇特效代碼收集(落伍轉(zhuǎn)發(fā)-不錯(cuò))
論壇特效代碼收集(落伍轉(zhuǎn)發(fā)-不錯(cuò))...2006-12-12
可能被忽略的一些JavaScript數(shù)組方法細(xì)節(jié)
這篇文章主要給大家介紹了一些可能被忽略的JavaScript數(shù)組方法細(xì)節(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
js canvas實(shí)現(xiàn)擦除效果示例代碼
擦除效果在我們?nèi)粘i_(kāi)發(fā)中也是時(shí)有見(jiàn)到的,通過(guò)擦除效果大大加強(qiáng)了與用戶(hù)的交互性,所以下面這篇文章主要給大家介紹了利用js和canvas實(shí)現(xiàn)擦除效果的相關(guān)資料,文中給出了詳細(xì)的介紹和示例代碼,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-04-04
JavaScript基于自定義函數(shù)判斷變量類(lèi)型的實(shí)現(xiàn)方法
這篇文章主要介紹了JavaScript基于自定義函數(shù)判斷變量類(lèi)型的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了javascript判斷變量類(lèi)型的自定義函數(shù)定義與使用方法,并針對(duì)不同瀏覽器給出了相關(guān)的分析與說(shuō)明,需要的朋友可以參考下2016-11-11

