canvas画布曲线(贝塞尔曲线)优化
发布于 3 年前 作者 mxue 2657 次浏览 来自 分享

上一篇文章小汪写到用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个点确认点位

这个的话,小汪就想不到有啥办法能弄路径式的展现方法了,欢迎各位来探讨一下。

2 回复

其实这么做还是有缺陷的,因为手指触摸移动 如果一会快 一会慢,就会导致线位置有偏差,用攻略1点击方式与2拖拽方式计算点位这个结合我觉得还是好用一些

回到顶部