情况概述
坚信大伙儿平常在学习培训canvas 或 新项目开发设计中应用canvas的情况下应当都遇到过这样的要求:完成1个能够撰写的画板小专用工具。
嗯,坚信这对canvas应用较熟的童鞋来讲仅仅只是几10行编码便可以搞掂的事儿,下列demo便是1个再也简易但是的事例了:
<!DOCTYPE html> <html> <head> <title>Sketchpad demo</title> <style type="text/css"> canvas { border: 1px blue solid; } </style> </head> <body> <canvas id="canvas" width="800" height="500"></canvas> <script type="text/javascript"> let isDown = false; let beginPoint = null; const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); // 设定线条色调 ctx.strokeStyle = 'red'; ctx.lineWidth = 1; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; canvas.addEventListener('mousedown', down, false); canvas.addEventListener('mousemove', move, false); canvas.addEventListener('mouseup', up, false); canvas.addEventListener('mouseout', up, false); function down(evt) { isDown = true; beginPoint = getPos(evt); } function move(evt) { if (!isDown) return; const endPoint = getPos(evt); drawLine(beginPoint, endPoint); beginPoint = endPoint; } function up(evt) { if (!isDown) return; const endPoint = getPos(evt); drawLine(beginPoint, endPoint); beginPoint = null; isDown = false; } function getPos(evt) { return { x: evt.clientX, y: evt.clientY } } function drawLine(beginPoint, endPoint) { ctx.beginPath(); ctx.moveTo(beginPoint.x, beginPoint.y); ctx.lineTo(endPoint.x, endPoint.y); ctx.stroke(); ctx.closePath(); } </script> </body> </html>
它的完成逻辑性也很简易:
mousedown
、mouseup
和mousemove
,另外大家也建立了1个isDown
自变量;mousedown
,即起笔)时将isDown
置为true
,而放下电脑鼠标(mouseup
)的情况下将它置为false
,这样做的益处便是能够分辨客户当今是不是处在美术绘画情况;mousemove
恶性事件持续收集电脑鼠标历经的座标点,当且仅当isDown
为true
(即处在撰写情况)时将当今的点根据canvas的lineTo
方式与前面的点开展联接、绘图;根据以上几个流程大家便可以完成基础的画板作用了,但是事儿并没那末简易,细心的童鞋或许会发现1个很比较严重的难题——根据这类方法画出来的线条存在锯齿,不足光滑,并且你画得越快,折线感越强。主要表现以下图所示:
为何会这样呢?
难题剖析
出現该状况的缘故关键是:
大家是以canvas的lineTo
方式联接点的,联接邻近两点的是条平行线,非曲线图,因而根据这类方法绘图出来的是条折线;
受到限制于访问器对mousemove
恶性事件的收集频率,大伙儿都了解在mousemove
时,访问器是每隔1小段時间去收集当今电脑鼠标的座标的,因而电脑鼠标挪动的越快,收集的两个邻近点的间距就越远,故“折线感越显著“;
怎样才可以画出光滑的曲线图?
要画出光滑的曲线图,实际上也是有方式的,lineTo
靠不住那大家能够选用canvas的另外一个制图API——quadraticCurveTo
,它用于绘图2次贝塞尔曲线图。
2次贝塞尔曲线图
quadraticCurveTo(cp1x, cp1y, x, y)
启用quadraticCurveTo
方式必须4个主要参数,cp1x
、cp1y
叙述的是操纵点,而x
、y
则是曲线图的终点站:
更多详尽的信息内容可移步MDN
既然要应用贝塞尔曲线图,很明显大家的数据信息是不足用的,要详细叙述1个2次贝塞尔曲线图,大家必须:起止点、操纵点和终点站,这些数据信息如何来呢?
有1个凑巧妙的优化算法能够协助大家获得这些信息内容
获得2次贝塞尔重要点的优化算法
这个优化算法其实不难了解,这里我立即举事例吧:
假定大家在1次美术绘画中共收集到6个电脑鼠标座标,各自是A, B, C, D, E, F
;取前面的A, B, C
3点,测算出B
和C
的中点B1
,以A
为起始点,B
为操纵点,B1
为终点站,运用quadraticCurveTo
绘图1条2次贝塞尔曲线图直线;
接下来,测算得出C
与D
点的中点C1
,以B1
为起始点、C
为操纵点、C1
为终点站再次绘图曲线图;
先后类推持续绘图下去,当到最终1个点F
时,则以D
和E
的中点D1
为起始点,以E
为操纵点,F
为终点站完毕贝塞尔曲线图。
OK,优化算法便是这样,那大家根据该优化算法再对现有编码开展1次升級更新改造:
let isDown = false; let points = []; let beginPoint = null; const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); // 设定线条色调 ctx.strokeStyle = 'red'; ctx.lineWidth = 1; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; canvas.addEventListener('mousedown', down, false); canvas.addEventListener('mousemove', move, false); canvas.addEventListener('mouseup', up, false); canvas.addEventListener('mouseout', up, false); function down(evt) { isDown = true; const { x, y } = getPos(evt); points.push({x, y}); beginPoint = {x, y}; } function move(evt) { if (!isDown) return; const { x, y } = getPos(evt); points.push({x, y}); if (points.length > 3) { const lastTwoPoints = points.slice(⑵); const controlPoint = lastTwoPoints[0]; const endPoint = { x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2, y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2, } drawLine(beginPoint, controlPoint, endPoint); beginPoint = endPoint; } } function up(evt) { if (!isDown) return; const { x, y } = getPos(evt); points.push({x, y}); if (points.length > 3) { const lastTwoPoints = points.slice(⑵); const controlPoint = lastTwoPoints[0]; const endPoint = lastTwoPoints[1]; drawLine(beginPoint, controlPoint, endPoint); } beginPoint = null; isDown = false; points = []; } function getPos(evt) { return { x: evt.clientX, y: evt.clientY } } function drawLine(beginPoint, controlPoint, endPoint) { ctx.beginPath(); ctx.moveTo(beginPoint.x, beginPoint.y); ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y); ctx.stroke(); ctx.closePath(); }
在原来的基本上,大家建立了1个自变量points
用于储存以前mousemove
恶性事件中电脑鼠标历经的点,依据该优化算法可知要绘图2次贝塞尔曲线图至少必须3个点以上,因而大家仅有在points
中的点数超过3时才刚开始绘图。接下来的解决就跟该优化算法1毛1样了,这里已不赘述。
编码升级后大家的曲线图也变得光滑了很多,以下图所示:
本文到这里就完毕了,期待大伙儿在canvas画板中“画”得愉快~大家下一次再见了:)
感兴趣爱好的童鞋可戳这里关心我的blog,任何新鮮好玩的博文可能第1時间共享到这儿哦~
以上便是本文的所有內容,期待对大伙儿的学习培训有一定的协助,也期待大伙儿多多适用脚本制作之家。