工作中要实现一个超椭圆头像,超椭圆,不是圆角矩形么,和椭圆有什么区别?第一次看到这个需求不禁有些疑问 🤔,专门查了下,解释如下:
定义:
椭圆:椭圆是由两个焦点定义的,并且它的形状主要取决于这两个焦点之间的距离。
超椭圆:超椭圆是由两个指数定义的,通常表示为 nX 和 nY。当 nX 和 nY 都等于 2 时,超椭圆就变成了普通的椭圆。
形状:椭圆:椭圆的形状主要取决于两个焦点之间的距离。当两个焦点靠得很近时,椭圆会变得更扁;当两个焦点离得很远时,椭圆会变得更圆。
超椭圆:超椭圆的形状主要取决于 nX 和 nY 的值。当 nX 和 nY 的值增加时,超椭圆会变得更扁;当 nX 和 nY 的值减小时,超椭圆会变得更圆。
好像还是没有明白 😂 简单来说就是椭圆的形状由两个焦点决定,而超椭圆的形状由两个指数决定。
看一下这个是不是就一目了然了 👀
除了小米 logo
,很多手机的 App 图标都是超椭圆形状。
我们检查下元素,发现它是用 svg
实现的,换一种方式,用更灵活的 canvas
实现。
实现原理
下面这篇文章用图形的方式,讲的很详细:
上面是把数学公式转换成代码画出来的,那么有没有更好理解的方式呢?答案是有的 💡
通常在canvas
里面画一个圆,用的arc()
方法,如果不用这个方法,也可以用画线的方式,循环遍历Math.PI * 2
(360 度),每次移动 1 度,这样画出来的就是圆了。
超椭圆也可以用同样的方式实现,只是在画线过程中使用Math.cos(angle)
和Math.sin(angle)
计算角度的余弦和正弦值,然后使用偏导数(nX
和 nY
)调整坐标点,以绘制超椭圆。
以下是完整实现:
js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绘制超椭圆</title>
</head>
<body>
<!-- 创建一个600x600的canvas元素,用于绘制超椭圆 -->
<canvas id="myCanvas" width="300" height="300"></canvas>
<script>
/**
* 绘制超椭圆
* @param canvas - 画布元素
* @returns {Path2D} - 返回绘制的超椭圆路径
*/
function drawSuperEllipse(canvas) {
const ellipse = new Path2D(); // 创建一个新的Path2D对象,用于绘制超椭圆路径
const nX = 3.06; // x方向的超椭圆参数
const nY = 3.45; // y方向的超椭圆参数
const r = canvas.width / 2; // 计算椭圆的半径
const x = canvas.width / 2; // 椭圆中心的x坐标
const y = canvas.height / 2; // 椭圆中心的y坐标
for (let angle = 0; angle < Math.PI * 2; angle += 0.01) {
// 计算超椭圆上的每一个点的坐标
const px =
Math.abs(Math.cos(angle)) ** (2 / nX) *
r *
Math.sign(Math.cos(angle)) +
x;
const py =
Math.abs(Math.sin(angle)) ** (2 / nY) *
r *
Math.sign(Math.sin(angle)) +
y;
if (angle === 0) {
ellipse.moveTo(px, py); // 在第一个点处移动到坐标 (px, py)
} else {
ellipse.lineTo(px, py); // 绘制从上一个点到当前点的线段
}
}
ellipse.closePath(); // 关闭路径
return ellipse; // 返回绘制的超椭圆路径
}
const canvas = document.getElementById("myCanvas"); // 获取画布元素
const ctx = canvas.getContext("2d"); // 获取2D绘图上下文
// 添加点击事件监听
canvas.addEventListener("click", () => {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 要绘制的图形
const superEllipse = drawSuperEllipse(canvas);
ctx.stroke(superEllipse);
});
</script>
</body>
</html>
最后画出的图形是超椭圆:
当然我们也可以用其他方法实现,比如贝塞尔曲线,有兴趣的可以自行尝试。