Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

canvas-我是怎么把微积分用在动画上的 #2

Open
dk-plus opened this issue Apr 17, 2022 · 0 comments
Open

canvas-我是怎么把微积分用在动画上的 #2

dk-plus opened this issue Apr 17, 2022 · 0 comments

Comments

@dk-plus
Copy link
Owner

dk-plus commented Apr 17, 2022

我在青少年时期产生了探寻存在之意义的渴求,大学期间就只学习了文学与哲学等人文学科,所以我并不知道别人口中艰涩、无趣、毫无用处的微积分竟然是上帝的语言”。——《微积分的力量》

这句话我是感同身受的,我高中大学都是文科,意识到它们的作用时已经是相见恨晚。

说个故事

我读大一的时候(2015年),有门课叫西方经济学,现在很多东西我都忘了,唯一记得的就是边际效应。

在微观经济学中,边际效用(英语:Marginal utility),又译为边际效应,是指每新增(或减少)一个单位的[商品](https://zh.wikipedia.org/wiki/商品)或[[服务](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1)](https://zh.wikipedia.org/wiki/服务),它对商品或服务的收益增加(或减少)的效用,也即是“效用──商品或服务量”图的斜率。——维基百科

当时学边际效应,老师要求我们计算收益曲线的斜率,也就是对曲线求导。

边际效益递减规律- 快懂百科

我只觉得很神奇,但不知道为什么求导就能得出边际收益曲线,而且这玩意跟微积分很像。

微积分是什么

我不打算涉及任何数学符号表示的公式,我会引用一段话来阐述微积分:

为了探究任意一个连续的形状、物体、运动、过程或现象(不管它看起来有多么狂野和复杂),把它重新想象成由无穷多个简单部分组成的事物,分析这些部分,然后把结果加在一起,就能理解最初的那个整体。——《微积分的力量》

简单来说,微积分可分为微分和积分,分解的过程叫微分,叠加的过程叫积分。

以我们中学最熟悉的位移、速度、加速度举例:

  1. 微分过程:位移->速度->加速度
  2. 积分过程:加速度->速度->位移

File:位移速度加速度之微積分關係.svg - 维基教科书,自由的教学读本

微积分与动画的关系

根据上面的物理例子,我们同样能对动画得出相似的结论:

  1. 微分过程:动画->帧->帧变化
  2. 积分过程:帧变化->帧->动画

为什么可以呢?

因为动画也是连续的过程,人眼识别连续图像的速度是24帧/秒,只要在1秒内塞下24张静态图片,那么我们就认为他是动画;如果1秒只有那么几张,我们可能认为它是PPT。

下图是MIT的经典力学公开课,教授做了一个自由落体运动的实验,关掉所有的灯,留下一台按一定频率闪烁的灯,用相机长曝光记录轨迹。image-20220417214003336

有了这张轨迹图,即使它不会动,我们也能感受到球在做自由落体运动,这就是积分学在摄影长曝光上展示的魅力,包括瀑布、星轨等照片亦是如此,换句话说,摄影就是进光量的积分过程(说这话的时候感觉自己升华到了哲学层次

那如果频闪一次拍一张照片,这些照片连续播放起来就是动画,像小时候看的连环画一样。

翻书动画小人-西瓜视频搜索

(图不会动,不是你网络不行,而是我没找到动图)

关于RAF

什么是RAF

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。——MDN

RAF就是为了动画而生的函数,会根据屏幕的刷新率执行。如果屏幕的刷新率为60Hz,就是1秒内最多能连续播出60张静态画面。

递归调用RAF做动画

在回调函数再次调用RAF,就能做到连续绘制图像。

  const animate = () => {
    // 每一帧的绘制
    update();
    requestAnimationFrame(animate);
  };
  animate();

是否清除前一帧的残留

上面的代码并没有清除上一帧的残留,动画会像长曝光一样出现轨迹:

1

清除残留很简单,在绘制下一帧前调用上一篇文章提到的橡皮擦ctx.clearRect即可:

  const animate = () => {
    // 先清除画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    update();
    requestAnimationFrame(animate);
  };
  animate();

1

如果想来点残影,像流星一样带一条尾巴,只要清除残留的时候不完全清除即可,这里就不能用橡皮擦了,要用ctx.fillRect重新覆盖一个带透明度的背景色:

  const animate = () => {
    // 覆盖带透明度的背景色
    ctx.fillStyle = 'rgba(255,255,255,0.1)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    update();
    requestAnimationFrame(animate);
  };
  animate();

1

实现一个简单的二维粒子运动

说了这么多,现在就着手实践一下

基本的代码框架

  1. 粒子类,包含位置(x,y),绘制方法(画圆),更新数据(改变位移)
  2. 动画,递归调用RAF来更新粒子的位移,这里就是运用积分学的地方
class Particle {
  x,
  y,
  ...
  constructor(props) {...}
  draw() {
    ...
  }
  update() {
		...
    this.draw();
  }
}

const p = new Particle({
  ...
});

const animate = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  p.update();
  requestAnimationFrame(animate);
};
animate();

实践积分方程

我们先要找到粒子运动微分的部分,回想一下我们上面提到的:位移->速度->加速度。

我们抛开加速度不说,速度(vx,vy)就是位移(x,y)微分后的结果,反过来只要我们求出粒子的速度(vx,vy),在每一帧更新的时候使用如下公式更新位移:

下一帧的位置=当前帧的位置+速度

那么,第n帧的位置就是n个速度积分后的结果:

// 第一帧
S1=v0
// 第二帧
S2=v0+v1
// 第三帧
S3=v0+v1+v2
...
// 第n帧
Sn=v0+v1+...vn-1

我们设速度为(1,1),写进update方法里:

class Particle {
  ...
  update() {
    // 其他操作
    ...
    // 积分过程
		this.x+=1;
    this.y+=1;
    this.draw();
  }
}

得出的效果就会类似下图所示:

1

つづく

本文引入微积分简单介绍了动画的绘制,后面的篇章就会涉及到动画交互、向量、力等更有兴趣的东西。

又到了引流时间,欢迎大家关注dkplus公众号,聊一些图像相关的技术。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant