探索canvas画布绘制技术
图片来自KrzysztofBanaś
下面我们开始尝试研究不同的绘图风格和技术 - 边缘平滑,贝塞尔曲线,墨水和粉笔,笔和印章和图案 -等等。事实证明,网上没有太多关于此的内容。在下面的示例中,您请大家查看演示源代码,以便了解正在发生的事情。
这篇教程将带您从基础知识(在画布上绘制原始鼠标跟随线),一直到那些和谐画笔,以及复杂的曲线和笔触,从边缘跨越并卷曲成奇怪美丽的结构。
下面我将介绍不同的刷子代码实现,以便您可以自己了解如何在画布上实现自由绘图。
基本
所以让我们从一个非常基本的方法开始。
简单的铅笔
我们在画布上观察“mousedown”,“mousemove”和“mouseup”事件。
在“mousedown”上,我们将指针移动到单击的坐标(ctx.moveTo
)。在“mousemove”上,我们画一条线到鼠标的新坐标(ctx.lineTo
)。最后,在“mouseup”上,我们通过将isDrawing
flag 设置为false来结束绘图。
此标志用于防止在画布上移动鼠标时进行绘制(没有单击画布的情况)。您可以通过在“onmousedown”中分配“onmousemove”事件处理程序(然后在“onmouseup”中删除它)来避免标记,但flag是一个简单的解决方案,也可以正常工作。
设置光滑连接
现在,我们可以通过改变值来控制线的厚度ctx.lineWidth
。但是,粗线造成锯齿状的边缘。
这通常发生在“急转弯”的位置,并且可以通过设置ctx.lineJoin
和ctx.lineCap成"round"
来解决(有关这些如何影响渲染的示例,请参阅MDN )。
借助阴影使边缘平滑
现在线条没有在拐角周围出现锯齿。但它们的边缘也不是很光滑。这是因为这里没有抗锯齿化(在canvas上控制抗锯齿从未如此简单)。那么我们如何效仿呢?
有种方法就是借助阴影使边缘平滑。
我们添加的内容都是ctx.shadowBlur
和ctx.shadowColor
。边缘现在肯定更平滑,因为线条被阴影包围。但还是有一点问题:你会注意到线条在开始时比较薄和模糊,但是随着绘制的内容增加逐渐变得更厚更坚固。
这是一个有趣的效果,但也许完全不是我们想要的。那么为什么会这样呢?
原来这是由于阴影相互重叠,每增加一个点都重新stroke了,这样当前笔划的阴影与前一笔划的阴影重叠,后者与前一笔划的阴影重叠,依此类推。阴影越重叠,模糊越少,线越粗。那么我们如何解决这个问题呢?
基于点的方法
避免这类问题的一种方法是始终stroke一次。我们可以在一个数组中引入一个容器存储鼠标点,并且一次性stroke它们。而不是盲目地在每一次接收到鼠标mousemove的时候都去stroke。
如您所见,它看起来与第一个示例效果相同。现在我们可以尝试在此基础上添加阴影。注意它在整个路径中是如何保持均匀的。
基于点的阴影
边缘平滑与径向渐变
另一种平滑的途径是使用径向渐变。渐变允许更均匀的颜色分布,不像阴影通常比“平滑”更模糊。
但是,正如您所看到的,stroke使用渐变还有其他问题。请注意我们如何在每个鼠标移动点上使用圆形渐变填充区域的。当快速移动鼠标时,我们得到一系列断开连接的圆,而不是具有光滑边缘的直线。
解决此问题的一种方法是在两点间隔距离大时生成额外的点来填补断开的空间。
最后这是一条相当平滑的曲线!
您可能会注意到上面示例中的一个小变化。我们只存储上一个点,而不是存储路径的所有点。我们总是stroke从上一个点到当前的一个点。
有了上一个点那我们需要计算它与当前点之间的距离。如果距离太大,我们就在其中填充更多。这种方法的好处是我们不用存储整个points
阵列,这样就减少了内存的使用!
贝塞尔曲线
我遇到的一个有趣的概念是使用bezier线而不是直线。这允许自由绘制路径的曲线更自然更平滑。
这个想法的实现是使用quadraticCurveTo来替换straight-line,
用两个连续点之间的中间点作为二次贝塞尔曲线控制点。来试试吧:
到现在你已经知道:绘图和平滑曲线的一些基本知识。从简单的连线到更复杂的基于贝塞尔的曲线。让我们继续前进发现更有趣的事情吧。
刷子,毛皮,笔
刷子
现实的画笔工具箱中的一个技巧是简单地用图像stroke。我在 blog post by Andrew Trice.遇到了这种技巧。这种想法是使用一小部分图像作为画笔去stroke。这开启了很多可能性。
根据图像,我们可以实现不同的画笔样式。在这种情况下,它类似于厚刷。
毛皮(旋转画布)
跟前面一样用绘制图片方式填充路径,但每次绘制时生成随机的角度值旋转一下画布。
如果我们这样做,我们可以得到类似毛皮(或花环)一样的东西。
笔(变化宽度)
当谈到模拟钢笔写字时,一个很好的解决方案是简单地随机变化路径的线宽!我们仍然是结合moveTo
+ lineTo
的方式,但每次去更改“lineWidth”绘制出来的线是什么样子呢?这是它的外观:
要记住的一件事是,为了使绘图看起来真实,随机值差别不应该太大。
笔2(多线条)
另一模拟钢笔的实现是通过多笔触完成的。我们在点与点之间连线stroke多次,而不是仅仅一次。
但是我们并不是在同一个坐标位置重复画线,因为这不会改变任何事情。
相反,我们在原始(图片上的绿点)位置旁边取几个随机点(图片上的蓝点),然后从那里开始。因此,不是一行,而是在原始线旁边增加了两天“凌乱”的线条。完美的钢笔模拟!
厚刷
你可以用这种“多笔触”技术做很多事情。我鼓励大家尝试自己的想法。
这里有一个例子,如果我们增加线条宽度并稍加修正第条线的偏移量,我们可以模拟一个厚刷子。边缘上的那些空白点使其看起来很逼真。
“切片”笔触
多笔触的方法中如果使用均匀的小点的偏移量,我们可以得到类似于切片的东西。
这一次,不使用图像。
简单的方法让路径呈现偏斜效果。
带有透明度的“切片”笔触
如果我们使用与前面示例中相同的画笔,并给每次笔触依次变小的透明度,我们会得到一个像这样的有趣效果。
多行线条
笔直线条用够了。我们是否可以将相同的技术应用于基于贝塞尔曲线的路径呢?当然。我们只需要在偏离原始点一定距离的位置绘制每条曲线。这就是它的样子:
带有透明度的多行线条
我们也可以使用相同的“褪色”技术,让其中每条线的不透明度依次降低。这使得这些线条看起来更加简洁优雅。
与直笔触一样,使用贝塞尔曲线的可能性是无尽的。
印记样
基本概念
在我们学会了如何划线和曲线后,实现印章刷不是更简单!我们所需要的只是在每次鼠标移动时,在鼠标的位置绘制某种形状。而已。这是一个用红色圆圈标记的例子。
追踪效果
您可以看到中间点的相同问题,我们可以使用相同的预填充技术解决这些问题。邮票的预填充往往会产生非常有趣的线索或管状效果。您可以通过在最后一个点和当前点之间预先填充每个点的间隔来控制管的密度。
随机半径,不透明度
当然,我们总是可以调情,以某种方式改变每个邮票。例如,第一个例子中随机变化的半径和不透明度就是这个。
形状
当涉及到那种邮票时,你可以尽可能地去 - 从我们刚看到的基本形状(例如圆圈)到由数百或数千条曲线组成的更复杂的路径。这里唯一的限制因素是性能。这是一个用简单的五角星冲压的例子。
旋转形状
这是同一颗星,但每次移动都会随机旋转,以获得更自然的感觉。
随机化一切!
哎呀,让我们放大一点 - 尺寸,角度,不透明度,颜色,厚度!现在不是那么有趣。
彩色像素
我们也不仅限于形状。一种选择是直接操纵鼠标点周围的像素。一个简单的例子就是随机化它们的颜色和位置。
基于图案的画笔
现在我们已经过了抚摸和冲压,让我们来看看一个完全不同的野兽模式。我们可以使用canvas'随时createPattern
填充路径。这会产生一些非常有趣的效果。我们来看一个简单的点图案。
圆点图案
注意这里是如何创建模式的。我们将实例化迷你画布,在其上绘制圆圈,然后将该画布用作主画布上的图案!我们可能也习惯使用普通图像,但使用canvas的美妙之处在于我们可以通过编程方式访问它,并且无论如何我都可以更改它。这意味着我们可以创建动态模式,例如改变模式中圆的颜色,半径等。这也意味着我们可以更快更容易地尝试模式。
线条图案
根据前面的示例,您应该能够创建类似的东西。让我们说一个水平线条图案。
双色线条图案
...或垂直线条,具有互换的颜色。
彩虹
......甚至是多条不同颜色的线条。一切皆有可能。想想一些模式,并尝试在迷你画布上创建它。剩下的就是照顾createPattern
和填充路径。
图片
最后,这是一个使用基于图像的模式与贝塞尔曲线路径一起使用的示例。这里改变的是我们将图像对象传递给createPattern
(然后将结果模式分配给strokeStyle
)。
喷雾
现在goold-old喷刷怎么样?我们可以通过几种方式实现它。其中之一是用颜色简单地填充鼠标点周围的区域(像素)。面积(半径)越大,喷雾越厚。我们填充的像素越多,它就越密集。
基于时间的喷雾
您可能会注意到,之前的方法并不像真正的喷雾那样。一个真正的喷漆面积不断,不只是当我们移动鼠标/刷。为了实现这一点,我们需要在按下鼠标时以恒定间隔绘制区域。这样,某些区域可以通过更长时间“保持喷雾”而变得更暗。
基于时间的喷雾,圆形分布
前面的例子更现实,但并不完全如此。真正的喷涂油漆在圆形区域上,而不是矩形。所以让我们尝试在圆形区域上分布像素。
好多了。
随机点
最后,我们还能做些什么来使喷雾更逼真吗?当然,除了使用图像作为印章。我们当然可以使涂料偶尔散布,就像在现实生活中一样。如果我们改变每个绘制像素的不透明度,我们会得到非常相似的效果。
邻居点连接
连接邻居点的概念由zefrank的Scribbler和doob先生的Harmony推广。如果你还记得Harmony画笔,如粗略,阴影,铬色 - 这就是我所说的效果。
这个想法是:在已经绘制的路径的附近点之间添加额外的笔划。这通常会产生草图,网页或某种阴影的效果; 额外的笔划在小的“弯曲”区域增加了较暗点的错觉。
全点连接
一个天真的方法是采用我们的第一个简单的基于点的画笔的例子,并添加额外的抚摸。对于沿路径的每个点,我们将朝着路径上的前一个点冲程:
你可以开始看到类似Harmony画笔的东西,但它并不完全相同。通过降低额外笔画的不透明度(即对比度)可以使其更加逼真和阴影。但要完全重建效果,我们需要遵循不同的算法。
邻居点
负责“附近”抚摸的部分是这样的:
var lastPoint = points[points.length-1]; for (var i = 0, len = points.length; i < len; i++) {
dx = points[i].x - lastPoint.x;
dy = points[i].y - lastPoint.y;
d = dx * dx + dy * dy; if (d < 1000) {
ctx.beginPath();
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
ctx.moveTo(lastPoint.x + (dx * 0.2), lastPoint.y + (dy * 0.2));
ctx.lineTo(points[i].x - (dx * 0.2), points[i].y - (dy * 0.2));
ctx.stroke();
}
}
这里发生了什么?看起来很疯狂 我花了一些时间来理解,但这个概念非常简单!
绘制线时,我们检查已经绘制的路径的整个距离,将所有点与当前(最后一个)点进行比较。如果该点在d < 1000
最后一个点的某个接近度()中,我们移动指向它的指针并从那里划线到当前点。dx * 0.2
并dy * 0.2
给那些额外的笔画稍微偏移。
而已。简单的想法,强大的效果。
毛皮通过邻居点
这种技术的一个有趣的转折 - 在和谐中看到 - 是创造皮毛效果。不是向附近点(从最后一点)划动,而是向相反方向行程。稍微偏移,它会在某些(近距离)区域产生毛茸茸的笔触。
调查Harmony画笔后不久,我偶然发现了LukášTvrdý的精彩博客文章,很好地解释了邻居点技术的一些变化。他描述了不同参数如何影响笔画及其产生的效果类型。绝对值得一试。
所以你有它 - 一些基本的以及更有趣的绘图技术。我们这里只是划了一个表面。有无限的可能性来定制任何一个画笔,创造更多令人兴奋的效果。改变不透明度或颜色,宽度或偏移,引入随机因素,并产生全新的效果。
翻译优化中……
翻译原文:http://perfectionkills.com/exploring-canvas-drawing-techniques/
探索canvas画布绘制技术的更多相关文章
- 用html5的canvas画布绘制贝塞尔曲线
查看效果:http://keleyi.com/keleyi/phtml/html5/7.htm 完整代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...
- [技术博客]海报图片生成——小程序canvas画布
目录 背景介绍 canvas简介 代码实现 难点讲解 圆角矩形裁剪失败之PS的妙用 编码不要过硬 对过长的文字进行截取 真机首次生成时字体不对 drawImage只能使用本地图片 背景介绍 目标:利用 ...
- 软件项目技术点(7)——在canvas上绘制自定义图形
AxeSlide软件项目梳理 canvas绘图系列知识点整理 图形种类 目前我们软件可以绘制出来的形状有如下这几种,作为开发者我们一直想支持用户可以拖拽的类似word里面图形库,但目前还没有找到比 ...
- CANVAS画布与SVG的区别
CANVAS是html5提供的新元素<canvas>,而svg存在的历史要比canvas久远,svg并不是html5专有的标签,最初svg是用xml技术(超文本扩展语言,可以自定义标签或属 ...
- 微信小程序--canvas画布实现图片的编辑
技术:微信小程序 概述 上传图片,编辑图片大小,添加文字,改变文字颜色等 详细 代码下载:http://www.demodashi.com/demo/14789.html 概述 微信小程序--ca ...
- canvas离屏技术与放大镜实现
教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步>>> (原文)canvas 离屏技术与放大镜实现. 更多讨论或者错误提交,也请移步. 利用canvas除了可以实现 ...
- canvas高效绘制10万图形,你必须知道的高效绘制技巧
最近的一个客户项目中,简化的需求是绘制按照行列绘制很多个圆圈.需求看起来不难,上手就可以做,写两个for循环. 原始绘制方法 首先定义了很多Circle对象,在遍历循环中调用该对象的draw方法.代码 ...
- 用canvas画布画一个画板
前段时间,在对H5的回顾中突然对canvas有了感觉,闲来无事便对其进行了一些捯饬.这不,上周我还做了一个好玩的画板呢,废话不多说,直接上代码(PS:翠花,上代码~): HTML部分: <!DO ...
- 清除canvas画布内容--点擦除+线擦除
清空canvas画布内容 1.重置宽或高 由于canvas每当高度或宽度被重设时,画布内容就会被清空,因此可以用以下方法清空:(此方法仅限需要清除全部内容的情况) var c=document.get ...
随机推荐
- [redis]复制机制,调优,故障排查
在redis的安装目录下首先启动一个redis服务,使用默认的配置文件,作为主服务 ubuntu@slave1:~/redis2$ ./redis-server ./redis.conf & ...
- 2016级算法期末模拟练习赛-A.wuli51和京导的毕业旅行
1063 wuli51和京导的毕业旅行 思路 中等题,二分+贪心. 简化题意,将m+1个数字分成n份,ans为这n段中每段数字和的最大值,求ans最小值及其方案. 对于这种求最小的最大值,最常用的方法 ...
- create-react-app安装出错问题解决
在用create-react-app的时候 报错 错误如下图: 在SF上查到说是或许是因为国内npm拉去资源,拉去不到的问题,可以试着从解决创建create-react-app慢的方法着手: 解决方法 ...
- [转] linux alias 编写 函数 脚本
[From] https://blog.csdn.net/csdnmonkey/article/details/53286314 案例 alias ttt='ttt(){ echo $1 ; };tt ...
- Q147 对链表进行插入排序
插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插入排序算法: 插入排序 ...
- docker + nginx 部署vuejs3.0项目
1:用指令 npm run build 打包vusjs项目(该项目是在github上下载的).打包成功后会生成一个目录dist. 2:把该文件夹拷贝到腾讯云服务器(操作系统 centos7)下的/us ...
- MIME类型是什么?包含哪些类型?
摘自:http://www.tuidc.com/idczixun/newsx/newsidc/3479.html 问:MIME类型是什么? 答:MIME(Multipurpose Interne ...
- C# 文件读写系列三
1.读写文本文件 在C# 文件读写系列二中列举了相当多的读写文本文件的方法,大致有以下几种: (1).通过静态类File的静态方法来进行文本文件的读写,主要有ReadAllBytes().ReadAl ...
- HTTP传输数据压缩
一.基础 1.HTTP压缩是指: Web服务器和浏览器之间压缩传输的”文本内容“的方法. HTTP采用通用的压缩算法,比如gzip来压缩HTML,Javascript, CSS文件. 能大大减少网络传 ...
- Mysql5.7 半同步改进
Mysql5.6半同步策略 Mysql 5.6在半同步的时候,采用的是After Commit策略.即在主库上commit了之后,等待从库返回确认. 在这里,首先会出现幻读的问题,即当前连接的事务读取 ...