为了账号安全,请及时绑定邮箱和手机立即绑定
  • js 实现帧动画原理

    查看全部
    0 采集 收起 来源:认识帧动画

    2021-06-19

  • img src

    div background


    查看全部
    0 采集 收起 来源:认识帧动画

    2019-12-21

  • 和上一个差别不大

    Animation.prototype.changeSrc = function (el,imgList) {

    var me = this;

    var len = imgList.length;

    var taskFn;

    var type;

    if (len) {

    taskFn = function (next, time) {

    var index = Math.min(time / me.interval | 0 , len-1);

    el.src = imageList[index]

    //结束时跳到下一个

    if (index === len - 1) {

    next();

    }

    }

    type = TASK_ASYNC;

    } else {

    //这里的next就是全局方法 next

    taskFn = next;

    type = TASK_SYNC;

    }

    return this._add(taskFn,type)

    }


    查看全部
  • 接下来就是具体用timeline的各个接口了。


    Animation.prototype.changePosition = function (el, positions, imageUrl) {

    var me = this;

    var len = position.length;

    var taskFn;

    var type;

    if (len) {

    taskFn = function (next, time) {

    //next, time 是._asyncTask的时候,传过来的

    //如果传入了图片地址,那么修改el的背景图

    if (imageUrl) {

    el.style.backgoundImage = 'url(' + imageUrl + ')';

    }

    //如果当前已经执行的回调次数 还没到最大值( 设定中的动画执行次数)

    //那么选择当前已经执行的回调次数作为索引(从0开始数,所以可以直接用)

    // |0 既 Math.floor

    var index = Math.min(time / me.interval | 0, len - 1)

    //是这样[ 200 332 , 333 33,]的格式

    var position = positions[index].split(' ')

    el.style.backgoundPosition = position[0]+'px' +' '+ position[1]+'px'

    //动画循环完成了的时候。

    if (index === len - 1) {

    next();

    }

    }

    type = TASK_ASYNC;

    } else {

    //这里的next就是全局方法 next

    taskFn = next;

    type = TASK_SYNC;

    }

    return this._add(taskFn,type)

    }

    function next(callback) {

    callback && callback()

    }

    //异步方法是这样的:


    Animation.prototype._asyncTask = function(task) {

    //设置timeline里面的每帧都调用的回调函数onenterframe。

    var enterFrame = function (time) {

    var me = this;

    var taskFn = task.taskFn;

    //回调函数的设置

    var next = function () {

    //停止当前的

    me.timeline.stop()

    //执行下一个

    me._next()

    }

    taskFn(next,time)

    }

    this.timeline.onenterframe = enterFrame;

    //通过this.prototype.start传进来的参数Interval传给timeline

    this.timeline.start(this.interval)

    }



    查看全部
  • 创建Timeline实例,然后用在异步方法里。

    function Animation() {

    this.timeline = new Timeline();

    //来自用户设定的时间间隔

    this.interval = 0;

    }

    Animation.prototype._asyncTask = function(task) {

    //设置timeline里面的每帧都调用的回调函数onenterframe。

    var enterFrame = function (time) {

    var me = this;

    var taskFn = task.taskFn;

    //回调函数的设置

    var next = function () {

    //停止当前的

    me.timeline.stop()

    //执行下一个

    me._next()

    }

    taskFn(next,time)

    }

    this.timeline.onenterframe = enterFrame;

    //通过this.prototype.start传进来的参数Interval传给timeline

    this.timeline.start(this.interval)

    }



    查看全部
  • 我的笔记代码并不是完全和视频一致的。

    这一小节有趣的一点是做了setInterval的迭代式形式。 nextTick函数,其中有requestAnimationFrame(nextTick)。

    其中不断更新记录上一帧结束的时间lastTick。

    而这样的话,lastTick- startTime /interval 大概就是过了多少帧了。

    代码:

    function startTimeline(timeline, startTime) {

    //设置实例上的数据,储存用

    timeline.startTime = startTime

    //为用requestAnimationFrame加上的callback.interval

    nextTick.interval = timeline.interval


    //记录最后一次回调的时间戳

    var lastTick = +new Date();

    nextTick();

    //+new Date() == new Date().getTime()


    //其实这是一个迭代形式的setInterval

    //每一帧都执行的函数哦

    function nextTick() {

    //判断如果时间到interval设定了的时间了,就执行回调,

    var now = +new Date();

    timeline.animationHandler = requestAnimationFrame(nextTick)

    if (now - lastTick >= timeline.interval) {

    timeline.onenterframe(now - startTime)

    //并且更新最后一次回调的时间

    lastTick = now;

    }

    }

    }


    类: 

    function Timeline(interval) {

    //当前动画的状态。

    this.state = STATE_INITIAL;

    //当前动画进行时间。

    this.startTime = 0;

    //每次回调的时间间隔。

    this.interval = DEFAULT_INTERVAL;

    //setTimeout的ID

    this.animationHandler = 0;

    //动画开始了多久,暂停的时候储存留待再次开始

    this.dur = 0;

    }


    /**

     * 动画停止

     */

    Timeline.prototype.stop = function (interval) {

    if (this.state !== STATE_START) {

    return

    }

    this.state = STATE_STOP;

    //如果动画已经开始了,那么记录一下已经开始多久了。

    if (this.stateTime) {

    this.dur = +new Date() - this.startTime

    }

    cancelAnimationFrame(this.animationHandler)

    }

    /**

    * 重新播放动画

    */

    Timeline.prototype.restart = function (interval) {

    if (this.state !== STATE_START) {

    return

    }

    if (!this.dur || this.interval) {

    return

    }

    this.state = STATE_START

    //从停止那一刻算起,开始动画

    startTimeline(this, +new Date() - this.dur)

    }





    查看全部
    0 采集 收起 来源:timeline的实现

    2019-05-20

  • 这里是定义具体执行异步函数的方法。

    首先是处理window.requestAnimationFrame 和window.cancelAnimationFrame的兼容性问题。这里使用的自执行函数,通过 || 返回经过类型转换后为true的值。如果不支持这个方法,就用setTimeout,如果用setTimeout,默认的时间间隔是1000/60毫秒。


    代码:


    var DEFAULT_INTERVAL = 1000/60

    //requestAnimationFrame每17毫秒会刷新一次。

    var requestAnimationFrame = (function () {

    //浏览器兼容

    return window.requestAnimationFrame ||

    window.webketRequestAnimationFrame ||

    window.mozRequestAnimationFrame ||

    window.oRequestAnimationFrame ||

    //如果不支持,则用setTimeout ,默认为 1000/60 毫秒后

    function (callback) {

    return window.setTimeout(callback(),callback.interval || DEFAULT_INTERVAL)

    }

    })()

    var cancelAnimationFrame = (function () {

    return window.cancelAnimationFrame ||

    window.webketCancelAnimationFrame ||

    window.mozCancelAnimationFrame ||

    window.oCancelAnimationFrame ||

    //如果不支持,则用setTimeout ,默认为 1000/60 毫秒后

    function (id) {

    return window.clearTimeout(id)

    }

    })()

    接下来定义class。


    查看全部
    0 采集 收起 来源:timeline的实现

    2019-05-20

  • 总结:

    这一个小节的内容,是把需要的任务方法添加到任务队列,然后将任务按照不同类型处理。

    这里需要注意的是,每一个任务函数都要有处理callback的能力,这样才能next;

    第一个任务,将任务添加到任务队列,是这样做的。在使用这个库的时候,demo会首先调用Animation.prototype.loadImage()这个方法。而在代码中,并不是直接调用预加载loadImage()方法,而是把这个方法定义为对象中的一个属性,并定义它的类型,然后将完成的对象加入一个列表中,并且返回this。

    这个列表就是this.taskQueue.

    格式: [{taskFn: taskFn,

                type: type}]

    Animation.prototype._add 将任务加入到任务队列中,

    最终this._add返回this。这个this也被Animation.prototype.loadImage()最终返回。

    代码:

    Animation.prototype.loadImage = function (imageList) {

    var taskFn = function (next) {

    //为了保证不影响原对象。使用slice

    loadImage(imageList.slice(),next)

    }

    //设置为同步任务。

    var type = TASK_SYNC;

    //放到任务队列中,同时返回由this._add()所返回的this

    return this._add(taskFn, type);

    }

    Animation.prototype._add = function (taskFn, type) {

    this.taskQueue.push({

    taskFn: taskFn,

    type: type

    })

    return this;

    }


    任务队列taskQueue已经有了。接下来就是执行里面的任务了。

    通过调用Animation.prototype.start(),

    设置了任务执行状态,以及时间间隔之后

    这里调用的是,Animation.prototype._runTasks

    _runTasks中,遍历并没有用for 语句,而是通过迭代。

    代码:

    Animation.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._runTasks();

    return this;

    }

    通过在this上定义一个值为0的index属性,index会在完成任务的时候递增,并且在Index == 任务队列的长度的时候,也就是执行完所有任务的时候,停止执行,释放内存等。

    代码:

    function Animation() {

    //任务队列

    this.taskQueue = [];

    //控制遍历的计数器

    this.index = 0;

    //执行状态

    this.state = STATE_INITIAL

    }


    使用this.taskQueue[this.index]的形式取出当前方法。

    index每次递增都会重新调动任务队列的执行函数_runTasks

    _runTasks里面的逻辑是,

    通过之前this._add的时候,设置的方法状态,来确定是异步还是同步。

    代码:

    Animation.prototype._runTasks = function() {

    //所以呢,它应该是接收一个任务队列,然后按照异步或者同步的方式分别执行。

    //在这里这个任务队列在实例上的taskQueue

    if (!this.taskQueue.length || this.state !== STATE_START ) {

    return;

    }

    //任务执行完毕,释放内存

    if (this.index === this.taskQueue.length) {

    this.dispose()

    return;

    }

    //由于由this.index控制遍历,所以从当前任务开始,this.index默认为第一个

    //这里已经是经过this._add后,对象的形式{taskFn: taskFn, type: type}

    var task = this.taskQueue[this.index]

    //task可能是同步任务或者异步任务,两种任务执行方式不同

    if (task.type === TASK_SYNC) {

    this._syncTask(task)

    } else if (task.type === TASK_ASYNC) {

    this._asyncTask(task)

    }

    }


    这里this.index++,以及_runTasks的再次调用,是在同步或者异步任务执行完毕之后,由负责同步任务或者异步任务的函数来调用的。

    代码:

    /*执行同步任务的方法 */

    Animation.prototype._syncTask = function (task) {

    //保留this,因为会在闭包中调用。

    var m = this

    //执行完毕的回调

    var next = function () {

    me._next()

    }

    //取出要执行的方法

    var taskFn = task.taskFn;

    //执行它,并传入回调。

    taskFn(next)

    }

    /*

    *切换到下一个任务的方法

    */

    Animation.prototype._next = function () {

    this.index++;

    //这里是遍历。

    this._runTasks();

    }


    /**

    * 异步任务执行方法

    */


    Animation.prototype._asyncTask = function(task) {

    //未待续完,使用requestAnimationFrame

    }


    Animation.prototype._runTasks 负责确定极限值的收尾工作,以及确定任务状态并且执行负责同步任务或者负责异步任务的函数。



    所有的数据都由实例来负责。在实例上定义。

    查看全部
  • 总结下,大概是这样的逻辑

    预加载图片流程

    • 由定义在proptotype上的LoadImage方法把数组交给真正去加载图片的组件模块。该数组为[{src:'imageurl'}] 这样的格式。

    • 经由该模块之后,会返回一个数组,其中标志了哪些图片加载成功,哪些没有。为了这样做的话,就需要给图片分别加一个对应ID。

    • 在这个模块中,会将数组中的对象遍历取出来加载。为了这样做,需要先排除错误数据,。不存在可以遍历的对象,该对象不存在src,以及在Prototype上的属性,如果是string的话,做类型转换成Object。

      遍历是用for in 语句。

            for (var key in images) {}

            在真正的加载过程中,首先要新建Image对象。将其绑定到window上。像前面说的,为Image加上ID,同时数据中的对象也要保存这个ID。

            设置onload, onerror的处理后,设置Image对象的src,就会加载了。

    而为了全部加载成功之后之后调用callback回调函数,需要进行计数和确定完成的方法。

    在这里是遍历的时候count ++ ,每次加载完成,无论失败与否,都会 --count ,

    当然失败与否在该对象上进行的标记不同,这里用

    status 为loaded 作为成功。status 为error作为失败

    最后当没有图片可以加载 的时候,就可以加载回调函数了。

    当然还有对加载超时的优化。

    这里我觉得比较特别的是,并没有用var timer 而用了 timoutId 这个变量设置setTimeout

    这里分为两种情况,所以最开始也设置了一个局部变量作为成功或者失败的标志位。var success

    如果加载成功,那么success为true, clearTimeout

    如果所有的遍历和加载都完成了,而且success == false 的情况,当然就是setTimeout了


    查看全部
  • https://github.com/ustbhuangyi/animation/tree/gh-pages/images

    查看全部
  • js实现帧动画

    查看全部
    0 采集 收起 来源:认识帧动画

    2018-05-12

  • 帧动画原理

    查看全部
    0 采集 收起 来源:认识帧动画

    2018-04-21

  • 老师声音好好听(≧∇≦)讲得好好
    查看全部
    0 采集 收起 来源:课程总结

    2018-04-06

  • 链式调用视作任务链
    查看全部
  • 调用方式
    查看全部
  • 编程接口3
    查看全部
  • 编程接口2
    查看全部
  • 编程接口
    查看全部
  • 需求分析
    查看全部
  • 设计过程
    查看全部
首页上一页1234567下一页尾页

举报

0/150
提交
取消
课程须知
1、对前端基础知识已经掌握(html、js、css) 2、对前端面向对象编程有一定了解
老师告诉你能学到什么?
1、什么是帧动画及网页中常见的几种帧动画方式 2、原生 JS 实现帧动画的原理 3、如何设计一个通用的帧动画解决方案 4、了解类似 promise的设计思想(异步任务链式调用) 5、面向对象的开发方式和模块化开发思想 6、如何实现图片预加载模块

微信扫码,参与3人拼团

意见反馈 帮助中心 APP下载
官方微信
友情提示:

您好,此课程属于迁移课程,您已购买该课程,无需重复购买,感谢您对慕课网的支持!