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