Git Product home page Git Product logo

dk-plus.github.io's People

Contributors

dk-plus avatar

Watchers

 avatar

dk-plus.github.io's Issues

canvas:摸得着的动画

你在凝望深渊的时候,深渊也在凝望你。——Who(你猜

这也是交互吗?

什么叫交互

看电影是交互吗?不是,你的行为影响不了画面(你把屏幕砸坏的行为除外

玩游戏是交互吗?是的,你能控制游戏角色的行为。

你的行为,比如移动鼠标、敲下键盘、在摄像头前摇摇头、喊一声Siri,通过输入设备能对画面产生变化的,就叫交互。

事件与监听

你的特定行为能被电脑感知,是因为触发(emit)了特定事件,同时电脑有监听(on)这一事件,并执行针对这一事件发生后的对应操作(callback)。

你移动鼠标,画面就会有个鼠标跟着你的行为移动;

你点击图标,画面上的图标就会是选中状态;

你拿着鼠标抠脚,画面根本不会有什么反应,因为它没有监听你用鼠标抠脚这种事件。

虽然我举的这个例子很荒谬,但你不能否认只要有对应的输入设备,就能监听到这种行为,就像《国产凌凌漆》所说的那样。

image-20220423142631787

鼠标事件获取交互数据

我们拿鼠标事件举例,简单讲讲鼠标与屏幕的交互。

鼠标事件的目的大多数是根据事件类型(按下、松开、移动)获取所在的位置(x,y)。

mousedown
mouseup
mouseenter
mouseleave
mouseout
mouseover
...

试下在任意一个网页的控制台输入并运行window.onmousedown = console.log,再点击网页的任意位置,就会输出以下信息:

我截取了部分即便是非程序员也多少能看得懂的属性

altKey: true // 是否同时按下alt键
button: 0 // 左键、滚轮键or右键
ctrlKey: false // 是否同时按下ctrl键
shiftKey: false // 是否同时按下shift键
x: 852
y: 180
  1. x、y就是鼠标位置,用于判断点是否在形状之内,比如有没有选中图标
  2. button就是按键类型,玩CS的右键瞄准、左键攻击
  3. 点鼠标的时候同时点击ctrl能选中多个文件

除了鼠标事件以外,还有其他输入设备产生交互的事件,比如键盘(keydown、keyup)、触控屏(touchstart、touchend),这些事件存在的意义就是让我们了解用户交互行为的详细信息,然后写一些对应的操作,比如动画。

交互对数据的影响

如果数据也符合牛顿第一定律,那么数据一开始是静止的,它发生变化一定是受到了外力的作用,这个外力就是交互。

image-20220423220851685

交互会触发事件,事件记录当时的交互数据,交互数据和其他数据之间会有运算。

  1. 移动鼠标,鼠标的位置(x,y)必然变化,属于交互数据的变化
  2. 画面有一个按钮,属于其他数据
  3. 鼠标来到按钮上,两者之间的运算得出一个结果:isIntersected: true,这就是交互对数据的影响

你喜欢怎么表达这个运算结果都可以,温柔点,你可以让按钮变色;粗暴点,你可以让它爆炸。

image-20220423220700393

数据的变化引起的动画

画面的本质是数据,数据发生变化,画面随之变化。

我们遵循以下规则:

  1. 交互->数据,通过交互改变数据
  2. 数据->绘制,根据当前的数据绘制画面

也就是说,交互不直接调用绘制函数,第1条是本篇文章的核心,第2条是上一篇的核心,数据静止就是静态画面,数据变化就变成了动画。

实践

上一篇,我们写了一个简单的二维运动,我们在这个二维运动的基础上加入鼠标交互:圆心在鼠标方圆五百里内(换算一下就是100px,当然这是我瞎扯的)的粒子变大

获取交互数据

这里的交互数据是鼠标位置,设一个全局变量mouse

const mouse = {
	x: 0;
	y: 0;
}

监听鼠标移动事件,移动时更新mouse

window.addEventListener('mousemove', (e) => {
  mouse.x = e.x;
  mouse.y = e.y;
});

通过交互更新数据

沿用上一篇文章的结构,粒子类有个update函数,在这里更新交互带来的数据影响。

在此增加minRmaxR作为粒子变大变小的极值。

class Particle {
	...
  update() {
  	...
  	// 勾股定理,两点距离<100,粒子变大,否则变小
    if (Math.sqrt((mouse.x - this.x) ** 2 + (mouse.y - this.y) ** 2) <= 100) {
      if (this.r < this.maxR) {
        this.r += 1;
      }
    } else if (this.r > this.minR) {
      this.r -= 1;
    }

    this.x += this.dx;
    this.y += this.dy;
    this.draw();
  }
}

效果如下:

1

如果你觉得这个不是很有趣,不妨看看下面这个效果:

  1. 鼠标松开的时候做抛体运动
  2. 鼠标按下的时候做向心加速运动

1

可以提前思考一下这个效果怎么实现,如无意外,后两篇就是介绍向量和力的可视化了。

つづく

本文简单贴了几张周星驰电影的截图介绍并实现了交互事件在canvas中的运用,喜欢的朋友可以点个赞,也欢迎大家关注dkplus公众号,没东西写的时候说不定就开始画画了。

canvas-前端的纸和笔

在一切谎言中,艺术是最真实的。

——福楼拜

先说我小时候的故事

四年级的时候,我得到了人生的第一台电脑——用CRT显示器的老古董,老表不要的送的(语言艺术

什么是CRT(阴极射线管)显示器,与液晶显示器的区别- 好文部- 作文范文,高中小学生字数作文,知识百科

拿到电脑后的我心情澎湃,想用它去画《猛兽侠》的黑猩猩队长,马上请教老表能不能画,怎么画(广西表妹《表哥我出来了哦》

超级帅气的猩猩队长!永远的黑猩猩!

他说当然能画!然后给我打开这个软件

WINDOWS系列工具--画图详细教程_多特软件

一顿操作后,我发现拿鼠标根本画不出黑猩猩队长,甚至画个圆都画不稳,最后我就放弃了。以至于后面很多年都没接触过电子绘画。

(理论上是能画出来的,毕竟位图都是像素,就像国足也存在理论上出线的可能)

直到2019年我大学毕业,刷起了ins,发现很多国外的画师,打开了新世界(别骂了,真的村通网

然后买了个数位板,接着顺理成章的成为了自由画师继续吃灰(这才是

到这里故事就讲完了,大家可以关掉页面了我们顺着windows画图开始进入正题——canvas。

canvas和windows画图

这两个东西非常像,概括起来就是:「画图」是用鼠标画,canvas是用代码画。

但「画图」的本质也是用代码画,只是你用鼠标和电脑交互,执行了对应的代码。

为什么拿「画图」来对比,不拿PS,因为它足够简单,很多功能都能一一对应。

canvas是纸

浏览器有个<canvas>标签,这个标签有两个属性,widthheight,就像纸有尺寸一样。

下面就是尺寸为300*300px的纸

<canvas id="canvas" width="300" height="300"></canvas>

打开「画图」,新建尺寸300*300,一样的。

canvas.context是笔盒

画画要用到哪些工具?铅笔、尺规、上色笔、橡皮擦,这些工具被集合到一个叫context的地方,你可以把它看做笔盒。

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

对标画图,就是左侧的工具栏。

笔盒里有什么

现实中你能想象到的尺规,勾线笔,填色笔,橡皮擦它都有,计算机图像处理范畴的线性变化、混合模式它也有,我不打算做API搬运工,只想简单阐述一下。

勾线笔和填色笔

有了纸,我们还必须有笔才能写写画画。

笔分勾线笔和填色笔,前者用于勾勒轮廓,后者用于填充颜色至轮廓内。

  1. 默认勾线笔在「画图」中对应「铅笔」
  2. 填色笔对应「油漆桶」
  3. 「刷子」是线条较粗的勾线笔
  4. 「喷枪」是特殊勾线笔,也许我后面会出一篇专门的画笔制作记录
  5. 勾线和填色前,可以先「选取颜色」

至此,你应该能想象到哪些API对应上面的概念

ctx.stroke(); // 勾线
ctx.fill(); // 填色
ctx.strokeStyle = "white"; // 选取勾线笔颜色
ctx.fillStyle = "white"; // 选取填色笔颜色
ctx.lineWidth = 1; // 线条粗细

橡皮擦

有铅笔的地方就会有橡皮擦,这里所谓的橡皮擦就是清除矩形

ctx.clearRect(45, 45, 60, 60);

起笔、行笔、收笔

我们知道用勾线笔和填色笔能画出东西,也要知道画的步骤。

起笔、行笔、收笔,书法大家的必学之路! - 15练字网

我们拿书法的起笔、行笔、收笔来阐述以下步骤。

ctx.beginPath(); // 起笔
// 行笔
ctx.moveTo(125, 125);
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.closePath(); // 收笔

思考一下,如果写下一笔前,没有起笔这一步,写出来的字会怎样?

会连在一起是吧,就像楷书转行书一样。

image-20220403171810715

对标「画图」,就是按下鼠标,按住拖动鼠标,松开鼠标。

尺规

知道了画画的步骤,想要画出具体的形状,还需要借助尺规。

直线、三角形、圆、矩形、多边形、贝塞尔曲线都在这个尺规内,对应「画图」工具栏中底部的形状部分。

∵两点成线

∴直线只要两个点

ctx.moveTo(75, 50);
ctx.lineTo(100, 75);

∵三点成面

∴三个以上的点相互连接成一个多边形

ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);

圆形由圆弧组成,具体参数含义可以查看API,圆无非就是确定圆心、半径、弧度0~2π

ctx.arc(60, 65, 5, 0, Math.PI * 2, true)

矩形只要确定左上角坐标、宽、高即可

ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);

至于贝塞尔曲线,挖个坑,后面展开讲

quadraticCurveTo(cp1x, cp1y, x, y);
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

局部换笔

以前上学的时候,上课会用到黑笔、红笔、荧光笔做笔记,默认是用黑笔,遇到特殊的地方会用红笔或者荧光笔标记,标记后重新切回黑笔,这种局部换笔的操作在canvas里也有。

// 默认用黑笔
...
ctx.save();
// 切换成红笔
ctx.strokeStyle = 'red';
...
ctx.restore();
// 再回来黑笔
...

在「画图」中,就是切换工具,没啥好说的。

其他

还有很多API,我并不打算在这个基础篇中展开讲

  1. 变形,也就是平移、缩放、旋转、剪切,可以另开一篇线性代数相关
  2. 混合模式,就是你们在PS界面图层那块看到的,包括正片叠底效果,也可以另开一篇(这是今天挖的第几个坑来着

数据可视化

图形,本质上是数据的可视化显示。

我们要做的,是写一个通用逻辑,设计一个通用数据结构,然后用逻辑把数据变成图形。

我们简单设计一个数据结构吧,比如形状,它可以是矩形、三角形、圆等,相应的拥有自己独特的参数,比如圆的圆心、半径,三角形的三个顶点。

interface Shape {
	type: 'rect' | 'triangle' | 'circle' | 'line' | 'point';
	params: any;
}

再来写个通用逻辑,先判断Shape类型,调用相应的方法渲染图形

function renderShape(shape: Shape) {
  ctx.save();
  ctx.beginPath();
  
	if (shape.type === 'rect') {
    const {x, y, width, height} = shape.params
		ctx.strokeRect(x, y, width, height);
	}
	...
  
  ctx.stroke();
  ctx.closePath();
  ctx.restore();
}

最后,我们拿到一堆这样的数据,遍历它们,调用通用逻辑渲染即可

const data = [
	{
		type: 'rect',
		params: {
			x: 10,
			y: 10,
			width: 100,
			height: 100,
		},
	},
	{
		type: 'triangle',
		params: {
			p1: { x: 100, y: 150 },
			p2: { x: 200, y: 250 },
			p3: { x: 100, y: 250 },
		},
		...
	}
]

for(let s of data) {
  renderShape(s)
}

最终结果如图所示,这就是所谓的数据可视化(看起来很low,但它确实是

需要示例的朋友可以复制查看:https://codepen.io/dkplus/pen/gOoooBb?editors=0010

image-20220404235832830

つづく

以上,就是本篇的全部内容,我们认识了前端的纸和笔,对标了经典windows软件「画图」,也看到了如何使用通用数据结构和通用逻辑进行静态绘制。后面的篇章就会往动画的方向去写了。

最后就是引流时间了:欢迎关注公众号dkplus,一起探讨图像背后的各种技术细节!

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

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

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

说个故事

我读大一的时候(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公众号,聊一些图像相关的技术。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.