使用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(); }
上面的代码实现效果如下:
很好,我们获得了一个完美的不完美圆形!
最新评论
- 相关文章
OpenGL/WebGL顶点坐标变换过程简介
世界坐标是按照笛卡尔坐标系定义出来的绝对坐标系,下面的各种坐标系都建立在世界坐标的基础上。对象坐标系对象被应用于任何...
ARCore基本概念和工作原理简介
谷歌的WebAROnARCore项目基于Android手机提供的ARCore增强现实引擎,要了解WebAROnARCore,需要先了解ARCore的工作原理。基本上ARCore做了两件事,首先跟踪手机...
ES6小知识:动态对象键(Dynamic Object Keys)语法简介
在ES5,对象的键(key)总是被解释为字符串。ES6允许我们使用计算的值作为对象的键,使用方括号:[myKey]const
CSS3人行走动作图解和动画实现
对于人类而言,行走是一种很自然的想要前进并防止跌倒的一组动作重复。大部分人1岁就学会了走路,但至此以后的几十年间,或许我们从来没留意过自己行走姿势。当...
前端开发框架技术选型:Angular2 VS React VS jQuery
Angular和React是主流的2个前端开发框架,但是严格来说两者并非对等的概念。Angular是一个基于MVC(或者MVVM)的框架,包含model(模型)/view(视图)/controll...
如何使用CSS3合成模式(blend-mode)和滤镜(filter)实现彩色蜡笔(时光机)照片特效
在之前的文章中我们已经详细讲解过CSS3滤镜(filter,也可称之为过滤器)的工作方式,本文将实现一个当下流行的时光机相片特效实例来说明其实际用途。
我们...三维向量的简单运算和实用意义
在WebGL的实际应用中我们广泛使用向量的几何运算来计算角度、距离,判断点线、点面之间的关系,比如物体之间的碰撞检测。本文简要介绍三维计算机图形学中常用的...
粒子运动模拟 - Verlet积分算法简介
Verlet算法是经典力学(牛顿力学)中的一种最为普遍的积分方法,被广泛运用在分子运动模拟(Molecular Dynamics Simulation),行星运动以及织物变形模拟等领域...
如何使用Three.js加载obj和mtl文件
OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...
HTML网页布局:静态、自适应、流式、响应式
静态布局(Static Layout)即传统Web设计,对于PC设计一个Layout,在屏幕宽高有调整时,使用横向和竖向的滚动条来查阅被遮掩部分;对于移动设备,单独设计一个布...
Three.js 开发基础知识 - 绘制3D对象
Three.js是一个用来简化WebGL开发的JavaScript库,比如绘制一个三维立方体,使用WebGL需要100多行,那Three.js只要10几行就能够完成。本文通过创建一个立方体来...
使用requestAnimationFrame和Canvas给按钮添加绕边动画
要给按钮添加酷炫的绕边动画,可以使用Canvas来实现。基本的思路是创建一个和按钮大小相同的Canvas元素,内置在按钮元素中。然后在Canvas上实现边线环绕的动画。...
更多...