js canvas 动画,前端 入门 动画 canvas,JS前端可视化canvas动画原理及其推导实现

js canvas 动画,前端 入门 动画 canvas,JS前端可视化canvas动画原理及其推导实现

本文主要介绍JS前端可视化画布动画的原理及其推导和实现。有需要的朋友可以借鉴一下,希望能有所帮助。祝你进步很大,早日升职加薪。

目录

前言动画的本质,动画的实现,动画的演绎总结

前言

到目前为止,我们的fabric.js原型已经成型。麻雀虽小五脏俱全,我们不仅可以在画布上自由添加对象,还可以点击选择方框,对其进行一些改动。但是,只有变更操作不够灵活。让物体动起来就好了,所以我们介绍了这一章的主题:动画,以及动画的核心问题。如何保证不同电脑上的动画效果相同?然后说到做到,马上去做。

虽然我写了一系列文章,但是一个人吃每一章都是木头问题,请放心大胆看。

动画的本质

我们来看看画布库中调用动画的一般方式。例如,如果我们想让一个矩形移动,它一般如下使用:

矩形动画(

{top: 50,left: 400,angle: 45 },//要制作动画的属性

{duration: 1000,onchange:canvas . render all . bind(canvas)}//动画执行时间和手动渲染

);

代码很好理解,然后我们来思考动画的本质。为什么能看到动画效果?这个大家应该都知道吧,不就是画布重画了吗?只要进行足够多和快速的重绘,就会根据人类视觉的残余效果形成动画。

没错,这是总的原因,但是我们可以更具体一点,想想为什么要重画画布。并不是因为画布中某个对象的某个值发生了变化,我们才要更新图片来显示它发生了移动。这个对象的状态值的变化是产生动画的根本原因。

例如,一个物体从左=100移动到左=200需要1s。只要我不断修改左边的值,然后不断渲染All all,就能看到物体从左向右移动。这个很好理解,但是又出现了一个新的问题。它应该如何移动?匀速,加速还是减速?或者是另一种方式?其实都是可以的,看你希望这个左怎么变,怎么有规律的变。

动画的实现

既然动画的本质是价值的变化,那么这种价值的变化与哪些因素有关呢?根据刚才的例子,我们可以知道大约有以下四个因素:

初始值:startValue endValue:改变时间endValue值:时长如何变化(匀速、慢动作或弹跳):easing(出现一个熟悉的词)

很明显,动画也是一个通用的东西,所以我们把它写在Util工具类里。代码不多,直接吃就是了:

接口IAnimationOption {

/* *初始值*/

startValue?数字;

/* *最终值*/

endValue?数字;

/* *执行时间*/

持续时间?数字;

/* *减速功能*/

缓和?功能;

/* *动画开始时的回调*/

onStart?功能;

/* *当属性值更改时将进行回调*/

onChange?功能;

/* *属性值更改完成时进行的回调*/

onComplete?功能;

}

类实用工具{

静态动画(选项:IAnimationOption) {

窗户。RequestAnimationFrame((timestamp:number)={//RequestAnimationFrame会有一个默认的参数timestamp,以毫秒为单位,表示回调函数启动的时间。

//初始化一些变量

Start=timestamp || newdate(),//开始时间

Duration=options.duration || 500,//动画时间

完成=开始持续时间,//结束时间

时间,//当前时间

onchange=options . onchange | |(()={ }),//值更改的回调。

Easing=options.easing || ((t,b,c,d)=-c * math . cos((t/d)*(math . pi/2))c b),//点动函数,不管名字如何,都可以简单理解为普通函数,它会返回一个数值。

start value=options . start value | | 0,//初始值

End value=options.end value || 100,//end value

by value=options . by value | | end value-start value;//值变化的范围

函数tick (tick time: number) {//Tick的主要任务是根据当前时间更新值。

time=tick time | | new Date();

让currentTime=time结束?持续时间:时间-开始;//当前执行了多长时间(介于0和0 ~持续时间之间)

onChange(easing(currentTime,startValue,byValue,duration));//根据当前时间和缓动函数计算当前动画值。easing可以理解为一个普通的函数,它会返回一个这样的值:curVal=f(x)=easing(currentTime)

If(时间结束){//动画结束

options . on complete options . on complete();//动画完成的回调

返回;

}

window . requestanimationframe(tick);//循环调用tick,不断更新值,从而形成一个动画。

}

options . onstart options . onstart();//动画开始前回调

打勾(开始);//开始播放动画

});

}

}

相信上面的注意事项应该解释的很清楚很明白了。但是,我们应该关注两点:

一个是为什么要用requestAnimationFrame api来完成动画,这应该也是老生常谈了。由于setInterval和setTimeout不准确,容易造成执行定时不准确、堆叠执行、切换页面时执行不流畅等问题。而且,它们也不是专门为动画设计的(当然,如果你不习惯使用requestAnimationFrame,可以直接改成setTimeout,供你自己理解);requestAnimationFrame是根据帧率进行刷新的,这样我们就不用在跟随帧率的同时做很多无用功,也能更好的知道绘制下一帧的最佳时间,也更流畅。它们之间的一个主要区别是:setInterval 和 setTimeout 是主动告诉浏览器什么时候去绘制;而 requestAnimationFrame 则是浏览器在它觉得可以绘制下一帧的时候通知我们(如果你尝一下,如果你仔细尝一下,它会有那种味道)。

当然,我们不能只是这样称呼它:

//假设你想从左向右移动

让左=100;

函数tick() {

左;//更新值

window . requestanimationframe(tick);

}

tick();

因为每个屏幕的刷新频率不一样,如果按照上面写的话,有的电脑会快一些,有的电脑会慢一些。不仅如此,页面切换到后台时帧率也会降低,从而导致各种问题,这显然不是我们所预期的。

那我们要做什么?

我们应该以时间为维度来播放动画,因为时间对于我们来说是以同样的速度流逝的,所以我们需要在动画开始的时候记录下开始时间start,然后动画播放到哪里都以开始时间为基准。回头看看刚才代码中计算当前动画执行了多长时间的方式:

让currentTime=time结束?持续时间:时间-开始;

它是建立在start基础上的,这一点非常重要。

第二点是关于缓和功能。虽然好像被触动过,但是很多同学还是会对它感到困惑,所以接下来我就具体说一下这方面的内容,比如:这个函数是什么,是怎么推导出来的,最后的结果是什么,和我们平时说的点动函数是不是一个东西等等。

动画的推导

在解释OnChange这个东西(Easing (Current Time,StartValue,By Value,Duration))之前,我们先来看看如何让每一个对象都动画化,也就是在object基类中扩展一下就可以了,看一眼就可以了:

Class FabricObject {//对象基类

_ animate (property,to,options:ianimationoption={ }){//要更改的属性在哪里?

options=Util.clone(选项);

let current value=this . get(property);//获取初始值

如果(!options.from) options.from=当前值;//一般不传递初始值,默认取当前属性值。

Util.animate({

startValue: options.from,

endValue:到,

Easing: options.easing,//决定值如何变化,常用的有点动和弹跳。

持续时间:选项。持续时间,

OnChange: (value)={//value是easing函数的返回值,本质上是value的计算,value=easing()

this.set(属性,值);//重置属性值

options . onchange options . onchange();//值改变后,调用onChange回调会重新渲染画布,数据和视图分离的优势再次体现。

},

onComplete: ()={

this . setcoords();//更新对象本身的一些坐标值等

options . on complete options . on complete();//在动画结束时回调

},

});

}

}

然后再次强调,动画的核心是价值的变化。Util.animate中的缓动函数其实就是计算动画在(0,时长)中间某个时刻的值,仅此而已。先简单说一下缓和功能。一般可以称为点动功能。

对,一开始是函数,会返回一个数值,类似于y=f(x),我们这里是value=easing(时间,begin值,change值,duration)。这个函数有四个参数(当前时间,初始值,变化=结束值-初始值,动画时间),它返回当前时间点对应的值。显然,后三个参数是已知的、固定的,唯一会改变的是当前时间,其取值范围从0到持续时间。

当动画被执行时,当前时间实际上被改变了。根据当前时间,我们可以代入缓动函数得到相应的值。

可能有些同学还不明白这个慢动作函数,其实是因为被上面的公式忽悠了,这个公式是简单的推导后的写法。直接看公式很难理解,单靠公式在脑子里想象动画效果也不太现实,下面简单推导一下这个公式是怎么来的。以最简单的匀速运动为例,看看下图:

以上过程显而易见,不必推导。让我们看另一个更一般的例子。首先,只取一个函数y=x * x(其他也行)顺便简单画出函数图像:

绿色代表起点,即动画开始值,红色代表终点,即动画结束值。x轴是动画时间,Y轴是当前动画值。为了方便统一,我们需要把时间换算成[0,1]的范围,0为起点,1为终点,Y轴表示的值是一样的。

那么我们的起点和终点分别是(0,0)和(1,1)

(注:虽然xy的取值范围是0到1,看起来像个正方形,但它们的单位或含义不一样,不要混淆),起点和终点是固定不变的,中间的曲线怎么画都行,怎么能写成慢动函数呢?

我们先来看看x轴代表什么。x是一个取值范围从0到1的变量。让我们看看点动函数有哪些变量。只是一个currentTime,但是currentTime的取值范围是从[0,duration],所以我们需要将其映射到[0,1]。其实放currentTime/duration,然后用currentTime/duration代替X也是可以的;

你呢?Y根据X计算出的值代表当前时间点对应的值,也就是我们点动函数的值。它的取值范围在[startValue,startValue byValue]之间,所以我们也需要使它为[0,1]。所以value的值就变成了这样(value-startValue)/byValue,所以现在y值有了,我们可以直接代入初始公式y=x * x,就像这样:

y=x * x

替换x和y

(value - startValue)/byValue=(当前时间/持续时间)*(当前时间/持续时间)

整理一下。

值=(当前时间/持续时间)*(当前时间/持续时间)* by值startValue

简化(只简化英文单词)

值=(t,b,c,d)=((t/d) * (t/d) * c b)

这个效果其实就是easeInQuad的慢进效果,其他函数也是这样推导出来的,只要你会写。但是,即使你知道如何推导,你也很难有直观的效果。其实常见的常用的也就那么几个,网上有很多打包演示的。有印象就好(比如可以搜索Tween.js)。

当然,你也可以看看功能图,简单猜测一下效果。具体看每个点的斜率。斜率越接近水平越慢,斜率越接近垂直越快。如果你的函数曲线中y值超过1,说明中点会在某个点超过终点。如果y值小于0,说明中点在某一点会小于起点。

慢动函数有一个很大的特点,就是起点和终点都是确定的。可以随机计算中间位置,可以快也可以慢,可以超过终点,也可以小于起点。你可以只写一个方程来运行它,然后根据效果进行调试。我相信你一定见过以下类型的图片:

现在再看看,不知道会不会感觉好一点?

小结

本章主要说明动画在画布上的实现。其中很重要的一点就是如何在不同的帧率下达到同样的动画效果,也就是要用时间来衡量。用画布制作的游戏也是如此。每次时间向前滴答(滴答的意思是,这是一个生动的名字,古代时钟的感觉),画布就会向前推进一次(重画)。

然后补充两个小点:

通常情况下,动画总是伴随着画布的重绘,但是默认情况下,fabric.js不会自动为我们重绘,需要我们手动调用(你可以在开场代码中看到onChange的回调是怎么写的)。这是因为如果画布中有许多对象在移动,默认的自动重绘会降低性能。动画不仅可以作用于位置,还可以作用于各种属性,如透明度、颜色等。其实只要是数值就可以动画。感谢我们之前将数据与视图分离的架构,这一章所做的只是改变数据,而不是画布绘制的内容。

下面是fabric.js的简短版本代码

以上是JS前端可视化画布动画原理及推导的详细内容。更多关于JS前端可视化画布动画的信息,请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: