当前位置: 首页 > news >正文

可以做区位分析的网站/山东疫情最新情况

可以做区位分析的网站,山东疫情最新情况,做网站报价表,代理网页游戏需要多少钱简介 帧动画就是在连续的关键帧中分解动画动作,也就是在时间轴上的每帧上逐帧绘制不同的内容,使其连续播放成动画。常见的帧动画方式有GIF,CSS3 animation和js。使用GIF和CSS3 animation的不足是不能灵活地控制动画的暂停和播放(比如点击暂停…

简介

帧动画就是在连续的关键帧中分解动画动作,也就是在时间轴上的每帧上逐帧绘制不同的内容,使其连续播放成动画。常见的帧动画方式有GIF,CSS3 animation和js。使用GIF和CSS3 animation的不足是不能灵活地控制动画的暂停和播放(比如点击暂停),也不能灵活地捕捉到动画完成的事件,另外在动画扩展性上js更好。

原理

js实现帧动画的有几种方法。一是用一个img标签去承载图片,定时改变img的src属性,这样显然不好。第二种是把所有动画关键帧绘制在一张图片里,把图片作为元素的background-image,定时改变元素的background-position(雪碧图)。对于只是位移的动画,可以通过js直接改变元素的left或者top值

实践

下面说一下第二种

   var imgUrl = 'xx.png';var positions = ['0 -854', '-174 852', 'x y'...];var ele = document.getElementById('ele');animation(ele, positions, imgUrl);function animation(ele, positions, imgUrl){ele.style.backgroundImage = 'url(' + imgUrl + ')';ele.style.backgroundRepeat = 'no-repeat';var index = 0;function run(){var position = positions[index].split(' ');ele.style.backgroundPosition = position[0] + 'px ' + position[1] + 'px';index++;if(index >= positions.length){index = 0;}setTimeout(run, 80);}run();}

封装

封装一个动画对象我们一般会经过这四步,需求分析,设计编程接口,设计调用方式,最后是代码设计。
首先是需求分析,首先我们要支持图片预加载(图片加载是异步过程,我们要保证动画开始的时候图片已经加载完)。第二个是要支持单组动画控制循环次数(可以不断循环),第三个是可以切换到另一个动画。第四个是动画可以暂停和继续播放,第五个是支持动画完成后的回调函数。
根据以上需求我们设计了这样一个接口

   loadImage(imglist) //预加载图片changePosition(ele, position, imageUrl) //通过改变元素的background-position实现动画enterFrame(callback) //每一帧动画执行的函数,可以自定义每一帧动画的callbackrepeat(times) //动画重复执行的次数,times为空时表示无限次repeatForever() //相当于上面不传参数的情况,这个接口比较友好wait(time)  //每个动画执行之后等待的时间then(callback) //动画执行完成后的回调函数start(interval) //动画开始执行,interval表示动画执行的间隔pause() //动画暂停restart() //动画从上一次暂停处重新执行unmount() //释放资源

接下来我们设计一下调用方式,首先我们希望支持链式调用,如下面的例子

   var animation = require('animation');var demo = animation().loadImage(images).changePosition(ele, position, imgUrl).repeat(2).then(function(){//所有动画执行后的回调})demo.start(80);

最后是代码设计,我们把图片预加载,动画执行,动画结束等看成一个任务队列(js的数组)
任务队列中有两种类型的任务,一种是同步执行完成(从预加载到动画执行),另一种是异步定时执行的。
那么我们需要记录当前任务链的索引,在每个任务执行完毕后,通过调用next方法,执行下一个任务,同时更新任务链索引值

代码编写

我们先定义一个基本框架

   function Animation(){   }Animation.prototype = {loadImage: function(imglist){},//添加一个异步定时任务changePosition: function(ele, positions, imageUrl){},//每一帧动画执行的回调函数enterFrame: function(taskFn){},//在一个任务结束时的回调函数then: function(callback){},start: function(interval){},//添加一个同步任务,该任务是回退到上一个任务中,实现重复上一个任务的效果repeat: function(times){},//无限循环上一次任务repeatForever: function(){},//设置当前任务结束后到下个任务前的等待时间timewait: function(time){},pause: function(){},restart: function(){},unmount: function(){}}

接下来我们开始填充具体代码

   //类似常量的写法,在ES6中使用const关键字var STATE_INITIAL = 0;var STATE_START = 0;var STATE_STOP = 0;//同步任务var TASK_SYNC = 0;var TASK_ASYNC = 1;//CMD写法,引入imageloader函数(后面会详细讲)var loadImage = require('./imageloader');var Timeline = require('./timeline');//简单的函数封装,执行callbackfunction next(callback){//如果callback没传进来,undefined就退出callback && callback();}function Animation(){   this.taskQueue = [];this.index = 0;this.state = STATE_INITIAL;this.timeline = new Timeline();}Animation.prototype = {//类内部使用//添加一个任务到任务队列_add: function(taskFn, type){this.taskQueue.push({taskFn: taskFn,type: type})return this;},_runTask: function(){if(!this.taskQueue || this.state !== STATE_START){return;}if(this.index === this.taskQueue.length){this.unmount();return;}var task = this.taskQueue[index];if(task.type === TASK_SYNC){this._syncTask(task);} else {this._asyncTask(task);}},_syncTask: function(task){var self = this;var next = function(){//切换到下一个任务self._next(task);}var taskFn = task.taskFn;taskFn(next);},_asyncTask: function(task){var self = this;//定义每一帧执行的回调函数var enterFrame = function(time){var taskFn = task.taskFn;var next = function(){//停止当前任务self.timeline.stop();self._next(task);}taskFn(next, time);}this.timeline.onenterframe = enterFrame;this.timeline.start(this.interval);},_next: function(task){this.index++;//如果有wait属性,设置等待时间var self = this;task.wait ? setTimeout(function(){self._runTask();}, task.wait) : this._runTask();},loadImage: function(imglist){var taskFn = function(next){//把next当作callback传入loadImage(imglist.slice(), next);}var type = TASK_SYNC;//把this实例传出去,那么在_add方法中也要return thisreturn this._add(taskFn, type);},//添加一个异步定时任务changePosition: function(ele, positions, imageUrl){var len = positions.length;var taskFn;var type;if(len){var self = this;taskFn = function(next, time){if(imgUrl){ele.style.backgroundImage = 'url(' + imageUrl + ')';}//获得当前背景图片位置索引// | 0 相当于Math.floorvar index = Math.min(time/self.interval | 0, len - 1);var position = positions[index].split(' ');//改变dom对象的背景图片位置ele.style.backgroundPosition = position[0] + 'px ' + position[1] + 'px';if(index === len - 1){next();}}type = TASK_ASYNC;} else {taskFn = next;type = TASK_SYNC;}return this._add(taskFn, type);},//每一帧动画执行的回调函数enterFrame: function(taskFn){return this._add(taskFn, TASK_ASYNC);},//在一个任务结束时的回调函数then: function(callback){var taskFn = function(next){callback();next();}var type = TASK_SYNC;return this._add(taskFn, type);},start: function(interval){if(this.state === STATE_START){return this;}if(!this.taskQueue.length){return this;}this.state = STATE_START;this._runTask();return this;},//添加一个同步任务,该任务是回退到上一个任务中,实现重复上一个任务的效果repeat: function(times){var self = this;var taskFn = function(){if(typeof times === 'undefined'){//无限回退到上一个任务self.index--;self._runTask();return;}if(times){times--;self.index--;self._runTask();      } else {//达到重复次数var task = self.taskQueue[self.index];self._next(task);}}var type = TASK_SYNC;return this._add(taskFn, type);},//无限循环上一次任务repeatForever: function(){return this.repeat();},//设置当前任务结束后到下个任务前的等待时间wait: function(){if(this.taskQueue && this.taskQueue.length > 0){this.taskQueue[this.taskQueue.length - 1].wait = time;}return this;},pause: function(){if(this.state === STATE_START){this.state = STATE_STOP;this.timeline.stop();return this;}return this;},restart: function(){if(this.state === STATE_STOP){this.state = STATE_START;this.timeline.restart();return this;}return this;},unmount: function(){if(this.state !== STATE_INITIAL){this.state = STATE_INITIAL;this.taskQueue = null;this.timeline.stop();this.timeline = null;return this;}}}module.exports = function(){//类似工厂模式return new Animation();}

我们在imgeloader.js中定义一个预加载模块

   //images为加载图片的数组或者对象//callback 全部图片加载完毕后的回调函数//timeout 加载超时的时长function loadImage(images, callback, timeout){//图片路径记数器var count = 0;//全部图片加载成功的tagvar success = true;var timeoutId = 0;var isTimeout = false;for(var key in images){//过滤掉原型上的属性if(!images.hasOwnProperty(key)){continue;}var item = images[key];//如果item是一个string的时候,当作src处理if(typeof item === 'string'){//连等表示前两个等于第三个item = image[key] = {src: item}}if(!item || !item.src){continue;}count++;//设置图片的iditem.id = '_img_'+ key + getId();item.img = window[item.id] = new Image();doload(item);}//如果数组的数据验证失败,count为0,直接调用回调//这里是对count任务的同步验证,count还没开始--if(!count){callback(false);} else if(timeout){timeoutId = setTimeout(onTimeout, timeout);}function doload(item){item.status = 'loading';var img = item.img;//加载成功img.onload = function(){success = success && true;item.status = 'loaded';done();}img.onerror = function(){item.status = 'error';success = false;done();}//发起一个http/https请求img.src = item.src;//图片加载完成function done(){img.onload = img.onerror = null;try {delete window[item.id];} catch(e){}//如果count为0的时候//所有图片加载且没有超时情况,清除计时器且执行回调if(!--count && !isTimeout){clearTimeout(timeoutId);callback(success);}}//超时处理函数function onTimeout(timeout){isTimeout = true;callback(false);} }}//_id变量不会污染全局,只是在这个模块闭包中var _id = 0;function getId(){return ++_id;}module.exports = loadImage;

然后使用一个模块timeline.js来取代定时器,因为在浏览器运行环境中定时器是不准的

   //setTimeout的定时器值推荐最小使用16.7ms的原因(16.7 = 1000 / 60, 即每秒60帧)var DEFAULT_INTERVAL = 1000 / 60;var STATE_INITIAL = 0;var STATE_START = 1;var STATE_STOP = 2;var requestAnimaitionFrame = (function(){return window.requestAnimationFrame ||//chromewindow.webkitRequestAnimationFrame ||//firefoxwindow.mozRequestAnimationFrame ||//operawindow.oRequestAnimationFrame || function(callback){return window.setTimeout(callback, callback.interval || DEFAULT_INTERVAL);}})()var cancelAnimationFrame = (function(){//类似上面的兼容return window.cancelAnimationFrame ...})()//时间轴类function TimeLine(){this.animationHandler = 0;this.state = STATE_INITIAL;}TimeLine.prototype = {//时间轴上每一次回调执行的函数//time是从动画开始到当前执行的时间onenterframe: function(time){//在主对象中定义},//interval 每一次回调的间隔时间start: function(interval){if(this.state === STATE_START){return ;}this.state = STATE_START;this.interval = interval || DEFAULT_INTERVAL;//+new Date相当于new Date().getTime()startTimeline(this, +new Date());},stop: function(){if(this.state !== STATE_START){return;}this.state = STATE_STOP;//记录动画从开始到现在经历的时间if(this.startTime){this.dur = +new Date() - this.startTime;}cancelAnimationFrame(this.animationHandler);},restart: funtion(){if(this.state === STATE_START){return;}if(!this.dur || this.interval){return;}this.state = STATE_START;//无缝连接动画startTimeline(this, +new Date() - this.dur);}}//时间轴动画启动函数function startTimeline(timeline, startTime){timeline.startTime = startTime;nextTick.interval = timeline.interval;//记录上一次回调的时间戳,tick的意思是时钟的滴答var lastTick = +new Date();nextTick();//每一帧执行的函数function nextTick(){var now = +new Date();//每17毫秒刷新一次,更精确的定时器timeline.animationHandler = requestAnimationFrame(nextTick);//如果当前时间与上一次回调的时间戳大于设置的时间间隔,表示这次可以执行回调函数if(now - lastTick >= timeline.interval){timeline.onenterframe(now - startTime);lastTick = now;}}}module.exports = Timeline;
http://www.jmfq.cn/news/5015359.html

相关文章:

  • 个人开店的电商平台/seo优化代理
  • 建站之星官网 discuz/优化深圳seo
  • 泉州网站页面设计公司/成都网站建设系统
  • 140平米装修多少钱/seo营销策划
  • 威海建设局网站首页/怎样优化网站
  • 云主机 做网站/新手学seo
  • 网站制作的基本步骤/网络推广公司简介模板
  • 门户网站 cms/seo能从搜索引擎中获得更多的
  • php网站开发缓存的运用/seo综合查询工具下载
  • 先做网站还是服务器/建站之星官网
  • 淘宝网站开发源码/南京seo域名
  • 禁止粘贴的网站/品牌型网站制作价格
  • 建设招聘网站需要哪些资质/重庆seo全网营销
  • wordpress 内容 管理员查看/郑州seo关键词优化公司
  • 自己做的网站出现500错误怎么解决/今日热点新闻
  • 电子商务网站特点/长沙网站设计拓谋网络
  • 网站首页做301/怎么在百度发帖
  • 西宁网站建设平台公司/郑州网站建设七彩科技
  • 网站做好了怎么做后台管理/做网站推广
  • ui设计网站/网站怎么优化自己免费
  • 如何在凡科上做网站/百度竞价推广方案范文
  • 网站做的一样算不算侵权/网站平台有哪些
  • 对php网站开发技术课程总结/谷歌关键词搜索排名
  • 如何做网站的seo/长沙百度网站推广
  • 浏览器怎么连接网站的/最新seo课程
  • 自己建个网站做优化/哈尔滨seo关键词
  • 网络优化工程师前景如何/seo教程视频
  • 南宁网站开发价格/朋友圈广告推广平台
  • 淘宝店铺装网站导航怎么做/app开发公司推荐
  • 制作一个网站并上传访问/百度小说排行榜2021