背景提要
置信人人日常平凡在进修canvas 或 项目开辟中运用canvas的时刻应当都遇到过如许的需求:完成一个能够誊写的画板小工具。
嗯,置信这对canvas运用较熟的童鞋来讲仅仅只是几十行代码就能够搞掂的事变,以下demo就是一个再也简朴不过的例子了:
<!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>
它的完成逻辑也很简朴:
我们在canvas画布上重要监听了三个事宜:mousedown、mouseup和mousemove,同时我们也创建了一个isDown变量;
当用户按下鼠标(mousedown,即起笔)时将isDown置为true,而放下鼠标(mouseup)的时刻将它置为false,如许做的优点就是能够推断用户当前是不是处于绘画状况;
经由过程mousemove事宜不停收集鼠标经由的坐标点,当且仅当isDown为true(即处于誊写状况)时将当前的点经由过程canvas的lineTo要领与前面的点举行衔接、绘制;
经由过程以上几个步骤我们就能够完成基础的画板功用了,但是事变并没那末简朴,细致的童鞋或许会发明一个很严重的题目——经由过程这类体式格局画出来的线条存在锯齿,不够腻滑,而且你画得越快,折线感越强。表现如下图所示:
为何会如许呢?
题目剖析
涌现该征象的缘由重如果:
我们是以canvas的lineTo要领衔接点的,衔接相邻两点的是条直线,非曲线,因而经由过程这类体式格局绘制出来的是条折线;
受限于浏览器对mousemove事宜的收集频次,人人都晓得在mousemove时,浏览器是每隔一小段时候去收集当前鼠标的坐标的,因而鼠标挪动的越快,收集的两个邻近点的间隔就越远,故“折线感越显著“;
怎样才画出腻滑的曲线?
要画出腻滑的曲线,实在也是有要领的,lineTo靠不住那我们能够采纳canvas的另一个画图API——quadraticCurveTo ,它用于绘制二次贝塞尔曲线。
二次贝塞尔曲线
quadraticCurveTo(cp1x, cp1y, x, y)
挪用quadraticCurveTo要领须要四个参数,cp1x、cp1y形貌的是控制点,而x、y则是曲线的尽头:
更多细致的信息可移步MDN
既然要运用贝塞尔曲线,很显然我们的数据是不够用的,要完全形貌一个二次贝塞尔曲线,我们须要:起始点、控制点和尽头,这些数据怎样来呢?
有一个很奇妙的算法能够协助我们猎取这些信息
猎取二次贝塞尔症结点的算法
这个算法并不难理解,这里我直接举例子吧:
假定我们在一次绘画中共收集到6个鼠标坐标,分别是A, B, C, D, E, F;
取前面的A, B, C三点,盘算出B和C的中点B1,以A为出发点,B为控制点,B1为尽头,应用quadraticCurveTo绘制一条二次贝塞尔曲线线段;
接下来,盘算得出C与D点的中点C1,以B1为出发点、C为控制点、C1为尽头继承绘制曲线;
顺次类推不停绘制下去,当到末了一个点F时,则以D和E的中点D1为出发点,以E为控制点,F为尽头完毕贝塞尔曲线。
OK,算法就是如许,那我们基于该算法再对现有代码举行一次升级革新:
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(-2); 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(-2); 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(); }
在原有的基础上,我们创建了一个变量points用于保留之前mousemove事宜中鼠标经由的点,依据该算法可知要绘制二次贝塞尔曲线最少须要3个点以上,因而我们只要在points中的点数大于3时才最先绘制。接下来的处置惩罚就跟该算法一毛一样了,这里不再赘述。
代码更新后我们的曲线也变得腻滑了很多,如下图所示:
本文到这里就完毕了,愿望人人在canvas画板中“画”得兴奋~我们下次再会:)
以上就是怎样运用canvas画出腻滑的曲线?(代码)的细致内容,更多请关注ki4网别的相干文章!