弧形罗列卡牌效果

一、需求和优化想法

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

相关推荐
z_mazin几秒前
Chrome开发者工具实战:调试三剑客
前端·javascript·chrome·网络爬虫
sen_shan1 小时前
Vue3+Vite+TypeScript+Element Plus开发-04.静态菜单设计
前端·javascript·typescript·vue3·element·element plus·vue 动态菜单
旧识君2 小时前
移动端1px终极解决方案:Sass混合宏工程化实践
开发语言·前端·javascript·前端框架·less·sass·scss
ElasticPDF-新国产PDF编辑器2 小时前
Angular use pdf.js and Elasticpdf tutorial
javascript·pdf·angular.js
吃没吃2 小时前
vue2.6-源码学习-Vue 核心入口文件分析
前端
Carlos_sam2 小时前
Openlayers:海量图形渲染之图片渲染
前端·javascript
XH2762 小时前
Android Retrofit用法详解
前端
鸭梨大大大2 小时前
Spring Web MVC入门
前端·spring·mvc
你的人类朋友2 小时前
MQTT协议是用来做什么的?此协议常用的概念有哪些?
javascript·后端·node.js
吃没吃2 小时前
vue2.6-源码学习-Vue 初始化流程分析 (src/core/instance/init.js)
前端