canvas 实现超椭圆

工作中要实现一个超椭圆头像,超椭圆,不是圆角矩形么,和椭圆有什么区别?第一次看到这个需求不禁有些疑问 🤔,专门查了下,解释如下:

定义:

椭圆:椭圆是由两个焦点定义的,并且它的形状主要取决于这两个焦点之间的距离。

超椭圆:超椭圆是由两个指数定义的,通常表示为 nX 和 nY。当 nX 和 nY 都等于 2 时,超椭圆就变成了普通的椭圆。
形状:

椭圆:椭圆的形状主要取决于两个焦点之间的距离。当两个焦点靠得很近时,椭圆会变得更扁;当两个焦点离得很远时,椭圆会变得更圆。

超椭圆:超椭圆的形状主要取决于 nX 和 nY 的值。当 nX 和 nY 的值增加时,超椭圆会变得更扁;当 nX 和 nY 的值减小时,超椭圆会变得更圆。

好像还是没有明白 😂 简单来说就是椭圆的形状由两个焦点决定,而超椭圆的形状由两个指数决定。

看一下这个是不是就一目了然了 👀

除了小米 logo,很多手机的 App 图标都是超椭圆形状。

我们检查下元素,发现它是用 svg 实现的,换一种方式,用更灵活的 canvas 实现。

实现原理

下面这篇文章用图形的方式,讲的很详细:

canvas 实现超椭圆

上面是把数学公式转换成代码画出来的,那么有没有更好理解的方式呢?答案是有的 💡

通常在canvas里面画一个圆,用的arc()方法,如果不用这个方法,也可以用画线的方式,循环遍历Math.PI * 2(360 度),每次移动 1 度,这样画出来的就是圆了。

超椭圆也可以用同样的方式实现,只是在画线过程中使用Math.cos(angle)Math.sin(angle)计算角度的余弦和正弦值,然后使用偏导数(nXnY)调整坐标点,以绘制超椭圆。

以下是完整实现:

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>

最后画出的图形是超椭圆:

当然我们也可以用其他方法实现,比如贝塞尔曲线,有兴趣的可以自行尝试。

相关推荐
蜗牛快跑2139 分钟前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy10 分钟前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪1 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇1 小时前
ES6进阶知识一
前端·ecmascript·es6
前端郭德纲2 小时前
浏览器是加载ES6模块的?
javascript·算法
JerryXZR2 小时前
JavaScript核心编程 - 原型链 作用域 与 执行上下文
开发语言·javascript·原型模式