设为首页收藏本站
天天打卡

 找回密码
 立即注册
搜索
查看: 91|回复: 15

canvas小画板之平滑曲线的实现

[复制链接]
  • 打卡等级:无名新人
  • 打卡总天数:2
  • 打卡月天数:0
  • 打卡总奖励:34
  • 最近打卡:2024-05-17 13:30:07

2

主题

79

回帖

407

积分

中级会员

积分
407

热心会员付费会员

发表于 2024-4-20 10:31:56 | 显示全部楼层 |阅读模式
功能需求
项目需求:需要实现一个可以自由书写的小画板
简单实现
对于熟悉canvas的同学来说,这个需求很简单,大致逻辑如下:
1)监听事件pointerdown,pointermove,pointerup
2)标记是否拖拽画线模式变量 isDrawing,在down事件时置为true,up的时候置为false
3)使用canvas的api,设置线条样式,调用绘制线条接口lineTo方法
短短几十行代码就能实现:
  1. <!doctype html>
  2. <html>

  3. <head>
  4.     <meta charset=utf-8>
  5.     <style>
  6.         canvas {
  7.             border: 1px solid #ccc
  8.         }

  9.         body {
  10.             margin: 0;
  11.         }
  12.     </style>
  13. </head>

  14. <body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
  15.     <canvas id="c" width="1920" height="1080"></canvas>
  16.     <script>
  17.         var el = document.getElementById('c');
  18.         var ctx = el.getContext('2d');
  19.         //设置绘制线条样式
  20.         ctx.strokeStyle = 'red';
  21.         ctx.lineWidth = 1;
  22.         ctx.lineJoin = 'round';
  23.         ctx.lineCap = 'round';
  24.         var isDrawing;//标记是否要绘制
  25.         //存储坐标点
  26.         let lastX, lastY;
  27.         document.body.onpointerdown = function (e) {
  28.             console.log('pointerdown');
  29.             isDrawing = true;
  30.             lastX = e.clientX;
  31.             lastY = e.clientY;
  32.         };
  33.         document.body.onpointermove = function (e) {
  34.             console.log('pointermove');
  35.             if (isDrawing) {
  36.                 draw(e.clientX, e.clientY, lastX, lastY);
  37.             }
  38.             lastX = e.clientX, lastY = e.clientY;
  39.         };
  40.         document.body.onpointerup = function (e) {
  41.             if (isDrawing) {
  42.                 draw(e.clientX, e.clientY, lastX, lastY);
  43.             }
  44.             lastX = e.clientX, lastY = e.clientY;
  45.             isDrawing = false;
  46.         };

  47.         function draw(x, y, lastX, lastY) {
  48.             ctx.beginPath();
  49.             ctx.moveTo(lastX, lastY);
  50.             ctx.lineTo(x, y);
  51.             ctx.stroke();
  52.         }
  53.     </script>
  54. </body>
  55. </html>
复制代码
实现效果如下图:

以上就简单的实现了画板功能,如果要求不高的用户可以使用,但一旦遇到有点要求的用户就无法交付这种产品,仔细看是线条折线感太强。
为什么会有折线感呢?
主要原因:
我们调用的api方法lineTo是两点连线也就是直线
浏览器对鼠标事件mousemove的采集是有采集频率的,并不是每个鼠标移动经过的每一个像素点都会触发事件。
当鼠标移动的越快,那么两点之间的间隔就越远,那么折线感就更明显。

如何能绘制平滑的曲线?
canvas提供的api中是有现成接口的,贝塞尔系列的接口就能满足我们的要求,接下来我们讲一下使用二次贝塞尔曲线绘制平滑曲线。
quadraticCurveTo(cpx,cpy,x,y)
二次贝塞尔曲线接口需要四个参数,cpx,cpy是曲线的控制点,x,y是曲线终点。
有人问那曲线的起点在哪里?其实曲线的起点取决于上一操作状态,可以是moveTo的位置,或者是lineTo的位置,或者是贝塞尔的终点。
那么怎么调用quadraticCurveTo,参数怎么传呢?
我们需要找出关键位置,直接用例子告诉大家吧
1)假如我们用鼠标采集到ABCDEF六个点
2)取前面三个点ABC计算,BC的中点B1,以A为起点,B为控制点,B1为终点,那么利用quadraticCurveTo可以绘制出这样一条贝塞尔曲线

3)接下来计算CD的中点C1,以B1为起点,C为控制点,C1为终点,那么利用quadraticCurveTo可以绘制出这样一条贝塞尔曲线

4)以此类推,当到了最后一个点时以D1为起点,E为控制点,F为终点,结束贝塞尔绘制。

根据算法进行代码改造
OK我们介绍了具体算法的影响,那用该算法对我们前面的代码进行改造:
  1. <!doctype html>
  2. <html>

  3. <head>
  4.     <meta charset=utf-8>
  5.     <style>
  6.         canvas {
  7.             border: 1px solid #ccc
  8.         }

  9.         body {
  10.             margin: 0;
  11.         }
  12.     </style>
  13. </head>

  14. <body style="overflow: hidden;background-color: rgb(250, 250, 250);touch-action: none;">
  15.     <canvas id="c" width="1920" height="1080"></canvas>
  16.     <script>
  17.         var el = document.getElementById('c');
  18.         var ctx = el.getContext('2d');
  19.         //设置绘制线条样式
  20.         ctx.strokeStyle = 'red';
  21.         ctx.lineWidth = 1;
  22.         ctx.lineJoin = 'round';
  23.         ctx.lineCap = 'round';
  24.         var isDrawing;//标记是否要绘制
  25.         //存储坐标点
  26.         let points = [];
  27.         document.body.onpointerdown = function (e) {
  28.             console.log('pointerdown');
  29.             isDrawing = true;
  30.             points.push({ x: e.clientX, y: e.clientY });
  31.         };
  32.         document.body.onpointermove = function (e) {
  33.             console.log('pointermove');
  34.             if (isDrawing) {
  35.                 draw(e.clientX, e.clientY);
  36.             }

  37.         };
  38.         document.body.onpointerup = function (e) {
  39.             if (isDrawing) {
  40.                 draw(e.clientX, e.clientY);
  41.             }
  42.             points = [];
  43.             isDrawing = false;
  44.         };

  45.         function draw(mousex, mousey) {
  46.             points.push({ x: mousex, y: mousey });
  47.             ctx.beginPath();
  48.             let x = (points[points.length - 2].x + points[points.length - 1].x) / 2,
  49.                 y = (points[points.length - 2].y + points[points.length - 1].y) / 2;
  50.             if (points.length == 2) {
  51.                 ctx.moveTo(points[points.length - 2].x, points[points.length - 2].y);
  52.                 ctx.lineTo(x, y);
  53.             } else {
  54.                 let lastX = (points[points.length - 3].x + points[points.length - 2].x) / 2,
  55.                     lastY = (points[points.length - 3].y + points[points.length - 2].y) / 2;
  56.                 ctx.moveTo(lastX, lastY);
  57.                 ctx.quadraticCurveTo(points[points.length - 2].x, points[points.length - 2].y, x, y);
  58.             }
  59.             ctx.stroke();
  60.             points.slice(0, 1);

  61.         }
  62.     </script>
  63. </body>

  64. </html>
复制代码
在原有基础上我们用了一个数组points保存鼠标经过的点,根据算法可知绘制贝塞尔曲线至少要用三个点,绘制过程中维护points数组。
实现效果如下,可见平滑了很多!

后续文章:
实现蜡笔效果,实现笔锋效果,画笔性能优化
到此这篇关于canvas小画板之平滑曲线的实现的文章就介绍到这了,更多相关canvas平滑曲线内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

1

主题

54

回帖

133

积分

等待验证会员

积分
133

热心会员付费会员

发表于 2024-5-29 13:00:59 | 显示全部楼层
嘎嘎嘎嘎嘎嘎嘎

0

主题

45

回帖

91

积分

注册会员

积分
91
发表于 2024-6-2 16:56:07 | 显示全部楼层
6666666666

2

主题

58

回帖

141

积分

注册会员

积分
141
发表于 2024-6-11 20:48:30 | 显示全部楼层
确实牛逼

1

主题

55

回帖

133

积分

注册会员

积分
133
发表于 2024-6-14 04:12:38 | 显示全部楼层
友善的讨论氛围是非常重要的。

0

主题

57

回帖

115

积分

注册会员

积分
115
发表于 2024-7-7 19:15:38 | 显示全部楼层
我想了解更多

0

主题

35

回帖

67

积分

注册会员

积分
67
发表于 2024-7-22 10:47:52 | 显示全部楼层
我不确定这个信息的准确性,请再确认一下

3

主题

48

回帖

164

积分

注册会员

积分
164
发表于 2024-7-25 16:52:12 | 显示全部楼层
同意!
  • 打卡等级:初来乍到
  • 打卡总天数:6
  • 打卡月天数:0
  • 打卡总奖励:97
  • 最近打卡:2024-08-20 16:26:13

1

主题

66

回帖

323

积分

中级会员

积分
323
发表于 2024-8-19 01:40:46 | 显示全部楼层
说得太好了,完全同意!

0

主题

60

回帖

119

积分

注册会员

积分
119
发表于 2024-9-1 22:17:15 | 显示全部楼层
让我们一起努力
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|爱云论坛 - d.taiji888.cn - 技术学习 免费资源分享 ( 蜀ICP备2022010826号 )|天天打卡

GMT+8, 2024-11-15 17:00 , Processed in 0.101595 second(s), 28 queries .

Powered by i云网络 Licensed

© 2023-2028 正版授权

快速回复 返回顶部 返回列表