上一篇文章小汪写到用canvas实现画板的功能,但是思前想后在曲线上有一定的问题,点我去看上篇canvas画板。由于我把自己代入了角色,但作为用户的感官不应该是这样子的。
二次贝塞尔曲线canvas手册在描述中讲出:
也就是需要得到起始点,控制点,结束点,从而自动绘制出相切切角的曲线。
而小汪在上篇的案例中实际效果为:
也就是说 用户在一开始可能并不知道这是贝塞尔曲线,即便给出相应点位也应该把操作点2通过计算改变位置才对。
所以我做出了修改,改成用户拖拽画贝塞尔曲线。
思路:起始点在用户手指/鼠标点击时记录,在拖拽的过程中记录“touchmove”事件的返回位置并存入一个全局数组中。在用户结束触摸是正式绘制。
理论语法为:
this.ctx.moveTo(x1, y1); //起始点
this.ctx.quadraticCurveTo(x2, y2, x3, y3); //操作点与结束点
this.ctx.stroke();//封闭路径进行绘制
x1,y1 点位我们在触摸时获得,x3,y3 点位我们在结束触摸时获得,重点讲x2,y2 获得方式;
x2,y2 由于我们在拖拽的过程中向全局的一个数组存入鼠标/手指 移动的位置,然后我们取数组的中间那一个为操作点,然后计算出绘制这条曲线的实际操作点为:(这里贴出代码片段,提供思路)
setMoveTo() {
this.ctx.moveTo(this.sX, this.sY); //起点
this.ctx.lineCap = "round"; //设置线条的结束端点样式
this.ctx.lineJoin = "round"; //设置两条线相交时,所创建的拐角类型
},
/* 手指触摸画布开始 */
drawTouStart(e) {
let change = e.changedTouches[0];
this.ctx.beginPath(); //创建一条路径
let type = this.lineType;
this.sX = change.x;
this.sY = change.y;
this.setMoveTo();
},
/* 手指触摸画布移动 */
drawTouMove(e) {
let change = e.changedTouches[0];
if(!this.curveArr) this.curveArr = [];// 由于可能未定义 做一个判断
this.curveArr.push(change);// push位置信息
},
/* 手指触摸画布结束 */
drawTouEnd(e) {
let data = e.changedTouches[0];
let type = this.lineType;
this.lineTo2(data);// 结束时调用绘制方法
this.ctx.closePath(); //当鼠标移抬起时,创建从当前点回到起始点的路径
this.copyCanvas(); //每次结束都复制本次画布结果
},
/* 贝塞尔曲线 */
lineTo2(data) {
let arr = this.curveArr;// 重新声明一个变量存储
let conNum = arr.length % 2 ? (arr.length + 1) / 2 : arr.length / 2;
conNum = conNum - 1;
/* 获取中间那个的数组 */
this.ctx.lineCap = "round"; //设置线条的结束端点样式
this.ctx.moveTo(this.sX, this.sY); //起点
/* 假设 x1,y1 = 起始点; x4,y4 = 用户绘制中间点 ;
x3,y3 = 结束点; x2,y2 = 贝塞尔实际操作点
x4 = 2 * x2 - (x1 + x3) / 2
y4 = 2 * y2 - (y1 + y3) / 2
*/
let x2 = 2 * arr[conNum].x - (this.sX + data.x) / 2;
let y2 = 2 * arr[conNum].y - (this.sY + data.y) / 2;
this.ctx.quadraticCurveTo(x2, y2, data.x, data.y);
this.ctx.stroke();
this.curveArr = []; //每次结束都清空之前存坐标
},
实际效果 :PS 录屏工具的问题鼠标和实际位置有偏差 所以点了3个点确认点位
这个的话,小汪就想不到有啥办法能弄路径式的展现方法了,欢迎各位来探讨一下。
666
666