dk-plus / dk-plus.github.io Goto Github PK
View Code? Open in Web Editor NEWthis is my hexo blog
this is my hexo blog
你在凝望深渊的时候,深渊也在凝望你。——Who(你猜
这也是交互吗?
看电影是交互吗?不是,你的行为影响不了画面(你把屏幕砸坏的行为除外
玩游戏是交互吗?是的,你能控制游戏角色的行为。
你的行为,比如移动鼠标、敲下键盘、在摄像头前摇摇头、喊一声Siri,通过输入设备能对画面产生变化的,就叫交互。
你的特定行为能被电脑感知,是因为触发(emit)了特定事件,同时电脑有监听(on)这一事件,并执行针对这一事件发生后的对应操作(callback)。
你移动鼠标,画面就会有个鼠标跟着你的行为移动;
你点击图标,画面上的图标就会是选中状态;
你拿着鼠标抠脚,画面根本不会有什么反应,因为它没有监听你用鼠标抠脚这种事件。
虽然我举的这个例子很荒谬,但你不能否认只要有对应的输入设备,就能监听到这种行为,就像《国产凌凌漆》所说的那样。
我们拿鼠标事件举例,简单讲讲鼠标与屏幕的交互。
鼠标事件的目的大多数是根据事件类型(按下、松开、移动)获取所在的位置(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
除了鼠标事件以外,还有其他输入设备产生交互的事件,比如键盘(keydown、keyup)、触控屏(touchstart、touchend),这些事件存在的意义就是让我们了解用户交互行为的详细信息,然后写一些对应的操作,比如动画。
如果数据也符合牛顿第一定律,那么数据一开始是静止的,它发生变化一定是受到了外力的作用,这个外力就是交互。
交互会触发事件,事件记录当时的交互数据,交互数据和其他数据之间会有运算。
isIntersected: true
,这就是交互对数据的影响你喜欢怎么表达这个运算结果都可以,温柔点,你可以让按钮变色;粗暴点,你可以让它爆炸。
画面的本质是数据,数据发生变化,画面随之变化。
我们遵循以下规则:
也就是说,交互不直接调用绘制函数,第1条是本篇文章的核心,第2条是上一篇的核心,数据静止就是静态画面,数据变化就变成了动画。
上一篇,我们写了一个简单的二维运动,我们在这个二维运动的基础上加入鼠标交互:圆心在鼠标方圆五百里内(换算一下就是100px,当然这是我瞎扯的)的粒子变大
这里的交互数据是鼠标位置,设一个全局变量mouse
const mouse = {
x: 0;
y: 0;
}
监听鼠标移动事件,移动时更新mouse
window.addEventListener('mousemove', (e) => {
mouse.x = e.x;
mouse.y = e.y;
});
沿用上一篇文章的结构,粒子类有个update
函数,在这里更新交互带来的数据影响。
在此增加minR
和maxR
作为粒子变大变小的极值。
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();
}
}
效果如下:
如果你觉得这个不是很有趣,不妨看看下面这个效果:
可以提前思考一下这个效果怎么实现,如无意外,后两篇就是介绍向量和力的可视化了。
本文简单贴了几张周星驰电影的截图介绍并实现了交互事件在canvas中的运用,喜欢的朋友可以点个赞,也欢迎大家关注dkplus公众号,没东西写的时候说不定就开始画画了。
在一切谎言中,艺术是最真实的。
——福楼拜
四年级的时候,我得到了人生的第一台电脑——用CRT显示器的老古董,老表不要的送的(语言艺术
拿到电脑后的我心情澎湃,想用它去画《猛兽侠》的黑猩猩队长,马上请教老表能不能画,怎么画(广西表妹《表哥我出来了哦》
他说当然能画!然后给我打开这个软件
一顿操作后,我发现拿鼠标根本画不出黑猩猩队长,甚至画个圆都画不稳,最后我就放弃了。以至于后面很多年都没接触过电子绘画。
(理论上是能画出来的,毕竟位图都是像素,就像国足也存在理论上出线的可能)
直到2019年我大学毕业,刷起了ins,发现很多国外的画师,打开了新世界(别骂了,真的村通网
然后买了个数位板,接着顺理成章的成为了自由画师继续吃灰(这才是
到这里故事就讲完了,大家可以关掉页面了我们顺着windows画图开始进入正题——canvas。
这两个东西非常像,概括起来就是:「画图」是用鼠标画,canvas是用代码画。
但「画图」的本质也是用代码画,只是你用鼠标和电脑交互,执行了对应的代码。
为什么拿「画图」来对比,不拿PS,因为它足够简单,很多功能都能一一对应。
浏览器有个<canvas>
标签,这个标签有两个属性,width
和height
,就像纸有尺寸一样。
下面就是尺寸为300*300px的纸
<canvas id="canvas" width="300" height="300"></canvas>
打开「画图」,新建尺寸300*300,一样的。
画画要用到哪些工具?铅笔、尺规、上色笔、橡皮擦,这些工具被集合到一个叫context的地方,你可以把它看做笔盒。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
对标画图,就是左侧的工具栏。
现实中你能想象到的尺规,勾线笔,填色笔,橡皮擦它都有,计算机图像处理范畴的线性变化、混合模式它也有,我不打算做API搬运工,只想简单阐述一下。
有了纸,我们还必须有笔才能写写画画。
笔分勾线笔和填色笔,前者用于勾勒轮廓,后者用于填充颜色至轮廓内。
至此,你应该能想象到哪些API对应上面的概念
ctx.stroke(); // 勾线
ctx.fill(); // 填色
ctx.strokeStyle = "white"; // 选取勾线笔颜色
ctx.fillStyle = "white"; // 选取填色笔颜色
ctx.lineWidth = 1; // 线条粗细
有铅笔的地方就会有橡皮擦,这里所谓的橡皮擦就是清除矩形
ctx.clearRect(45, 45, 60, 60);
我们知道用勾线笔和填色笔能画出东西,也要知道画的步骤。
我们拿书法的起笔、行笔、收笔来阐述以下步骤。
ctx.beginPath(); // 起笔
// 行笔
ctx.moveTo(125, 125);
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.closePath(); // 收笔
思考一下,如果写下一笔前,没有起笔这一步,写出来的字会怎样?
会连在一起是吧,就像楷书转行书一样。
对标「画图」,就是按下鼠标,按住拖动鼠标,松开鼠标。
知道了画画的步骤,想要画出具体的形状,还需要借助尺规。
直线、三角形、圆、矩形、多边形、贝塞尔曲线都在这个尺规内,对应「画图」工具栏中底部的形状部分。
∵两点成线
∴直线只要两个点
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,我并不打算在这个基础篇中展开讲
图形,本质上是数据的可视化显示。
我们要做的,是写一个通用逻辑,设计一个通用数据结构,然后用逻辑把数据变成图形。
我们简单设计一个数据结构吧,比如形状,它可以是矩形、三角形、圆等,相应的拥有自己独特的参数,比如圆的圆心、半径,三角形的三个顶点。
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
以上,就是本篇的全部内容,我们认识了前端的纸和笔,对标了经典windows软件「画图」,也看到了如何使用通用数据结构和通用逻辑进行静态绘制。后面的篇章就会往动画的方向去写了。
最后就是引流时间了:欢迎关注公众号dkplus,一起探讨图像背后的各种技术细节!
我在青少年时期产生了探寻存在之意义的渴求,大学期间就只学习了文学与哲学等人文学科,所以我并不知道别人口中艰涩、无趣、毫无用处的微积分竟然是上帝的语言”。——《微积分的力量》
这句话我是感同身受的,我高中大学都是文科,意识到它们的作用时已经是相见恨晚。
我读大一的时候(2015年),有门课叫西方经济学,现在很多东西我都忘了,唯一记得的就是边际效应。
在微观经济学中,边际效用(英语:Marginal utility),又译为边际效应,是指每新增(或减少)一个单位的[商品](https://zh.wikipedia.org/wiki/商品)或[[服务](https://zh.wikipedia.org/wiki/%E6%9C%8D%E5%8A%A1)](https://zh.wikipedia.org/wiki/服务),它对商品或服务的收益增加(或减少)的效用,也即是“效用──商品或服务量”图的斜率。——维基百科
当时学边际效应,老师要求我们计算收益曲线的斜率,也就是对曲线求导。
我只觉得很神奇,但不知道为什么求导就能得出边际收益曲线,而且这玩意跟微积分很像。
我不打算涉及任何数学符号表示的公式,我会引用一段话来阐述微积分:
为了探究任意一个连续的形状、物体、运动、过程或现象(不管它看起来有多么狂野和复杂),把它重新想象成由无穷多个简单部分组成的事物,分析这些部分,然后把结果加在一起,就能理解最初的那个整体。——《微积分的力量》
简单来说,微积分可分为微分和积分,分解的过程叫微分,叠加的过程叫积分。
以我们中学最熟悉的位移、速度、加速度举例:
根据上面的物理例子,我们同样能对动画得出相似的结论:
为什么可以呢?
因为动画也是连续的过程,人眼识别连续图像的速度是24帧/秒,只要在1秒内塞下24张静态图片,那么我们就认为他是动画;如果1秒只有那么几张,我们可能认为它是PPT。
下图是MIT的经典力学公开课,教授做了一个自由落体运动的实验,关掉所有的灯,留下一台按一定频率闪烁的灯,用相机长曝光记录轨迹。
有了这张轨迹图,即使它不会动,我们也能感受到球在做自由落体运动,这就是积分学在摄影长曝光上展示的魅力,包括瀑布、星轨等照片亦是如此,换句话说,摄影就是进光量的积分过程(说这话的时候感觉自己升华到了哲学层次
那如果频闪一次拍一张照片,这些照片连续播放起来就是动画,像小时候看的连环画一样。
(图不会动,不是你网络不行,而是我没找到动图)
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。——MDN
RAF就是为了动画而生的函数,会根据屏幕的刷新率执行。如果屏幕的刷新率为60Hz,就是1秒内最多能连续播出60张静态画面。
在回调函数再次调用RAF,就能做到连续绘制图像。
const animate = () => {
// 每一帧的绘制
update();
requestAnimationFrame(animate);
};
animate();
上面的代码并没有清除上一帧的残留,动画会像长曝光一样出现轨迹:
清除残留很简单,在绘制下一帧前调用上一篇文章提到的橡皮擦ctx.clearRect
即可:
const animate = () => {
// 先清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
update();
requestAnimationFrame(animate);
};
animate();
如果想来点残影,像流星一样带一条尾巴,只要清除残留的时候不完全清除即可,这里就不能用橡皮擦了,要用ctx.fillRect
重新覆盖一个带透明度的背景色:
const animate = () => {
// 覆盖带透明度的背景色
ctx.fillStyle = 'rgba(255,255,255,0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
update();
requestAnimationFrame(animate);
};
animate();
说了这么多,现在就着手实践一下
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();
}
}
得出的效果就会类似下图所示:
本文引入微积分简单介绍了动画的绘制,后面的篇章就会涉及到动画交互、向量、力等更有兴趣的东西。
又到了引流时间,欢迎大家关注dkplus公众号,聊一些图像相关的技术。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.