Open ajccom opened 8 years ago
在思考如何对线段进行噪化的时候,我设想了一种方案 —— 在直线上获取的 N 个点并用光滑的曲线连接点与点,使之变成一条不规则的波浪线。
下面我就来讲述如何才能实现这个效果。
我设计了一种基于二次贝塞尔曲线的解决方案,即通过二次贝塞尔曲线去连接分割直线后的 N 个散列点,所以我需要完成如下几个步骤才能得到最终效果:
为了计算简单,我设计了一种对线段进行等分的散列点获取函数,它会返回被分割后线段所包含的所有的点。
/** * _getNoisePath 将路径噪化处理 * @param {Number} dist 路径长度 * @param {Number} interval 噪化点之间的间隙 * @param {Number} f 幅度 * @return {Array} 噪化路径的点集合 * * 处理前需要将画布原点移动到路径起始点位置且旋转画布使x轴与线段重合 */ function _getNoisePath (dist, interval, f) { var i = 1, l = Math.floor(dist / interval), d, result = [[0, 0]] for (i; i < l; i++) { result.push([interval * i, f * Math.random() * (Math.random() > 0.5 ? -1 : 1)]) } result.push([dist, 0]) return result }
这个函数有个前提设定,即设定我们的线段是一条从原点开始并与 X 轴重合的水平线。这是为了方便处理数据,而后期在绘制的时候,我们需要先对坐标轴进行变换,将坐标轴的 X 轴与线段重合并且原点 (0, 0) 处与线段起点重合。
另外,函数中的 f 参数表示振幅,即该点可能偏离原线段路径的可能偏移值。
f
为了能够使曲线与曲线之间可以光滑的连接在一起,我们需要找到特定的二次贝塞尔曲线的控制点。而这个控制点,就是上一条曲线的控制点相对该曲线的终点的对称点。
/** * _getSymmetryPoint 获取对称控制点 * @param {Array} controlPoint 当前路径控制点 * @param {Array} endPoint 当前路径终点 * @return {Array} 对称点 */ function _getSymmetryPoint (controlPoint, endPoint) { var x, y, x1 = controlPoint[0], y1 = controlPoint[1], x2 = endPoint[0], y2 = endPoint[1] x = 2 * x2 - x1 y = 2 * y2 - y1 return [x, y] }
有了上面两个函数的铺垫,绘制出我们需要的最终效果就易如反掌了。
var ctx = canvas.getContext('2d'), width = canvas.width, height = canvas.height //假设线段是由点 (100, 100) 和 点 (400, 400) 组成 //注意这里的 prevCtrl 变量,赋值的值是第一个曲线的控制点 var path = _getNoisePath(Math.sqrt(90000 + 90000), 100, 40), prevCtrl = [(path[1][0] - path[0][0]) / 2, 40] ctx.beginPath() ctx.translate(100, 100) ctx.rotate(Math.atan(300/300)) ctx.moveTo(0, 0) // 坐标轴变换的好处多多 path.shift() path.map(function (p, i) { ctx.quadraticCurveTo(prevCtrl[0], prevCtrl[1], p[0], p[1]) prevCtrl = _getSymmetryPoint(prevCtrl, p) //获取下一条曲线的控制点 }) ctx.stroke() ctx.closePath()
查看 DEMO
在日常生活中对“斜”着的线做计算总是很复杂,需要 X 轴与 Y 轴共同参与计算,而且两点之间的距离公式(a^2 + b^2 = c^2)也比较复杂。
而通过坐标系变换后,平移画布至线段起点,使画布原点与线段起点重合。再旋转画布,使 X 轴与线段重合,这样在计算线段本身长度(仅需要 X 轴参与计算)或垂直于该线段的线段的长度( Y 轴的值)时,取值都很简单方便。
当然了,你需要多做的就是平移和旋转坐标系。
如何将 N 个点用光滑的曲线连接
在思考如何对线段进行噪化的时候,我设想了一种方案 —— 在直线上获取的 N 个点并用光滑的曲线连接点与点,使之变成一条不规则的波浪线。
对比效果展示
线段噪化前后对比
下面我就来讲述如何才能实现这个效果。
实现方案
我设计了一种基于二次贝塞尔曲线的解决方案,即通过二次贝塞尔曲线去连接分割直线后的 N 个散列点,所以我需要完成如下几个步骤才能得到最终效果:
获取散列点
为了计算简单,我设计了一种对线段进行等分的散列点获取函数,它会返回被分割后线段所包含的所有的点。
这个函数有个前提设定,即设定我们的线段是一条从原点开始并与 X 轴重合的水平线。这是为了方便处理数据,而后期在绘制的时候,我们需要先对坐标轴进行变换,将坐标轴的 X 轴与线段重合并且原点 (0, 0) 处与线段起点重合。
另外,函数中的
f
参数表示振幅,即该点可能偏离原线段路径的可能偏移值。获取每条曲线的特定控制点
为了能够使曲线与曲线之间可以光滑的连接在一起,我们需要找到特定的二次贝塞尔曲线的控制点。而这个控制点,就是上一条曲线的控制点相对该曲线的终点的对称点。
当前曲线的控制点与前一个曲线的终点和控制点的关系
绘制
有了上面两个函数的铺垫,绘制出我们需要的最终效果就易如反掌了。
查看 DEMO
扩展阅读 - 坐标系变换
在日常生活中对“斜”着的线做计算总是很复杂,需要 X 轴与 Y 轴共同参与计算,而且两点之间的距离公式(a^2 + b^2 = c^2)也比较复杂。
而通过坐标系变换后,平移画布至线段起点,使画布原点与线段起点重合。再旋转画布,使 X 轴与线段重合,这样在计算线段本身长度(仅需要 X 轴参与计算)或垂直于该线段的线段的长度( Y 轴的值)时,取值都很简单方便。
当然了,你需要多做的就是平移和旋转坐标系。
Thanks