ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【canvas】三阶贝塞尔曲线拟合圆

2020-01-20 22:03:09  阅读:599  来源: 互联网

标签:曲线拟合 canvas const ctx cos 三阶 radius sin


回顾

在做这个之前,我还做了
【canvas】网易云音乐鲸云特效『水晶音波』的简单实现
【canvas】网易云音乐鲸云动效『孤独星球』的简单实现
【canvas】实现多种形状的烟花




相关公式

基本思想就是用三阶贝塞尔曲线拟合圆弧,用圆弧拼接成圆。
在这里插入图片描述
其中比较关键的是h的长度,其最佳公式为
h=4(1cosθ2)3sinθ2r h = \frac{ 4 ( 1 - \cos{ \dfrac{\theta}{2} } ) }{ 3 \sin{ \dfrac{\theta}{2} } } \cdot r h=3sin2θ​4(1−cos2θ​)​⋅r

计算出h后,4个点的坐标就很容易得到,为
A(r,  0)B(r,  h)C(rcosθ+hsinθ,  rsinθhcosθ)D(rcosθ,  rsinθ) \begin{aligned} & \textbf A(r,\ \ 0)\\ & \textbf B(r,\ \ h)\\ & \textbf C(r\cos \theta + h\sin \theta,\ \ r\sin \theta - h\cos \theta)\\ & \textbf D(r\cos \theta,\ \ r\sin \theta) \end{aligned} ​A(r,  0)B(r,  h)C(rcosθ+hsinθ,  rsinθ−hcosθ)D(rcosθ,  rsinθ)​

关于公式,我从本站的一位博主『你别无选择』,所写的一篇博客『三阶贝塞尔曲线拟合圆弧的一般公式』中学到的,这里注明一下出处。


如果用 4 段三阶贝塞尔曲线模拟圆,则 θ=π2\theta = \dfrac{\pi}{2}θ=2π​,效果如下

在这里插入图片描述
由于对称性,我只需要算出其中第一段圆弧的起始点 A\textbf AA,控制点 B\textbf BB,控制点 C\textbf CC,然后旋转,得到剩余点的坐标,就可以画圆了。



canvas

理解了公式,就可以动手写代码了。

class Cricle {
  /**
   * 三阶贝塞尔曲线模拟圆
   * @param {Object} context canvas.getContext("2d")
   * @param {Array} pole 圆心位置
   * @param {Number} petal 用 petal 段三阶贝塞尔曲线模拟圆
   * @param {Number} radius 半径
   * @param {String} color 圆内填充颜色
   * @param {Number} α 将圆旋转一个α°
   */
  constructor(context, pole, petal, radius, color, α = 0) {
    this.ctx = context;
    this.pole = pole;
    this.petal = petal;
    this.radius = radius;
    this.color = color;
    this.α = α;
    this.length = petal * 3;
    this.buffer = [];
    this.data = [];
    this.__init();
    this.render();
  }

  __init() {
    const θ = 2 * Math.PI / this.petal;
    const cosθ = Math.cos(θ);
    const sinθ = Math.sin(θ);
    const h = this.radius * (4 * (1 - Math.cos(θ / 2))) / (3 * Math.sin(θ / 2));
    const p1 = [this.radius, 0];
    const p2 = [this.radius, h];
    const p3 = [this.radius * cosθ + h * sinθ, this.radius * sinθ - h * cosθ];
    for (let i = 0, idx = 0; i < this.petal; ++i, idx += 3) {
      const cosNθ = Math.cos(i * θ + this.α);
      const sinNθ = Math.sin(i * θ + this.α);
      this.data[idx] = this.__rotate(p1, cosNθ, sinNθ);
      this.data[idx + 1] = this.__rotate(p2, cosNθ, sinNθ);
      this.data[idx + 2] = this.__rotate(p3, cosNθ, sinNθ);
    }
    this.data.forEach((v, i) => { this.buffer[i] = [v[0] + this.pole[0], v[1] + this.pole[1]]; });
    this.buffer[this.buffer.length] = this.buffer[0];
  }

  __rotate(p, cosα, sinα) {
    return [p[0] * cosα - p[1] * sinα, p[1] * cosα + p[0] * sinα];
  }

  render() {
    this.ctx.moveTo(this.buffer[0][0] + this.pole[0], this.buffer[0][1] + this.pole[1]);
    this.ctx.beginPath();
    this.ctx.strokeStyle = 'blue';
    for (let i = 0, idx = 0; i < this.petal; ++i, idx += 3) {
      const A = this.buffer[idx];
      const B = this.buffer[idx + 1];
      const C = this.buffer[idx + 2];
      const D = this.buffer[idx + 3];
      this.ctx.lineTo(...A);
      this.ctx.bezierCurveTo(...B, ...C, ...D);
      // 标出所有点
      // this.ctx.fillStyle = `hsl(${Math.floor(Math.random() * 360)}, 60%, 60%)`;
      // this.ctx.fillRect(A[0] - 2, A[1] - 2, 4, 4);
      // this.ctx.fillRect(B[0] - 2, B[1] - 2, 4, 4);
      // this.ctx.fillRect(C[0] - 2, C[1] - 2, 4, 4);
    }
    this.ctx.closePath();
    this.ctx.fillStyle = this.color;
    this.ctx.fill();
  }
}

const canvas = document.getElementById('background');
const pole = [canvas.width / 2, canvas.height / 2];
const petal = 4;
const radius = canvas.width / 2 * .5625;
const cricle = new Cricle(canvas.getContext("2d"), pole, petal, radius, 'rgba(241, 240, 237, .1)', 0);

效果展示

4段贝塞尔曲线拟合圆

在这里插入图片描述
24段贝塞尔曲线拟合圆

在这里插入图片描述



应用

可以利用三阶贝塞尔曲线拟合圆,做炫酷的音频可视化。

如网易云音乐的鲸云动效『迷幻水波』在这里插入图片描述

我之前已经做了
【canvas】网易云音乐鲸云特效『水晶音波』的简单实现
【canvas】网易云音乐鲸云动效『孤独星球』的简单实现
这次,我同样尝试去实现鲸云动效『迷幻水波』。但是效果不是很好,就不贴源码了,也没放到codepen上。只是展示一下效果图。如果有兴趣,源码链接在这:github

在这里插入图片描述

三生翰旋醉梦 发布了10 篇原创文章 · 获赞 51 · 访问量 1万+ 私信 关注

标签:曲线拟合,canvas,const,ctx,cos,三阶,radius,sin
来源: https://blog.csdn.net/qq_24380063/article/details/104055086

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有