弧形罗列卡牌效果

一、需求和优化想法

效果样式如下,实现了一个可以拖拽的弧形卡牌的效果,但是这个效果是一次性罗列了很多卡牌的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。

相关推荐
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&3 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch7 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光7 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   7 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发