图片来自 KrzysztofBanaś
下面我们开始尝试研究不同的绘图风格和技术 - 边缘平滑, 贝塞尔曲线, 墨水和粉笔, 笔和印章和图案 - 等等. 事实证明, 网上没有太多关于此的内容. 在下面的示例中, 您请大家查看演示源代码, https://github.com/paulirish/harmony 以便了解正在发生的事情.
这篇教程将带您从基础知识(在画布上绘制原始鼠标跟随线), 一直到那些和谐画笔, 以及复杂的曲线和笔触, 从边缘跨越并卷曲成奇怪美丽的结构.
下面我将介绍不同的刷子代码实现, 以便您可以自己了解如何在画布上实现自由绘图.
当然, 在继续之前, 对 html5 画布有一定的了解是必要的.
基本
所以让我们从一个非常基本的方法开始.
简单的铅笔
我们在画布上观察 "mousedown","mousemove" 和 "mouseup" 事件.
在 "mousedown" 上, 我们将指针移动到单击的坐标(ctx.moveTo). 在 "mousemove" 上, 我们画一条线到鼠标的新坐标(ctx.lineTo). 最后, 在 "mouseup" 上, 我们通过将 isDrawingflag 设置为 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(多笔)
另一笔模拟是通过多次笔画完成的. 我们不再在点之间进行一次抚摸, 而是再增加 2 次传球. 但是我们不想在同一个地方罢工, 因为这不会改变任何事情. 相反, 我们在原始 (图片上的绿点) 旁边取几个随机点(图片上的蓝点), 然后从那里开始. 因此, 不是一行, 而是在原始行旁边 "划线" 抚摸 2 行. 完美的笔模拟!
厚刷
你可以用这种 "多冲程" 技术做很多事情. 我劝你尝试自己的变化. 这里有一个例子, 如果我们增加线条厚度并略微偏移第二道次, 我们可以模拟一个粗刷. 边缘上的那些空白点使其看起来很逼真.
"切片" 笔画
如果我们实现多个笔划, 但是在均匀和小的偏移处, 我们可以再次获得类似于切片的东西. 这一次, 不使用图像. 路径简直是偏斜的.
不透明度的 "切片" 笔划
如果我们使用与前面示例中相同的画笔, 并给每个笔画更小和更小的不透明度, 我们会得到一个像这样的有趣效果.
多行
但足够直冲. 我们可以将相同的技术应用于基于贝塞尔曲线的路径吗? 当然. 我们只需要在偏离原始点的位置绘制每条曲线. 这就是它的样子:
多行具有不透明度
我们也可以使用相同的 "褪色" 技术, 其中每条线的不透明度较低. 这使得这些线条看起来更加优雅.
与直笔画一样, 贝塞尔曲线的可能性是无穷无尽的.
邮票样
基本概念
在我们学会了如何划线和曲线后, 实现印章刷不是更简单! 我们所需要的只是在每次鼠标移动时, 在鼠标的位置绘制某种形状. 而已. 这是一个用红色圆圈标记的例子.
追踪效果
您可以看到中间点的相同问题, 我们可以使用相同的预填充技术解决这些问题. 邮票的预填充往往会产生非常有趣的线索或管状效果. 您可以通过在最后一个点和当前点之间预先填充每个点的间隔来控制管的密度.
随机半径, 不透明度
当然, 我们总是可以调情, 以某种方式改变每个邮票. 例如, 第一个例子中随机变化的半径和不透明度就是这个.
形状
当涉及到那种邮票时, 你可以尽可能地去 - 从我们刚看到的基本形状 (例如圆圈) 到由数百或数千条曲线组成的更复杂的路径. 这里唯一的限制因素是性能. 这是一个用简单的五角星冲压的例子.
旋转形状
这是同一颗星, 但每次移动都会随机旋转, 以获得更自然的感觉.
随机化一切!
哎呀, 让我们放大一点 - 尺寸, 角度, 不透明度, 颜色, 厚度! 现在不是那么有趣.
彩色像素
我们也不仅限于形状. 一种选择是直接操纵鼠标点周围的像素. 一个简单的例子就是随机化它们的颜色和位置.
基于图案的画笔
现在我们已经过了抚摸和冲压, 让我们来看看一个完全不同的野兽模式. 我们可以使用 canvas'随时 createPattern 填充路径. 这会产生一些非常有趣的效果. 我们来看一个简单的点图案.
圆点图案
注意这里是如何创建模式的. 我们将实例化迷你画布, 在其上绘制圆圈, 然后将该画布用作主画布上的图案! 我们可能也习惯使用普通图像, 但使用 canvas 的美妙之处在于我们可以通过编程方式访问它, 并且无论如何我都可以更改它. 这意味着我们可以创建动态模式, 例如改变模式中圆的颜色, 半径等. 这也意味着我们可以更快更容易地尝试模式.
线条图案
根据前面的示例, 您应该能够创建类似的东西. 让我们说一个水平线条图案.
双色线条图案
... 或垂直线条, 具有互换的颜色.
彩虹
...... 甚至是多条不同颜色的线条. 一切皆有可能. 想想一些模式, 并尝试在迷你画布上创建它. 剩下的就是照顾 createPattern 和填充路径.
图片
最后, 这是一个使用基于图像的模式与贝塞尔曲线路径一起使用的示例. 这里改变的是我们将图像对象传递给 createPattern(然后将结果模式分配给 strokeStyle).
喷雾
现在 goold-old 喷刷怎么样? 我们可以通过几种方式实现它. 其中之一是用颜色简单地填充鼠标点周围的区域 (像素). 面积(半径) 越大, 喷雾越厚. 我们填充的像素越多, 它就越密集.
基于时间的喷雾
您可能会注意到, 之前的方法并不像真正的喷雾那样. 一个真正的喷漆面积不断, 不只是当我们移动鼠标 / 刷. 为了实现这一点, 我们需要在按下鼠标时以恒定间隔绘制区域. 这样, 某些区域可以通过更长时间 "保持喷雾" 而变得更暗.
基于时间的喷雾, 圆形分布
前面的例子更现实, 但并不完全如此. 真正的喷涂油漆在圆形区域上, 而不是矩形. 所以让我们尝试在圆形区域上分布像素.
好多了.
随机点
最后, 我们还能做些什么来使喷雾更逼真吗? 当然, 除了使用图像作为印章. 我们当然可以使涂料偶尔散布, 就像在现实生活中一样. 如果我们改变每个绘制像素的不透明度, 我们会得到非常相似的效果.
邻居点连接
连接邻居点的概念由 zefrank 的 Scribbler http://www.zefrank.com/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://lukast.mediablog.sk/log/?p=347 精彩博客文章, 很好地解释了邻居点技术的一些变化. 他描述了不同参数如何影响笔画及其产生的效果类型. 绝对值得一试.
所以你有它 - 一些基本的以及更有趣的绘图技术. 我们这里只是划了一个表面. 有无限的可能性来定制任何一个画笔, 创造更多令人兴奋的效果. 改变不透明度或颜色, 宽度或偏移, 引入随机因素, 并产生全新的效果.
翻译优化中......
来源: https://www.cnblogs.com/fangsmile/p/9990318.html