Contents
  1. 1. 前言
  2. 2. 分析
  3. 3. 计算
  4. 4. 完成
  5. 5. 总结
  6. 6. Reference

前言

接到个项目,前端负责制作页面效果,后端负责编写数据接口。项目中一个效果是拨动指针选择时间,前端同事在开发过程中碰到一个问题,当手指按住指针往上下垂直移动或左右水平移动时,指针会胡乱旋转。由于后端开发压力较轻松,我尝试解决问题。

分析

由于手指需要拨动的是指针,自然而然的想到了使用坐标和三角函数来解决。
在canvas中,坐标与通常意义上的不同,顶部是x轴,左侧为y轴,页面的左上角是原点(0,0)。
为了方便后续开发,先自定义第一到第四象限,如下图:
canvas自定义象限

获取canvas的高宽作为虚拟坐标x,y轴的中心点(简称cp:center point)。
这样只需要实时监测手指触点的坐标(x,y),并与cp的的坐标进行比对,就能知道手指触点所在的象限,并计算指针指向。

计算

以第一象限为例,若手指触点(简称tp:touch point)、箭头指向(简称ap:arrow point)与cp的关系如下图所示:
第一象限,手指触点、箭头指向与cp的关系

使用HTMLElement.prototype.getBoundingClientRect获取tp的坐标,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BA、CD垂直于oy。
AB = cp.x - tp.x;
AO = cp.y - tp.y;
根据三角函数,正切tan = 对边比邻边。
tan(∠ABO) = AO/AB;
可求出∠ABO,同时∠DCO = ∠ABO。
CO的长度即为箭头图片的高度。
根据正弦sin = 对边比斜边。
sin(∠DCO) = DO/CO;
可求出DO,再根据勾股定理推出DC。
ap.x = cp.x - DO;
ap.y = cp.y - DC;
即可得箭头AP的坐标。

之后发现问题,AP的坐标是箭头图片左上角的坐标,在插入箭头图片时,需要将AP的坐标以垂直于BO向左移动1/2图片宽度的距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
EC垂直于CO,EC为1/2图片宽度,延长线相交于oy,取点H。
CD垂直于oy,EF垂直于CD,∠DCO为已知。
∠DCH = PI/2 - ∠DCO;
∠DHC = PI/2 - ∠DCH;
根据三角函数,正弦sin = 对边比斜边。
sin(∠DHC) = CD/CH;
CD = cp.y - ap.y;
可得CH。
EH = CH - CE;
根据平行线同位角定理,∠FEC = ∠DHC。
sin(∠FEC) = CF/CE;
可得CF。
E.x = cp.x - (EF + ap.x);
E.y = cp.y - (CD - CF);

这样箭头图片的左上角就能正确的在应有的位置上。
接下来是计算canvas旋转的角度。需要先按角度旋转canvas画布,插入图片,再把角度转回去。
由于上述计算得出的∠DOC的角度是基于CP的虚拟x轴,而CanvasRenderingContext2D.prototype.rotate所用到的角度是从虚拟x轴右侧向下开始计算的。
所以需要将算得的第一象限的∠DOC + PI得到Canvas旋转所用角度,再 - 1.5 * PI,其他象限相同处理。
Canvas旋转所用角度

完成

角度坐标都有了,接下来的事情就简单了,为了方便测试,使用鼠标代替手指。
将计算坐标的方法绑定在onmousemove上,给onmouseuponmousedown分别加上开关,功能就完成了。
最终可以鼠标点住指针拖动选择任意角度放开。
这里看效果,手机和pc都可以。

总结

有目标的解决实际问题,快速学习canvas动画,理解canvas中角度的计算等基础知识。
虽然demo做到这里算是完成了,总用时也就一个下午,但是由于项目的进度问题,并没有来得及用上。
使用初中的三角函数知识能够解决现在工作中的问题算是一个意外之喜,遇到卡壳的时候回头看看,说不定以前学过的知识能够帮助解决。

Reference

js获取移动端触摸坐标 - pengchengzhong
直角三角函数公式大全
效果展示 - backwallkid

Contents
  1. 1. 前言
  2. 2. 分析
  3. 3. 计算
  4. 4. 完成
  5. 5. 总结
  6. 6. Reference