弧形罗列卡牌效果

一、需求和优化想法

效果样式如下,实现了一个可以拖拽的弧形卡牌的效果,但是这个效果是一次性罗列了很多卡牌的div,批量的进行角度修改的,性能差,可能会造成js的卡顿,所以我有一种将div掰弯的想法,就是在一个掰弯的div里一次性罗列这些卡牌,然后再对父div进行角度偏移,但是我没找到支撑该想法的工具,于是我用了canvas去实现这个效果。

以下是上述效果图的实现代码(原生js)

js 复制代码
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        user-select: none;
      }

      :root {
        --origin-y-rate: 4;
        --size: 240px;
        --basic-translate: translate(50%, -120%);
        --offset: 0deg;
        --max: 0;
        --transition-property: transform;
        --move-deg: 0;
      }

      .card-wrap {
        position: fixed;
        background-color: aqua;
        top: 50%;
        left: 50%;
        width: 0;
        height: 0;
        transform: rotate(var(--move-deg));
        transform-origin: center
          calc(calc(var(--size) * calc(var(--origin-y-rate) - 1.2)));
      }

      .card {
        height: calc(var(--size) * 1.2);
        width: var(--size);
        background-color: lightcyan;
        border: 2px solid gray;
        border-radius: 8px;
        position: absolute;
        top: 0;
        right: 0;
        transform-origin: center calc(var(--size) * var(--origin-y-rate));
        transform: var(--basic-translate)
          rotate(calc(calc(var(--index) * var(--offset)) - 90deg));
        transition: var(--transition-property) 1s ease
          calc(calc(var(--max) - var(--index)) * 100ms);
      }

      .card::before {
        content: attr(data-index);
        display: block;
      }
    </style>
  </head>

  <body>
    <div class="card-wrap"></div>
    <script>
      function initCard(max) {
        document.documentElement.style.setProperty("--max", max);
        let i = 0;
        while (i < max) {
          const card = document.createElement("div");
          card.classList.add("card");
          card.style.setProperty("--index", i++);
          card.dataset.index = `第${i}张`;
          document.querySelector(".card-wrap").appendChild(card);
        }
      }
      let currentX = 0;
      let moveX = 0;
      function initAction() {
        document.documentElement.style.setProperty(
          "--transition-property",
          "none"
        );
        let active = false;
        let x = 0;
        document.addEventListener("mousedown", (e) => {
          active = true;
          x = e.x;
        });
        document.addEventListener("mouseup", (e) => {
          active = false;
          x = e.x;
          currentX = move;
        });
        document.addEventListener("mousemove", (e) => {
          if (!active) return;
          moveX = currentX + e.x - x;
        });
        rafFn();
      }
      function rafFn() {
        document.documentElement.style.setProperty(
          "--move-deg",
          `${(moveX * 0.05).toFixed(3)}deg`
        );
        requestAnimationFrame(rafFn);
      }
      const max = 36;
      initCard(max);
      setTimeout(() => {
        document.documentElement.style.setProperty("--offset", "5deg");
        setTimeout(() => {
          initAction();
        }, max * 100);
      }, 1000);
    </script>
  </body>
</html>

二、优化思路

既然没办法将div掰弯,那我就自己制造弧形 以下是实现的代码

js 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div class="container">
      <canvas id="myCanvas" width="500" height="500"></canvas>
    </div>
  </body>
  <script>
    const container = document.querySelector(".container");
    const canvas = document.getElementById("myCanvas");
    const context = canvas.getContext("2d");
    const numOfDivs = 78; //个数
    const radius = 1800; //半径
    const centerX = 500 / 2; //圆心X
    const centerY = 500 / 2; //圆心Y
    const angleStep = 0.04; // 计算每个长方形之间的角度差
    // context.beginPath();
    // for (let i = 0; i < numOfDivs; i++) {
    //   const startAngle = i * angleStep;
    //   const endAngle = (i + 1) * angleStep;
    //   context.moveTo(centerX, centerY);
    //   context.arc(centerX, centerY, radius, -startAngle, -endAngle);
    //   context.lineTo(centerX, centerY);
    // }
    // context.strokeStyle = "black";
    // context.stroke();
    let jd;
    for (let i = 1; i <= numOfDivs; i++) {
      const width = 100;
      const height = 150;
      const angle = i * -angleStep; // 计算当前长方形的角度位置
      const x = -width / 2 + centerX + radius * Math.cos(angle); // 根据角度计算 x 坐标
      const y = -height + centerY + radius * Math.sin(angle); // 根据角度计算 y 坐标
      const dX = radius * -Math.cos(angle);
      const dY = radius * Math.cos(angle);
      jd = -Math.asin(dX / radius) * (180 / Math.PI);
      const childDiv = document.createElement("div");
      childDiv.className = "card";
      childDiv.style.position = "absolute";
      childDiv.style.left = `${x}px`;
      childDiv.style.top = `${y}px`;
      childDiv.style.width = `${width}px`;
      childDiv.style.height = `${height}px`;
      childDiv.style.backgroundColor = "red";
      childDiv.style.zIndex = "-1";
      childDiv.style.transformOrigin = "center bottom";
      console.log(dX, radius, dX / radius, jd);
      childDiv.style.transform = `rotate(${jd}deg)`;
      container.appendChild(childDiv);
    }
    let isMouseDown = false;
    let startX = 0;
    let startY = 0;
    let moveX = 0;
    let moveY = 0;
    document.addEventListener("mousedown", function (e) {
      isMouseDown = true;
    });

    document.addEventListener("mouseup", function (e) {
      isMouseDown = false;
    });

    document.addEventListener("mousemove", function (e, d) {
      const container = document.querySelector(".container");
      if (isMouseDown) {
        moveX = e.clientX - startX;
        startX = e.clientX;
        if (moveX < 0) {
          jd = jd - 0.5;
          container.style.transform = `rotate(${jd}deg)`;
        } else if (moveX > 0) {
          jd = jd + 0.5;
          container.style.transform = `rotate(${jd}deg)`;
        }
      }
    });
  </script>
  <style>
    .card {
      border: 2px solid black;
    }
    .container {
      height: 500px;
      width: 500px;
      margin: 2000px auto;
      transform: rotate(0deg);
    }
  </style>
</html>

注释掉的部分是canvas制造的弧形,我用来参考我的卡片是否对齐的辅助线,在写的时候我感觉脑子里死去的高中知识在疯狂攻击我,满脑子都是sin,cos。

相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄1 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端