其他分享
首页 > 其他分享> > 【canvas】三阶贝塞尔曲线拟合圆

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

作者:互联网

回顾

在做这个之前,我还做了
【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