使用Canvas绘制完美的不完美圆形

techbrood 发表于 2016-05-23 15:41:17

标签: canvas, html5, circle, fractal, 分形

- +

真实世界是不完美的,当我们需要模拟真实世界时,经常需要引入不完美/不规则的形状。比如陨石、雨滴、行星、树叶、绵延的海岸线、云朵等。本文介绍如何基于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();
}

上面的代码实现效果如下:

无标题.png

上面这样的图形可以用来模拟松果、毛线球、刺猬等物体。

但如果想模拟海岸线、雨滴、云朵等线条比较柔和的物体,则显然不能满足要求。

我们需要边沿有一个平滑的过渡,而分形算法刚好可以用来完成这个任务。

我们假设半径的长度为1,我们来对这个区间进行细分,第1步在线段中间添加一个节点把区间分成2段,随机一个y坐标,第2步在左右半区间内重复类似操作,如此反复,直到达到预先设定的细分粒度。


fractal subdivision

为了避免线条的起伏过大,我们在给新增中间节点设定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();
}

上面的代码实现效果如下:

blob.png

很好,我们获得了一个完美的不完美圆形!

你可以自己在线试试。

possitive(7) views16404 comments0

发送私信

最新评论

请先 登录 再评论.
相关文章
  • 微信公众号在线生成二维码带参数怎么搞?

    带参数二维码是微信公众号渠道二维码的一种实现
    微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,但是永久二维码的生成是有个数限制的,微...

  • CSS3特性查询(Feature Query: @supports)功能简介

    这是2017年不能不了解和学习的一个CSS新特性,非常实用,考虑到现实世界浏览器的复杂性,该特性本应该先于其他新特性出来。我们已经知道使用媒体查询(Media Que...

  • 使用CSS3 box-decoration-break特性实现多行文本样式

    当文章中的长文本被自动断行为多行文本时,其样式可能会出乎我们的设计。本文介绍如何使用CSS3中的box-decoration-break特性来处理多行元素样式。
    按照规范...

  • NodeJS、Java和PHP性能考量和若干参考结论

    首先需要说明的是,严格而言NodeJS和Java、PHP并非对等概念,NodeJS是基于JS的一个应用程序,而Java/PHP是语言。我们这里实际指的是分别使用node、java和php来实...

  • 使用HTML5 Canvas实现的界面元素截屏功能

    如果网站出现问题,常常需要截图来提交反馈,这个功能很实用。使用HTML5的Canvas可以实现这个目标。我们首先引入

  • HTTP1.1协议现状、问题和解决方案

    HTTP的现状最早的HTTP协议非常简单,只能用来传送文本,方法也只有GET,后来逐步发展到1.1,能够支持多种MIME格式数据(如文本、文件),支持GET,POST,HEAD,OPTI...

  • HTML5、Hybrid APP、Native APP对比和技术选型

    HTML5和Native APP都很容易理解。为了获得HTML5的移植性和移动本地应用的高性能,搞出来一些混合APP的解决方案。比如Apache的Cordova(也就是以前的PhoneGap),...

  • Three.js 对象局部坐标转换为世界坐标

    在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...

  • 三维向量的简单运算和实用意义

    在WebGL的实际应用中我们广泛使用向量的几何运算来计算角度、距离,判断点线、点面之间的关系,比如物体之间的碰撞检测。本文简要介绍三维计算机图形学中常用的...

  • 纹理基础知识和过滤模式详解

    1、 为什么在纹理采样时需要texture filter(纹理过滤)。
    我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixe...

  • 如何使用Three.js加载obj和mtl文件

    OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...

  • 浏览器控制台报JS脚本执行错误:Module is not defined

    现在JS分成了两个分支,一部分在服务器端发展如NodeJS,一部分是传统的浏览器运行环境。
    有些插件在编写JS代码时,是针对Node编写的,所以直接在浏览器中使...

  • D3.js读取外部json数据

    D3.js是一个很好的数据可视化工具,支持从web服务读取json数据,或者从外部文件如.json, .csv文件中直接读取。由于部分服务比如flickrs上的图文数据服务需要VPN...

  • 如何使用CSS3实现书页(书本)卷角效果

    我们有时候想在页面显示一个公告或用户提示信息。一个常用设计是使用书签形状。我们可以给书签添加卷角效果,以使其更为逼真。所谓的“卷角”实际上可以用小角度...

  • 更多...