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

iefreer 发表于 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(1) negative(0) views7940 comments0
私信 收藏 分享
分享到

发送私信

最新评论

请先 登录 再评论.
相关文章
  • 我的第一个微博

    测试用

  • 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...

  • React JSX语法简介

    JSX是一种类似XML的标签语法,用来简化代码,我们可以不使用JSX,但了解并使用也没什么坏处:)在React中,JSX是一个使用 React.createElement() API的快捷方式...

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

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

  • Babylon.js入门教程和开发实例

    Babylon.js是一款WebGL开发框架。和Three.js类似。主要的技术区别是Three.js还试图回退兼容CSS 3D。Three.js是完全社区推动的,比Babylon.js要成熟些,而Babylon...

  • WebVR简介和常用资源链接

    什么是WebVR这是一个实验性的JavaScript API,提供了在用户网页浏览器中访问虚拟现实设备的统一接口。当前主流VR设备如Oculus Rift DK2、谷歌的CardBoard、三星...

  • Three.js入门教程4 - 创建粒子系统动画

    嗨,又见面了。这么说我们已经开始学习Three.js了,如果你还没有看过之前三篇教程,建议你先读完。如果你已经读完前面的教程了,你可能会想做一些关于粒子的东西。让我们直面这个话题吧,每个人都爱粒子效果。不管你是否知道,你可以很轻易地创建它们。

  • Three.js入门教程2 - 着色器(下)

    这是WebGL着色器教程的后半部分,如果你没看过前一篇,阅读这一篇教程可能会使你感到困惑,建议你翻阅前面的教程。

  • S3TC(S3 Texture Compression)纹理压缩格式详解

    使用S3TC格式存储的压缩纹理是以4X4的纹理单元块(texel blocks)为基本单位存储的,每纹理单元块(texel blocks)有64bit或者128bit的纹理单元数据(texel data)。这...

  • CSS3图片混合(Blend)效果及其参考计算公式一览表

    在Photoshop软件中,混合是将两个图层的色彩值进行合成,从而创造出大量的效果。在这些效果的背后实际是一些简单的数学公式在起作用。下面所介绍的公式仅适用于R...

  • WebGL入门教程6 - 光照效果和Phong光照模型

    正是因为有了光,世界才能被我们看见,在3D的世界里,光照给物体带来真实的视觉感受。当光照射在某一表面上时,它可能被吸收、反射或投射。其中入射到表面上的一...

  • IE各版本CSS Hack(兼容性处理)语法速查表

    为了兼容IE各个版本,需要在CSS中添加额外的代码,比如以前常用的_width。之所以工作,是因为浏览器会忽略不能解析的样式规则,因此举个例子来说,把_width写在w...

  • WebGL入门教程3 - Canvas、Context、API和绘制一个矩形

    在教程2中,我们已经讲述了计算机图形处理硬件结构和流水线。在本文中,我们将开始讲述WebGL的具体应用程序编程接口(API)。WebGL应用程序编程步骤分为以下几步:
    创建一个canvas元素从canvas中获取webgl渲染...

  • 如何使用纯CSS3实现一个3D泡沫

    要实现一个逼真的泡沫,涉及到比较复杂的光学/物理学知识。我们这里先简化下问题,实现一个相对简单而足够实用的泡沫元素。我们可以把基础的泡沫元素应用在很多场景中,比如水景、泡咖啡、啤酒甚至火焰特效中。泡沫首先是一个圆形元素.bubble

  • 更多...