白板通常会提供多种工具类型,每种工具的用途也各不相同,例如下表:
工具名称 | 用途 |
---|---|
框选工具 | 框选其他图形 |
涂鸦工具 | 涂鸦 |
橡皮擦工具 | 擦除画笔痕迹或者图形 |
激光笔工具 | 激光笔 |
直线工具 | 绘制直线 |
箭头工具 | 绘制箭头 |
矩形工具 | 绘制矩形 |
椭圆工具 | 绘制椭圆 |
文本工具 | 插入文本 |
正因此白板除了要判断当前画笔的工具类型的同时,还要对该工具类型在白板上的表现形式进行区分和处理,这个工作会面临着许多的条件判断,所以我们会借助一些算法来辅助我们判断是否满足条件,根据条件的不同执行不同的动作,从而在白板上呈现出不一样的画面。
比如:在白板中我们经常会用到碰撞检测算法来辅助判断以下情况:
- 框选工具是否框中图形,选中哪些图形?
- 鼠标是否选中图形(空心形状还是填充形状)?
- 图形是否相交/切?
- 等等
最终我们将这些检测算法一步步拆解下来后,会发现它们的实现会经常用到我们熟知的一些函数以及方程式:三角函数、勾股定理、圆的标准方程式等等,如果是动画的话还包括重力加速度、均速、抛物线等等。
那么今天我们就主要来聊聊,函数以及方程式在白板上的一些简单的应用场景吧!
- 复习一下:什么是直角坐标系、三角函数、勾股定理、圆的标准方程式
- 这些函数以及方程式都应用在哪些场景?
- 实战练习
直接坐标系
通常我们所接触的直角坐标系的 Y 轴都是向上的,但是由于前端的渲染机制是从左上角开始,到右下角结束,因此前端坐标系的 Y 轴都是向下的。
在平面内画两条互相垂直,并且有公共原点的数轴。其中横轴为 X 轴,纵轴为 Y 轴。这样我们就说在平面上建立了平面直角坐标系,简称直角坐标系。还分为第一象限,第二象限,第三象限,第四象限。从右上角开始(逆时针方向)算起。
勾股定理
勾股定理,常用于数学和几何学中,是一个基本的几何定理,指直角三角形的两条直角边的平方和等于斜边的平方。
公式:a2 + b2 = c2
应用场景
在白板中我们经常使用勾股定理来:
- 计算两个坐标点之间的距离
- 计算两形状是否相交(切)
- 判断坐标是否在圆上
- 高阶:计算物理移动的速度
三角函数
三角函数在数学这门学科中属于初等函数,通常在平面直角坐标系中定义。三角函数看似很复杂,也有很多公式,但都可以相互推导。在我们学习圆的标准方程式时,我们会发现三角函数结合勾股定理还可以推理得到圆的标准方程式。
首先,我们看一下几个简单又常用的三角函数在几何学和直角坐标系中的实现方式:
几何图形 | 坐标系 | |
---|---|---|
图片 | ![]() | ![]() |
正弦函数 | sin A = 对边 a / 斜边 c | sin θ = y / r |
余弦函数 | cos A = 邻边 b / 斜边 c | cos θ = x / r |
正切函数 | tan A = 对边 a / 邻边 b | tan θ = y / x |
根据上图,我们应该很快能浮想起:数学老师在讲台上第一次发出 sin
、cos
读法惹人发笑,复读机式的对边比邻边,邻边比对边
等一系列催眠口诀。
下面我们看一下三角函数的定义:任何角的集合与一个比值的集合的变量之间的映射。那么换成我的话来讲:
- 已知两条边,求两条边的夹角
- 已知一条边和这条边的夹角,求夹角的另一条边
在上面的基础上,我们要举一反三、互相推导。
应用场景
在白板中我们经常使用三角函数来:
- 绘制箭头
- 计算旋转角度
圆的标准方程式
圆的两个基本概念:圆心、半径,其中圆心坐标是圆的定位条件,半径是圆的定形条件。当圆心坐标和半径确定之后,圆在直角坐标系中的位置和大小也已经确定。
圆心坐标为(a,b),r 为圆的半径
公式:(x-a)²+(y-b)²=r²
应用场景
在白板中我们经常使用圆的标准方程式来判断坐标是否在圆上。
实战练习
针对上面几个函数以及表达式,为了避免过于干,下面我举了几个例子,大家也可以举一反三,慢慢推导:
- 计算两个图形是否相交(切)
- 绘制箭头
- 笔锋(签字笔、钢笔、毛笔)
通常我们会结合多个函数以及方程式相互配合去实现一个功能或者实现一个算法。例如
笔锋
,我们就需要结合物理学的速度计算公式以及数学的勾股定理计算两点的(平面直接坐标系中的)距离。
计算两个图形是否相交(切)
白板相交判断通常有两种:一种是外接矩形判定法,另一种是外接圆形判定法。为了保证真实性,我们通常会将近似圆的图形使用外接圆形判定法,否则使用外接矩形判定法。
外接矩形判定法 和 外接圆形判定法 都可以使用中心判定法来实现,不同的是:
-
圆形只需要判断两个圆心之间的距离是否小于两圆半径之和
-
矩形则需要判断两个矩形中心点的:
- x 轴的距离是否小于两矩形宽度之和的 1 / 2
- y 轴的距离是否小于两个矩形高度之和的 1 / 2
中心判定法的本质是计算两点(中心)之间的距离。
绘制箭头
绘制箭头,我们需要用到勾股定理、三角函数。
这里我们以在直线末端绘制箭头为例,下面是它的实现步骤:
-
第一步,我们需要绘制一条直线,从 A(x0, y0) 点到 B(x1, y1) 点
-
第二步,设置箭头的长度
headlen
和箭头与之前的夹角θ
-
第三步,计算这条线与 X 轴正方向的夹角
这里我们使用 atan2 函数,该函数可以计算出从原点 (0,0) 到 (x,y) 点的线段与 x 轴正方向之间的平面角度(弧度值)。
我们将 A(x0, y0) 看做是原点 (0, 0),也就是从原点 A(x0, y0) 到 B(x1, y1) 点的线段与 x 轴正方向之间的平面角度(弧度值),然后将其转换成度数。
// 将弧度制转换成度数
const angle = Math.atan2(y1 - y0, x1 - x0) * 180 / Math.PI;
-
第四步,推理出箭头左右(上下)两侧斜线与 X 轴正方向的夹角
根据步骤三我们可以算出箭头两侧的斜线与 x 轴正方向之间的平面角度(弧度值),已知箭头与直线的夹角
θ
是固定的,另外已知直线与 X 轴正方向的夹角,只需要减去或者加上θ
就是箭头两侧的斜线与 x 轴正方向之间的平面角度。大家可以在箭头末端作出延伸线(如下图),箭头上边(下图左边)的夹角为
π + α - θ
,箭头下边(图右边)的夹角为π + α + θ
。
下面是代码示例:
// 箭头的左侧夹角
const angle1 = (angle - theta) * Math.PI / 180;
// 箭头的右侧夹角
const angle2 = (angle + theta) * Math.PI / 180;
// 计算出右侧箭头的坐标点
const topX = headlen * Math.cos(angle1);
const topY = headlen * Math.sin(angle1);
// 计算出左侧箭头的坐标点
const botX = headlen * Math.cos(angle2);
const botY = headlen * Math.sin(angle2);
最终,我们计算出了箭头的 2 个关键坐标:(topx, topy)
和 (botx, boty)
。更详细的介绍请移步这里。
笔锋
在开始介绍之前,我推荐一个第三方库 points-on-curve
,它会将一系列的坐标点转换成很好看到笔锋(svg 或者 canvas),还提供很多丰富的配置,详情点击。
下面我们讲讲笔锋的实现方式,其中最常见的步骤大致为:
- 落点是个圆形
- 通过计算画笔移动的距离和时间得到画笔的速度,通过速度的快慢来设置画笔的粗细
- 使用二次贝塞尔曲线或者三次贝塞尔曲线进行绘制
- 收笔画笔粗细慢慢变细(可以根据笔的类型:钢笔、毛笔、签字笔等自由发挥)
这里我们只阐述如果简单的实现笔锋效果,关于更多的实现方式在文末的参考文献中也有提供。下面我们一起来看看如何实现从 A(x0, y0) 点到 B(x1, y1) 点来绘制笔锋。
-
第一步,通过勾股定理计算出 AB 两点之间的距离
// 计算两点 X 轴上的距离 const x = x1 - x0; // 计算两点 Y 轴上的距离 const y = y1 - y0; // 计算两点的直线距离 const s = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
-
第二步,通过速度计算公式计算出两点之前移动的速度
一般白板中我们会记录每个坐标绘制的时间,因此我们可以很轻易的计算出 A 到 B 的耗时
t
,套用速度计算公式,我们就可以计算出,A 到 B 绘制的速度v
。// 速度计算公式 速度 v = 距离 s / 时间 t
-
第三步,根据速度快慢来调整画笔的粗细
最后我们根据速度的快慢,然后根据可变的速度比例,计算出速度在多少以下变粗,多少以下变细。
其他
除了以上我介绍的一些场景,白板还有其他的很多场景,例如:
- 图形旋转 - 使用矩阵转换的公式
- 动画 - 抛物线、匀速运动,加速运动,重力加速
- 等等
本文的目的只是一个入门的介绍,虽然大多都是讲一些在 2D 平面上的运用,但是在 3D 纬度上有些知识也还是受用的,想必有些同学已经磨手擦掌准备跃跃欲试了 ,欢迎留言、转发加评论。