【canvas】三阶贝塞尔曲线拟合圆
作者:互联网
回顾
在做这个之前,我还做了
【canvas】网易云音乐鲸云特效『水晶音波』的简单实现
【canvas】网易云音乐鲸云动效『孤独星球』的简单实现
【canvas】实现多种形状的烟花
相关公式
基本思想就是用三阶贝塞尔曲线拟合圆弧,用圆弧拼接成圆。
其中比较关键的是h
的长度,其最佳公式为
h=3sin2θ4(1−cos2θ)⋅r
计算出h
后,4个点的坐标就很容易得到,为
A(r, 0)B(r, h)C(rcosθ+hsinθ, rsinθ−hcosθ)D(rcosθ, rsinθ)
关于公式,我从本站的一位博主『你别无选择』,所写的一篇博客『三阶贝塞尔曲线拟合圆弧的一般公式』中学到的,这里注明一下出处。
如果用 4 段三阶贝塞尔曲线模拟圆,则 θ=2π,效果如下
由于对称性,我只需要算出其中第一段圆弧的起始点 A,控制点 B,控制点 C,然后旋转,得到剩余点的坐标,就可以画圆了。
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
标签:曲线拟合,canvas,const,ctx,cos,三阶,radius,sin 来源: https://blog.csdn.net/qq_24380063/article/details/104055086