Heim >Web-Frontend >js-Tutorial >Beispiele zur Erläuterung der Javascript-Frame-Animation

Beispiele zur Erläuterung der Javascript-Frame-Animation

巴扎黑
巴扎黑Original
2017-09-04 10:12:291370Durchsuche

Der folgende Editor zeigt Ihnen eine Javascript-Frame-Animation (Erklärung mit Beispielen). Der Herausgeber findet es ziemlich gut, deshalb werde ich es jetzt mit Ihnen teilen und es allen als Referenz geben. Folgen wir dem Editor und werfen wir einen Blick darauf.

Wie bereits erwähnt

Frame-Animation besteht darin, Animationsaktionen in „kontinuierliche Schlüsselbilder“ zu zerlegen. Das heißt, es handelt sich um eine Animation, die Bild für Bild auf jedem Bild der Zeitleiste unterschiedliche Inhalte zeichnet und diese kontinuierlich abspielt. Da die Frame-Animation Bild für Bild gezeichnet wird, bietet sie eine große Flexibilität und kann nahezu jeden Inhalt ausdrücken, den Sie ausdrücken möchten. In diesem Artikel wird die Javascript-Frame-Animation ausführlich vorgestellt

Übersicht

[Klassifizierung]

Es gibt drei gängige Methoden von Rahmenanimation, einschließlich GIF, CSS3-Animation und Javascript

Git- und CSS3-Animation können die Pause und Wiedergabe von Animationen nicht flexibel steuern und Rahmenanimationen nicht flexibel erweitern. Darüber hinaus können GIF-Bilder den Abschluss der Animation nicht erfassen. Verwenden Sie daher im Allgemeinen Javascript, um Frame-Animationen zu implementieren

[Prinzip]

Es gibt zwei Möglichkeiten, Frame-Animationen mit js zu implementieren

1 Wenn Wenn Sie mehrere Frame-Animationsbilder haben, können Sie ein Bild-Tag verwenden, um das Bild zu übertragen, und das src-Attribut des Bildes regelmäßig ändern (nicht empfohlen)

2. Zeichnen Sie alle Animationsschlüsselbilder in einem Bild. und setzen Sie das Bild als Hintergrundbild des Elements und ändern Sie regelmäßig das Hintergrundpositionsattribut des Elements (empfohlen).

Da die erste Methode mehrere HTTP-Anfragen erfordert, wird im Allgemeinen empfohlen, die zweite Methode zu verwenden

[Beispiel]

Das Folgende ist ein Beispiel für die Verwendung einer Rahmenanimation


<p id="rabbit" ></p> 
<button id="btn">暂停运动</button> 
<script>
var url = &#39;rabbit-big.png&#39;;
var positions = [&#39;0,-854&#39;,&#39;-174 -852&#39;,&#39;-349 -852&#39;,&#39;-524 -852&#39;,&#39;-698 -852&#39;,&#39;-873 -848&#39;];
var ele = document.getElementById(&#39;rabbit&#39;);
var oTimer = null;
btn.onclick = function(){
 if(btn.innerHTML == &#39;开始运动&#39;){
  frameAnimation(ele,positions,url);
  btn.innerHTML = &#39;暂停运动&#39;;
 }else{
  clearTimeout(oTimer);
  btn.innerHTML = &#39;开始运动&#39;;
 } 
}
frameAnimation(ele,positions,url);
function frameAnimation(ele,positions,url){
 ele.style.backgroundImage = &#39;url(&#39; + url + &#39;)&#39;;
 ele.style.backgroundRepeat = &#39;no-repeat&#39;; 
 var index = 0;
 function run(){
  var pos = positions[index].split(&#39; &#39;);
  ele.style.backgroundPosition = pos[0] + &#39;px &#39; + pos[1] + &#39;px&#39;;
  index++;
  if(index >= positions.length){
   index = 0;
  }
  oTimer = setTimeout(run,80);
 }
 run();
} 
</script>

Allgemeiner Rahmen Animation

Lassen Sie uns eine allgemeine Frame-Animationsbibliothek entwerfen

[Anforderungsanalyse]

 1. Unterstützen Sie das Vorladen von Bildern

 2. Unterstützen Sie zwei Arten der Animationswiedergabemethode und Anpassung jedes Animationsframes

3. Unterstützt die Anzahl der Animationsregelkreise für einzelne Gruppen (kann unbegrenzte Zeiten unterstützen)

4. Unterstützt die Fertigstellung eines Satzes von Animationen und der nächste Satz von Animationen

5. Unterstützt die Wartezeit nach Abschluss jeder Animation

6. Unterstützt das Anhalten und Fortsetzen der Wiedergabe der Animation

7. Unterstützt die Ausführung der Callback-Funktion nach der Animation ist abgeschlossen

【Programmierschnittstelle】

1. LoadImage(imglist)//Bild vorladen

2. ChangePosition(ele, positions, imageUrl)//Animation durch Ändern erreichen die Hintergrundposition des Elements

3. changeSrc(ele,imglist)//Durch Ändern der Quelle des Bildelements

4. Die von jedem ausgeführte Funktion Frame der Animation entspricht dem benutzerdefinierten Callback für jeden Frame der Animation

5. Die Häufigkeit, mit der die Animation wiederholt wird, bedeutet unbegrenzte Male >

6. repeatForever()//Wiederholen Sie die Animation das letzte Mal unendlich, äquivalent zu repeat()

7. Wartezeit nach Abschluss jeder Animationsausführung

8. then(callback)//Callback nach Abschluss der Animationsausführung

9. Die Animation beginnt mit der Ausführung, das Intervall stellt das Intervall zwischen den Animationsausführungen dar

10. pause()//Die Animation pausiert

11. restart( )//Die Animation wird ab dem vorherigen Pausenpunkt erneut ausgeführt

12.Entsorgen()//Ressourcen freigeben

[Aufrufmethode]

Unterstützt Kettenaufrufe unter Verwendung von Verben. Beschreiben Sie die Schnittstelle auf eine Art und Weise

[Codedesign]

1 Behandeln Sie eine Reihe von Operationen wie Bildvorladen -> Animationsausführung -> Animationsende als Aufgabenkette. Die Aufgabenkette umfasst zwei Arten von Aufgaben: synchrone Ausführung und asynchrone geplante Ausführung

2. Notieren Sie den Index der aktuellen Aufgabenkette

3. Nachdem jede Aufgabe ausgeführt wurde, wird die nächste Aufgabe ausgeführt indem Sie die nächste Methode aufrufen und den Indexwert der Aufgabenkette aktualisieren

[Schnittstellendefinition]


&#39;use strict&#39;;
/* 帧动画库类
 * @constructor
 */
function FrameAnimation(){}

/* 添加一个同步任务,去预加载图片
 * @param imglist 图片数组
 */
FrameAnimation.prototype.loadImage = function(imglist){}

/* 添加一个异步定时任务,通过定时改变图片背景位置,实现帧动画
 * @param ele dom对象
 * @param positions 背景位置数组
 * @param imageUrl 图片URL地址
 */
FrameAnimation.prototype.changePosition = function(ele,positions,imageUrl){}

/* 添加一个异步定时任务,通过定时改变image标签的src属性,实现帧动画
 * @param ele dom对象
 * @param imglist 图片数组
 */
FrameAnimation.prototype.changeSrc = function(ele,imglist){}

/* 添加一个异步定时任务,自定义动画每帧执行的任务函数
 * @param tastFn 自定义每帧执行的任务函数
 */
FrameAnimation.prototype.enterFrame = function(taskFn){}

/* 添加一个同步任务,在上一个任务完成后执行回调函数
 * @param callback 回调函数
 */
FrameAnimation.prototype.then = function(callback){}

/* 开始执行任务,异步定时任务执行的间隔
 * @param interval
 */
FrameAnimation.prototype.start = function(interval){}

/* 添加一个同步任务,回退到上一个任务,实现重复上一个任务的效果,可以定义重复的次数
 * @param times 重复次数
 */
FrameAnimation.prototype.repeat = function(times){}

/* 添加一个同步任务,相当于repeat(),无限循环上一次任务
 * 
 */
FrameAnimation.prototype.repeatForever = function(){}

/* 设置当前任务执行结束后到下一个任务开始前的等待时间
 * @param time 等待时长
 */
FrameAnimation.prototype.wait = function(time){}

/* 暂停当前异步定时任务
 * 
 */
FrameAnimation.prototype.pause = function(){}

/* 重新执行上一次暂停的异步定时任务
 * 
 */
FrameAnimation.prototype.restart = function(){}

/* 释放资源
 * 
 */
FrameAnimation.prototype.dispose = function(){}

Vorladen von Bildern

Das Vorladen von Bildern ist eine relativ unabhängige Funktion, die in ein Modul imageloader.js gekapselt werden kann


&#39;use strict&#39;;
/**
 * 预加载图片函数
 * @param  images  加载图片的数组或者对象
 * @param  callback 全部图片加载完毕后调用的回调函数
 * @param  timeout 加载超时的时长
 */
function loadImage(images,callback,timeout){
 //加载完成图片的计数器
 var count = 0;
 //全部图片加载成功的标志位
 var success = true;
 //超时timer的id
 var timeoutId = 0;
 //是否加载超时的标志位
 var isTimeout = false;
 //对图片数组(或对象)进行遍历
 for(var key in images){
  //过滤prototype上的属性
  if(!images.hasOwnProperty(key)){
   continue;
  }
  //获得每个图片元素
  //期望格式是object:{src:xxx}
  var item = images[key];
  if(typeof item === &#39;string&#39;){
   item = images[key] = {
    src:item
   };
  }
  //如果格式不满足期望,则丢弃此条数据,进行下一次遍历
  if(!item || !item.src){
   continue;
  }
  //计数+1
  count++;
  //设置图片元素的id
  item.id = &#39;__img__&#39; + key + getId();
  //设置图片元素的img,它是一个Image对象
  item.img = window[item.id] = new Image();
  doLoad(item);
 }
 //遍历完成如果计数为0,则直接调用callback
 if(!count){
  callback(success);
 }else if(timeout){
  timeoutId = setTimeout(onTimeout,timeout);
 }

 /**
  * 真正进行图片加载的函数
  * @param  item 图片元素对象
  */
 function doLoad(item){
  item.status = &#39;loading&#39;;
  var img = item.img;
  //定义图片加载成功的回调函数
  img.onload = function(){
   success = success && true;
   item.status = &#39;loaded&#39;;
   done();
  }
  //定义图片加载失败的回调函数
  img.onerror = function(){
   success = false;
   item.status = &#39;error&#39;;
   done();
  }
  //发起一个http(s)请求
  img.src = item.src;
  /**
   * 每张图片加载完成的回调函数
   */
  function done(){
   img.onload = img.onerror = null;
   try{
    delete window[item.id];
   }catch(e){

   }
   //每张图片加载完成,计数器减1,当所有图片加载完成,且没有超时的情况,清除超时计时器,且执行回调函数
   if(!--count && !isTimeout){
    clearTimeout(timeoutId);
    callback(success);
   }
  }
 }
 /**
  * 超时函数
  */
 function onTimeout(){
  isTimeout = true;
  callback(false);
 }
}
var __id = 0;
function getId(){
 return ++__id;
}
module.exports = loadImage;

Zeitleiste

Bei der Animationsverarbeitung wird dies durch iterative Verwendung von setTimeout() implementiert, dieses Intervall ist jedoch nicht genau. Als nächstes implementieren Sie eine Zeitleistenklasse timeline.js


&#39;use strict&#39;;

var DEFAULT_INTERVAL = 1000/60;

//初始化状态
var STATE_INITIAL = 0;
//开始状态
var STATE_START = 1;
//停止状态
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);
    } 
})();
/**
 * 时间轴类
 * @constructor
 */
function Timeline(){
 this.animationHandler = 0;
 this.state = STATE_INITIAL;
}
/**
 * 时间轴上每一次回调执行的函数
 * @param  time 从动画开始到当前执行的时间
 */
Timeline.prototype.onenterframe = function(time){

}
/**
 * 动画开始
 * @param interval 每一次回调的间隔时间
 */
Timeline.prototype.start = function(interval){
 if(this.state === STATE_START){
  return;
 }
 this.state = STATE_START;
 this.interval = interval || DEFAULT_INTERVAL;
 startTimeline(this,+new Date());
}

/**
 * 动画停止
 */
Timeline.prototype.stop = function(){
 if(this.state !== STATE_START){
  return;
 }
 this.state = STATE_STOP;
 //如果动画开始过,则记录动画从开始到现在所经历的时间
 if(this.startTime){
  this.dur = +new Date() - this.startTime;
 }
 cancelAnimationFrame(this.animationHandler);
}

/**
 * 重新开始动画
 */
Timeline.prototype.restart = function(){
 if(this.state === STATE_START){
  return;
 }
 if(!this.dur || !this.interval){
  return;
 }
 this.state = STATE_START;
 //无缝连接动画
 startTimeline(this,+new Date()-this.dur);
}

/**
 * 时间轴动画启动函数
 * @param  timeline 时间轴的实例
 * @param  startTime 动画开始时间戳     
 */
function startTimeline(timeline,startTime){
 //记录上一次回调的时间戳
 var lastTick = +new Date();
 timeline.startTime = startTime;
 nextTick.interval = timeline.interval;
 nextTick();
 /**
  * 每一帧执行的函数
  */
 function nextTick(){
  var now = +new Date();
  timeline.animationHandler = requestAnimationFrame(nextTick);
  //如果当前时间与上一次回调的时间戳大于设置的时间间隔,表示这一次可以执行回调函数
  if(now - lastTick >= timeline.interval){
   timeline.onenterframe(now - startTime);
   lastTick = now;
  }
 }
}
module.exports = Timeline;

Animationsklassenimplementierung

Folgendes ist der vollständige Code für die Animationsklasse animation.js


&#39;use strict&#39;;

var loadImage = require(&#39;./imageloader&#39;);
var Timeline = require(&#39;./timeline&#39;);
//初始化状态
var STATE_INITIAL = 0;
//开始状态
var STATE_START = 1;
//停止状态
var STATE_STOP = 2;
//同步任务
var TASK_SYNC = 0;
//异步任务
var TASK_ASYNC = 1;

/**
 * 简单的函数封装,执行callback
 * @param  callback 执行函数
 */
function next(callback){
 callback && callback();
}
/* 帧动画库类
 * @constructor
 */
function FrameAnimation(){
 this.taskQueue = [];
 this.index = 0;
 this.timeline = new Timeline();
 this.state = STATE_INITIAL;
}

/* 添加一个同步任务,去预加载图片
 * @param imglist 图片数组
 */
FrameAnimation.prototype.loadImage = function(imglist){
 var taskFn = function(next){
  loadImage(imglist.slice(),next);
 };
 var type = TASK_SYNC;
 return this._add(taskFn,type);
}

/* 添加一个异步定时任务,通过定时改变图片背景位置,实现帧动画
 * @param ele dom对象
 * @param positions 背景位置数组
 * @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 = &#39;url(&#39; + imageUrl + &#39;)&#39;;
   }
   //获得当前背景图片位置索引
   var index = Math.min(time/me.interval|0,len);
   var position = positions[index-1].split(&#39; &#39;);
   //改变dom对象的背景图片位置
   ele.style.backgroundPosition = position[0] + &#39;px &#39; + position[1] + &#39;px&#39;;
   if(index === len){
    next();
   }
  }
  type = TASK_ASYNC;
 }else{
  taskFn = next;
  type = TASK_SYNC;
 }
 return this._add(taskFn,type);
}

/* 添加一个异步定时任务,通过定时改变image标签的src属性,实现帧动画
 * @param ele dom对象
 * @param imglist 图片数组
 */
FrameAnimation.prototype.changeSrc = function(ele,imglist){
 var len = imglist.length;
 var taskFn;
 var type;
 if(len){
  var me = this;
  taskFn = function(next,time){
   //获得当前背景图片位置索引
   var index = Math.min(time/me.interval|0,len);
   //改变image对象的背景图片位置
   ele.src = imglist[index-1];
   if(index === len){
    next();
   }
  }
  type = TASK_ASYNC;
 }else{
  taskFn = next;
  type = TASK_SYNC;
 }
 return this._add(taskFn,type); 
}

/* 添加一个异步定时任务,自定义动画每帧执行的任务函数
 * @param tastFn 自定义每帧执行的任务函数
 */
FrameAnimation.prototype.enterFrame = function(taskFn){
 return this._add(taskFn,TASK_ASYNC);
}

/* 添加一个同步任务,在上一个任务完成后执行回调函数
 * @param callback 回调函数
 */
FrameAnimation.prototype.then = function(callback){
 var taskFn = function(next){
  callback(this);
  next();
 };
 var type = TASK_SYNC;
 return this._add(taskFn,type);
}

/* 开始执行任务,异步定义任务执行的间隔
 * @param interval
 */
FrameAnimation.prototype.start = function(interval){
 if(this.state === STATE_START){
  return this; 
 }
 //如果任务链中没有任务,则返回
 if(!this.taskQueue.length){
  return this;
 }
 this.state = STATE_START;
 this.interval = interval;
 this._runTask();
 return this;
  
}

/* 添加一个同步任务,回退到上一个任务,实现重复上一个任务的效果,可以定义重复的次数
 * @param times 重复次数
 */
FrameAnimation.prototype.repeat = function(times){
 var me = this;
 var taskFn = function(){
  if(typeof times === &#39;undefined&#39;){
   //无限回退到上一个任务
   me.index--;
   me._runTask();
   return;
  }
  if(times){
   times--;
   //回退
   me.index--;
   me._runTask();
  }else{
   //达到重复次数,跳转到下一个任务
   var task = me.taskQueue[me.index];
   me._next(task);
  }
 }
 var type = TASK_SYNC;
 return this._add(taskFn,type);
}

/* 添加一个同步任务,相当于repeat(),无限循环上一次任务
 * 
 */
FrameAnimation.prototype.repeatForever = function(){
 return this.repeat();
}

/* 设置当前任务执行结束后到下一个任务开始前的等待时间
 * @param time 等待时长
 */
FrameAnimation.prototype.wait = function(time){
 if(this.taskQueue && this.taskQueue.length > 0){
  this.taskQueue[this.taskQueue.length - 1].wait = time;
 }
 return this;
}

/* 暂停当前异步定时任务
 * 
 */
FrameAnimation.prototype.pause = function(){
 if(this.state === STATE_START){
  this.state = STATE_STOP;
  this.timeline.stop();
  return this;
 }
 return this;
}

/* 重新执行上一次暂停的异步定时任务
 * 
 */
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;  
}

/**
 * 添加一个任务到任务队列
 * @param taskFn 任务方法
 * @param type  任务类型
 * @private
 */
FrameAnimation.prototype._add = function(taskFn,type){
 this.taskQueue.push({
  taskFn:taskFn,
  type:type
 });
 return this;
}

/**
 * 执行任务
 * @private
 */
FrameAnimation.prototype._runTask = function(){
 if(!this.taskQueue || this.state !== STATE_START){
  return;
 }
 //任务执行完毕
 if(this.index === this.taskQueue.length){
  this.dispose();
  return;
 }
 //获得任务链上的当前任务
 var task = this.taskQueue[this.index];
 if(task.type === TASK_SYNC){
  this._syncTask(task);
 }else{
  this._asyncTask(task);
 }
}

/**
 * 同步任务
 * @param task 执行的任务对象
 * @private
 */
FrameAnimation.prototype._syncTask = function(task){
 var me = this;
 var next = function(){
  //切换到下一个任务
  me._next(task);
 }
 var taskFn = task.taskFn;
 taskFn(next);
}

/**
 * 异步任务
 * @param task 执行的任务对象
 * @private
 */
FrameAnimation.prototype._asyncTask = function(task){
 var me = this;
 //定义每一帧执行的回调函数
 var enterframe = function(time){
  var taskFn = task.taskFn;
  var next = function(){
   //停止当前任务
   me.timeline.stop();
   //执行下一个任务
   me._next(task);
  };
  taskFn(next,time);
 }
 this.timeline.onenterframe = enterframe;
 this.timeline.start(this.interval);
}

/**
 * 切换到下一个任务,支持如果当前任务需要等待,则延时执行
 * @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-Konfiguration

Fällig Die AMD-Modulspezifikation wird bei der Produktion der Animationsrahmen-Animationsbibliothek verwendet. Da sie jedoch auf Browserebene nicht unterstützt wird, muss Webpack für die modulare Verwaltung verwendet werden, und dies gilt für animation.js, imageloader.js und timeline.js in einer Datei verpackt


module.exports = {
 entry:{
  animation:"./src/animation.js"
 },
 output:{
  path:__dirname + "/build",
  filename:"[name].js",
  library:"animation",
  libraryTarget:"umd",
 }
}
Das Folgende ist ein Codebeispiel, um den Animationseffekt des Blog-Starts durch die erstellte Frame-Animationsbibliothek zu erzielen


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
</head>
<body>
<p id="rabbit" ></p> 
<script src="../build/animation.js"></script> 
<script>var imgUrl = &#39;rabbit-big.png&#39;;
var positions = [&#39;0,-854&#39;,&#39;-174 -852&#39;,&#39;-349 -852&#39;,&#39;-524 -852&#39;,&#39;-698 -852&#39;,&#39;-873 -848&#39;];
var ele = document.getElementById(&#39;rabbit&#39;);
var animation = window.animation;
var repeatAnimation = animation().loadImage([imgUrl]).changePosition(ele,positions,imgUrl).repeatForever();
repeatAnimation.start(80); 
</script>
</body>
</html>

Weitere Beispiele

Zusätzlich zur Erzielung des Effekts eines Hasenkarrens können Sie auch Rahmenanimationen verwenden, um dies zu erreichen Auswirkung von Kaninchensieg und Kaninchenversagen


Das obige ist der detaillierte Inhalt vonBeispiele zur Erläuterung der Javascript-Frame-Animation. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn