资讯专栏INFORMATION COLUMN

PocketLibs(1)—— 动画 tween.js

ShowerSun / 1462人阅读

摘要:绘制变换曲线起飞以上函数就是我们基于内置的实现的自定义变换。例如飞行动画结束后,将飞机复位。

如何运行的?
new Vue({
    el:"#app-1",
    data:{
        position:{
            distance:10,
            height:30,
        }
    },
    methods:{
        flying:function(){
            var targetPos = {distance:300,height:120}
            var tween = new TWEEN.Tween(this.position)
            
            function animate(time){
                var id = requestAnimationFrame(animate);
                var isFlying = TWEEN.update(time);
                if(!isFlying) cancelAnimationFrame(id);
            }

            tween.to(targetPos, 2000)
            tween.start()
            //内部有个this._startTime,如果传的time-this._startTime大于2000
            //那么这个update只会执行一次
            // animate(3000)
            animate()
        },
    }
})
#app-1 p{
    font-size: 2em;
    position: absolute;
    color:#fff;
}

TWEEN作用就是对一个对象持续的进行线性式的改动,比如对象原始状态为{distance:10,height:30},最终状态为{distance:300,height:120},我们设置个时间,TWEEN就在该时间内把对象原始的状态渐进的变换成最终状态。
在初始化实例new TWEEN.Tween(original-obj)时设置原始数据对象,在实例方法to(final-obj,time)中设置最终数据对象及时间。通过实例方法start()启动一个TWEEN。然后通过全局方法TWEEN.update(time)去改动对应时间的原始数据对象,每一刻的时间对应着一份修改的数据,大概像下面这样:

调用update() 调用后数据的改变
TWEEN.update(20) {distance:15,height:31}
TWEEN.update(40) {distance:25,height:33}
TWEEN.update(70) {distance:45,height:37}

如修改下代码:

flying:function(){
    var targetPos = {distance:300,height:120}
    var tween = new TWEEN.Tween(this.position)
    
    // function animate(time){
    //     var id = requestAnimationFrame(animate);
    //     var isFlying = TWEEN.update(time);
    //     if(!isFlying) cancelAnimationFrame(id);
    // }
    
    tween.to(targetPos, 2000)
    tween.start()
    TWEEN.update(1500);
    //内部有个this._startTime,如果传的time-this._startTime大于2000
    //那么这个update只会执行一次
    // animate(3000)
    // animate()
},

点起飞,只会转换到1500毫秒时的状态。

结合requestAnimationFrame(fn)

如果我们不传参数,那么它会根据start()的时间自动计算好当前的时间再修改该时间数据。因为最后数据是要渲染成动画形式,我们不可能一行行去调用update()

因此要结合一个神器requestAnimationFrame(fn),这个函数就是在浏览器每一帧重绘一次屏幕,非常的稳定。在这里如果是60fps的情况下就是16ms(1000ms/60)调用一次animate(),然后我们在这个animate()重绘循环中不停的调用update(),它就可以线性的修改数据,渲染之后就形成动画。

TWEEN也可以链式调用函数。开始的代码可以修改成:

flying:function(){
    var targetPos = {distance:300,height:120}
    var tween = new TWEEN.Tween(this.position)
                .to(targetPos, 2000)
                .start();
    
    function animate(time){
        var id = requestAnimationFrame(animate);
        var isFlying = TWEEN.update(time);
        if(!isFlying) cancelAnimationFrame(id);
    }
    animate()
},
easing函数

前面默认情况下,数据变换和时间成正比的(Linear.None),我们可以通过传递参数easing()改变这种默认效果,内置了许多变换效果。

给TWEEN加上Bounce.Out效果

var tween = new TWEEN.Tween(this.position)
            .to(targetPos, 2000)
            .easing(TWEEN.Easing.Bounce.Out)
            .start();

自定义easing变换函数

TWEEN里最有特色的功能就是easing()参数可以是一个自定义的函数,这样可以实现变换的定制。

function customerFn(k){
    return fn(k) //用k作基础的自定义运算
}
tween.easing(customerFn);//写好后传给easing()函数就行

这个k是系统调用的,跟你无关,它是一个在[0,1]范围内不停增长的值,速度与时间成正比,函数返回一个基于k进行运算后的值。比如简单的像Linear.None那样的默认变换,就是不运算直接返回k

我们也可使用以有的变换做运算,正如下面这样做。

methods: {
    flying: function () {
        var targetPos = { distance: 300, height: 120 }
        /** 绘制变换曲线 */
        var target = document.getElementById("target");
        target.appendChild(this.createGraph("Noisy Exponential.InOut", noisyEasing) );
        function noisyEasing(k) {
            return 0.3 * Math.random() + 0.7 * TWEEN.Easing.Bounce.Out(k);
        }
        var tween = new TWEEN.Tween(this.position)
            .to(targetPos, 2000)
            .easing(noisyEasing)
            .start();
        function animate(time) {
            var id = requestAnimationFrame(animate);
            var isFlying = TWEEN.update(time);
            if (!isFlying) cancelAnimationFrame(id);
        }
        animate()
    },
    createGraph:function( t, f, c ) {
        var div = document.createElement( "div" );
        div.style.display = "inline-block";
        div.style.width = "200px";
        div.style.height = "120px";
    
        var canvas = document.createElement( "canvas" );
        canvas.width = 180;
        canvas.height = 100;
    
        var context = canvas.getContext( "2d" );
        context.fillStyle = "rgb(250,250,250)";
        context.fillRect( 0, 0, 180, 100 );
    
        context.lineWidth = 0.5;
        context.strokeStyle = "rgb(230,230,230)";
    
        context.beginPath();
        context.moveTo( 0, 20 );
        context.lineTo( 180, 20 );
        context.moveTo( 0, 80 );
        context.lineTo( 180, 80 );
        context.closePath();
        context.stroke();
    
        context.lineWidth = 2;
        context.strokeStyle = "rgb(255,127,127)";
    
        var position = { x: 5, y: 80 };
        var position_old = { x: 5, y: 80 };
    
        new TWEEN.Tween( position ).to( { x: 175 }, 2000 ).easing( TWEEN.Easing.Linear.None ).start();
        new TWEEN.Tween( position ).to( { y: 20 }, 2000 ).easing( f ).onUpdate( function () {
    
            context.beginPath();
            context.moveTo( position_old.x, position_old.y );
            context.lineTo( position.x, position.y );
            context.closePath();
            context.stroke();
    
            position_old.x = position.x;
            position_old.y = position.y;
    
        }).start();
    
        div.appendChild( document.createTextNode( t ) );
        div.appendChild( document.createElement( "br" ) );
        div.appendChild( canvas );
    
        return div;
    }
}  

    

以上noisyEasing(k)函数就是我们基于内置的Bounce.Out实现的自定义变换。createGraph()函数是官方例子扣下来的,就是在页面绘制下自定义变换的曲线。

这是内置的Bounce.Out变换,其实就是做了一丝丝抖动效果。

回调方法

TWEEN的四种状态启动、停止、更新和完成,每种下面都可以绑定一个回调函数,onStart(fn)onStop(fn)onUpdate(fn)onComplete(fn)。例如飞行动画结束后,将飞机复位。

new Vue({
    el: "#app-2",
    data: {
        position: {
            distance: 10,
            height: 30,
        }
    },
    methods: {
        flying: function () {
            var _this = this
            var targetPos = { distance: 300, height: 120 }
            var tween = new TWEEN.Tween(this.position)
            tween.to(targetPos, 5000)
                .easing(TWEEN.Easing.Circular.InOut)
                .onComplete(function () {
                    _this.position.distance = 10
                    _this.position.height = 30
                })

            function animate(time) {
                var id = requestAnimationFrame(animate);
                var isFlying = TWEEN.update(time);
                if (!isFlying) cancelAnimationFrame(id);
            }

            tween.start()
            animate()
        }
    }
})

由于闭包的关系,Vue里的this不能用钩子函数里,因此定义了一个中间变量_this
其实update()是最常用的钩子,一般用来在每次修改后对页面元素做数据绑定,但这里有Vue就不需要它了。

循环执行(repeat)与停止动画(stop)

调用实例方法repeat(frequency)设置动画循环次数,若参数为Infinity,则循环无限次。

new Vue({
    el: "#app-3",
    data: {
        rotation: {
            x: 0, y: 0, z: 0
        }
    },
    methods: {
        rotate: function () {
            var tween_x= new TWEEN.Tween(this.rotation)
                .to({ x: 360 })
            var tween_y = new TWEEN.Tween(this.rotation)
                .to({ y: 360 }).repeat(3)
            var tween_z = new TWEEN.Tween(this.rotation)
                .to({ z: 360 }).repeat(Infinity)
            function animate(time) {
                var id = requestAnimationFrame(animate);
                var isFlying = TWEEN.update(time);
                if (!isFlying) cancelAnimationFrame(id);
            }
            tween_x.start()
            tween_y.start()
            tween_z.start()
            animate()
        }
    }
})

转一圈 转三圈 无限转

可以使用tween.stop()停止动画,修改上段JS代码:

methods: {
    rotate: function () {
        var tween_x= new TWEEN.Tween(this.rotation)
            .to({ x: 360 })
        var tween_y = new TWEEN.Tween(this.rotation)
            .to({ y: 360 }).repeat(3).onComplete(function(){
                //当第二个tween动画完成时,停止第三个tween运行
                tween_z.stop()
            })
        var tween_z = new TWEEN.Tween(this.rotation)
            .to({ z: 360 }).repeat(Infinity)
        function animate(time) {
            var id = requestAnimationFrame(animate);
            var isFlying = TWEEN.update(time);
            if (!isFlying) cancelAnimationFrame(id);
        }
        tween_x.start()
        tween_y.start()
        tween_z.start()
        animate()
    }
}

当第二动画的三圈转完时,停止第三个动画效果。

调用链条与链条循环

使用tween_a.chain(tween_b)可以按顺序的(先a后b)链式调用多个tween实例,如果接着调用tween_b.chain(tween_a)调用将进入无限循环中(执行a->b->a->b),如下:

new Vue({
    el: "#app-4",
    data: {
        position: { x: 20, y: 0 }
    },
    methods: {
        move: function () {
            console.log("aaa");
            var tween_a = new TWEEN.Tween(this.position)
                .to({ x: 280, y: 0 }, 3000)
            var tween_b = new TWEEN.Tween(this.position)
                .to({ x: 280, y: 120 }, 3000)
            var tween_c = new TWEEN.Tween(this.position)
                .to({ x: 20, y: 120 }, 3000)
            var tween_d = new TWEEN.Tween(this.position)
                .to({ x: 20, y: 0 }, 3000)
            function animate(time) {
                var id = requestAnimationFrame(animate);
                var isFlying = TWEEN.update(time);
                if (!isFlying) cancelAnimationFrame(id);
            }
           
            tween_a.chain(tween_b)
            tween_b.chain(tween_c)
            tween_c.chain(tween_d)
            tween_d.chain(tween_a)
            tween_a.start()
            animate()
        }
    }
})


YOYO

如果TWEEN有循环(调用repeat()),并调用tween.yoyou(true),那么在执行下一次动画之前,动画会弹到起始位置。

el: "#app-5",
data: {
    position: { x: 20 }
},
methods: {
    move: function () {
        var tween = new TWEEN.Tween(this.position)
            .to({ x: 280 }, 1000).repeat(1).yoyo(true)
        function animate(time) {
            var id = requestAnimationFrame(animate);
            var isFlying = TWEEN.update(time);
            if (!isFlying) cancelAnimationFrame(id);
        }
        tween.start()
        animate()
    }
}


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93579.html

相关文章

  • PocketLibs(3)—— 进度条 NProgress

    摘要:可以指定一个具体值,而非增量,在之间。这是因为,使进度增加超过时,会变成,之后又从重新开始。所以,当为时,我们停止调用。 依赖jQuery。 import nprogress from nprogress import nprogress/nprogress.css $(#btn-loading).on(click, function () { nprogress.start...

    crossoverJie 评论0 收藏0
  • JS动画缓动—tween.js

    摘要:动画运动算法线性匀速运动效果二次方的缓动三次方的缓动四次方的缓动五次方的缓动正弦曲线的缓动指数曲线的缓动圆形曲线的缓动指数衰减的正弦曲线缓动超过范围的三次方缓动指数衰减的反弹缓动。 requestAnimFrame兼容 window.requestAnimFrame = (function (callback,time) { return window.requestAnima...

    wangxinarhat 评论0 收藏0
  • tween.js缓动(补间动画

    摘要:首先引入一个概念就补间动画做动画时会用到类,利用它可以做很多动画效果,例如缓动弹簧等等。代表的就是最后一帧减去初始值就是变化量,代表最后一帧的结束也是动画的结束。 一、理解tween.js 如果看到上面的已经理解了,可以跳过下面的部分.下面为对Tween.js的解释 下面就介绍如何使用这个Tween了,首先b、c、d三个参数(即初始值,变化量,持续时间)在缓动开始前,是需要先确定好的。...

    chengjianhua 评论0 收藏0
  • (个人笔记)在给在线简历添加js特效过程中遇到的问题及解决方法 二

    摘要:个人笔记在给在线简历添加特效过程中遇到的问题及解决方法二预览点击菜单滚动动画首页目标位置当作终点坐标当前滚动到的距离当做起点是步数分步是每次重复都加的变量既要清除又要毫秒除以帧就是每走一步的时间库缓动动画缓动函数速查表库搜索引入一个网站 (个人笔记)在给在线简历添加js特效过程中遇到的问题及解决方法 二 github预览 点击菜单滚动动画首页 let top = element.of...

    CarlBenjamin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<