使用Canvas绘制完美的不完美圆形
真实世界是不完美的,当我们需要模拟真实世界时,经常需要引入不完美/不规则的形状。比如陨石、雨滴、行星、树叶、绵延的海岸线、云朵等。本文介绍如何基于Canvas生成随机大小的不完美圆形。
首先我们要了解,在几何学上圆形可以通过增加等边对称多边形边数来无限逼近。
那么所谓不完美圆形,实际可以通过一个不等边不对称的多边形来实现。
要实现不等边不对称,一个简单的方法是使多边形各个顶点距离中心点的距离(即半径)为一个随机值就好。
为避免半径落差过大,我们可以给其设定一个最大和最小值,然后在这个区间进行随机,代码如下:
function drawCircle(centerX, centerY, minRad, maxRad) { var points = 512; //多边形边的总数目 var rad, theta; var twoPi = 2 * Math.PI; var x0, y0; context.strokeStyle = "#aa6699"; context.lineWidth = 1.01; context.fillStyle = "#6633aa"; context.beginPath(); theta = 0; x0 = centerX + rad * Math.cos(theta); y0 = centerY + rad * Math.sin(theta); context.lineTo(x0, y0); for (var i = 0; i < points; i++) { theta += twoPi / points; rad = minRad + Math.random() * (maxRad - minRad); //随机半径 x0 = centerX + rad * Math.cos(theta); y0 = centerY + rad * Math.sin(theta); context.lineTo(x0, y0); } context.stroke(); context.fill(); }
上面的代码实现效果如下:
上面这样的图形可以用来模拟松果、毛线球、刺猬等物体。
但如果想模拟海岸线、雨滴、云朵等线条比较柔和的物体,则显然不能满足要求。
我们需要边沿有一个平滑的过渡,而分形算法刚好可以用来完成这个任务。
我们假设半径的长度为1,我们来对这个区间进行细分,第1步在线段中间添加一个节点把区间分成2段,随机一个y坐标,第2步在左右半区间内重复类似操作,如此反复,直到达到预先设定的细分粒度。
为了避免线条的起伏过大,我们在给新增中间节点设定y坐标时,使其和所在细分线段的长度正相关,这样随着细分粒度的提高,局部区域的波动就越小,就形成了一个平滑过渡的效果,代码如下:
function setLinePoints(iterations) { var pointList = {}; pointList.first = { x: 0, y: 1 }; var lastPoint = { x: 1, y: 1 } var minY = 1; var maxY = 1; var point; var nextPoint; var dx, newX, newY; pointList.first.next = lastPoint; for (var i = 0; i < iterations; i++) { point = pointList.first; while (point.next != null) { nextPoint = point.next; dx = nextPoint.x - point.x; newX = 0.5 * (point.x + nextPoint.x); newY = 0.5 * (point.y + nextPoint.y); newY += dx * (Math.random() * 2 - 1); var newPoint = { x: newX, y: newY }; //min, max if (newY < minY) { minY = newY; } else if (newY > maxY) { maxY = newY; } //put between points newPoint.next = nextPoint; point.next = newPoint; point = nextPoint; } } var normalizeRate = 1 / (maxY - minY); point = pointList.first; while (point != null) { point.y = normalizeRate * (point.y - minY); point = point.next; } return pointList; } function drawCircle(centerX, centerY, minRad, maxRad, phase) { var point; var rad, theta; var twoPi = 2 * Math.PI; var x0, y0; //生成分形细分顶点链表,用来获取随机半径, 9次迭代将返回512个顶点。 var pointList = setLinePoints(9); context.strokeStyle = "#aa6699"; context.lineWidth = 1.01; context.fillStyle = "#6633aa"; context.beginPath(); point = pointList.first; theta = phase; rad = minRad + point.y * (maxRad - minRad); x0 = centerX + rad * Math.cos(theta); y0 = centerY + rad * Math.sin(theta); context.lineTo(x0, y0); while (point.next != null) { point = point.next; theta = twoPi * point.x + phase; rad = minRad + point.y * (maxRad - minRad); x0 = centerX + rad * Math.cos(theta); y0 = centerY + rad * Math.sin(theta); context.lineTo(x0, y0); } context.stroke(); context.fill(); }
上面的代码实现效果如下:
很好,我们获得了一个完美的不完美圆形!
最新评论
- 相关文章
谷歌ARCore技术特性简介
谷歌美国时间2017.8.29号刚发布了ARCore预览版,这是一个类似于苹果ARKit的增强现实SDK,在此之前,谷歌虽然已投资AR平台Tango,但由于需要特定的硬件和传感器,...
WebGL Roadmap
Unity 5.0 shipped with a working preview of our WebGL technology in March this year. Since then, Google has disabled (by default) NPAPI support in the...
使用HTML5 FileReader和Canvas压缩用户上传的图片
手机用户拍的照片通常会有2M以上,这对服务器带宽产生较大压力。因此在某些应用下(对图片要求不那么高)我们可以在客户端来压缩图片,然后再提交给服务器。总体...
HTML5动画背后的数学2 - 仿生智能算法综述
学习使用CSS制作进度条
进度条是基础的界面控件,可用于多种场合,比如任务完成进度,手机充电状态等。本文介绍一个简单实用的进度条制作方法。预期效果如下图所示:直观上,我们可以把该进度条控件分为2个部分,外部的边界用来表示固定的目标范围,里面的条形部分用来表示当前进度。外部目标范围元素的CSS代码编写如下:.pb-scope
计算WebGL中的uniforms变量使用数
在使用Three.js为人体模型加载皮肤材料时,启用了skinning:true的参数。有时候会导致GL编译错误,提示“too many uniforms”。下面的文章有助于理解错误原因和检...
Three.js入门教程5 - 10个必须知道的编程技巧
作者为Google的Paul,关于如何写出好的WebGL代码的文章。和很多开发者一样,我通过实践学习,但同时我也向其他更有经验的开发者们学习。在过去的几个月中,我在c...
Three.js入门教程2 - 着色器(上)
CSS3图片混合(Blend)效果及其参考计算公式一览表
在Photoshop软件中,混合是将两个图层的色彩值进行合成,从而创造出大量的效果。在这些效果的背后实际是一些简单的数学公式在起作用。下面所介绍的公式仅适用于R...
如何实现SVG clipPath自适应被裁剪对象
CSS3中引入的clip-path(裁剪路径)属性是一个很强大的特性。
clip-path的含义如下图所示,好比剪纸一样,你用剪刀沿着某条路径把目标对象(图像或元素)裁...WebGL入门教程4 - 使用纹理贴图(Texture Map)
3D建模和纹理贴图的关系就好比人体和皮肤(或着装)的关系,3D建模用来处理空间属性,而贴图适合用来处理细腻的表面属性。如果不使用贴图,而想在表面达到足够的...
使用top/left/margin和CSS3 translate两种方法实现标题居中的性能差异详解
要实现标题全屏居中(同时在垂直和水平方向居中),有若干种方法,包括使用弹性布局、表格单元、绝对定位、自动外边距和CSS3平移变换等。你可能已经使用了这些方...
Canvas实例教程:图像移动、大小调整和裁剪
本文介绍如何使用JavaScript和HTML5 Canvas元素来移动、调整大小和...
更多...