feat: 添加歌曲请求站页面及静态资源

style: 调整标签气泡动效样式和布局

refactor: 优化标签碰撞检测算法和位置计算

docs: 更新README文件说明

chore: 添加相关图片和CSS文件资源
This commit is contained in:
DESKTOP-RQ919RC\Pc
2025-09-18 19:03:24 +08:00
parent 35e3a11427
commit 8542840577
23 changed files with 1396 additions and 4 deletions

View File

@@ -0,0 +1,256 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>标签气泡动效</title>
<style></style>
<link rel="stylesheet" href="./static/css/song-request-station.css" />
</head>
<body>
<!-- <div class="container" id="bubbleContainer"></div> -->
<div class="container">
<div class="container-box mar1200">
<img class="logo" src="./static/img/logo.png" alt="" />
<div class="header">
<img class="halo" src="./static/img/halo.png" />
<img class="record-black" src="./static/img/record-black.svg" />
<div class="record-circle"></div>
<img class="star-icon" src="./static/img/star-icon.png" alt="" />
<img class="bj-2" src="./static/img/song-request-bj-2.svg" alt="" />
<img class="love-little" src="./static/img/love-little.svg" alt="" />
<img class="music-icon" src="./static/img/music-icon.svg" alt="" />
<img class="bj" src="./static/img/song-request-bj.svg" alt="" />
<img class="love-big" src="./static/img/love-big.svg" alt="" />
<img class="music-score" src="./static/img/music-score.png" />
<img class="robot" src="./static/img/robot.png" />
<img class="text" src="./static/img/song-request-text.svg" />
<img class="face" src="./static/img/smiling-face.png" />
<img class="star-icon-2" src="./static/img/star-icon-2.png" />
<img class="ai-music" src="./static/img/ai-music.png" />
<img class="green-glow" src="./static/img/green-glow.png" />
<img class="shadow" src="./static/img/shadow.png" />
</div>
<div class="list-box">
<div class="list" id="bubbleContainer"></div>
</div>
</div>
</div>
<script>
// 标签数据
const tags = ["前端开发", "JavaScript", "CSS动画", "HTML5", "React", "Vue", "TypeScript", "Node.js", "UI设计", "用户体验", "响应式布局", "性能优化", "微信小程序", "PWA", "Canvas", "SVG", "WebGL", "数据可视化", "模块化", "组件化", "", "", "", "", ""];
// 获取容器
const container = document.getElementById("bubbleContainer");
// 创建空白标签数组,用于存储所有空白标签的信息
const emptyTags = [];
// 存储所有标签的位置和大小信息
const allTags = [];
const tagSizes = []; // 存储每个标签的实际尺寸
const defaultTagWidth = 120; // 默认标签宽度
const defaultTagHeight = 40; // 默认标签高度
// 计算容器尺寸
function getContainerDimensions() {
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
return { containerWidth, containerHeight };
}
// 获取标签的实际尺寸
function getTagDimensions(tagText) {
// 创建一个临时标签来测量实际尺寸
const tempTag = document.createElement("div");
tempTag.className = "bubble-tag";
tempTag.textContent = tagText;
tempTag.style.position = "absolute";
tempTag.style.visibility = "hidden";
tempTag.style.left = "-9999px";
tempTag.style.top = "-9999px";
document.body.appendChild(tempTag);
const width = tempTag.offsetWidth;
const height = tempTag.offsetHeight;
document.body.removeChild(tempTag);
// 确保至少有默认的尺寸
return {
width: Math.max(width, defaultTagWidth),
height: Math.max(height, defaultTagHeight),
};
}
// 检查两个标签是否碰撞
function isColliding(tag1, tag2, padding = 20) {
// 计算两个标签之间的距离
const dx = Math.abs(tag1.x + tag1.width / 2 - (tag2.x + tag2.width / 2));
const dy = Math.abs(tag1.y + tag1.height / 2 - (tag2.y + tag2.height / 2));
// 如果距离小于两个标签半径之和加上内边距,则发生碰撞
return dx < tag1.width / 2 + tag2.width / 2 + padding && dy < tag1.height / 2 + tag2.height / 2 + padding;
}
// 计算理想的网格大小以实现均匀分布
function calculateGridSize(totalTags, containerWidth, containerHeight, avgTagSize) {
// 估计每个标签需要的空间(包括间距)
const tagSpacing = avgTagSize + 60; // 标签直径 + 间距
// 计算大致的行数和列数
const idealColumns = Math.ceil(Math.sqrt((totalTags * containerWidth) / containerHeight));
const idealRows = Math.ceil(totalTags / idealColumns);
// 确保网格不会太拥挤
const actualColumns = Math.min(idealColumns, Math.floor(containerWidth / tagSpacing));
const actualRows = Math.min(idealRows, Math.floor(containerHeight / tagSpacing));
return { columns: Math.max(1, actualColumns), rows: Math.max(1, actualRows) };
}
// 为新标签找到一个不与其他标签碰撞的位置,优先使用均匀分布
function findNonCollidingPosition(tagWidth, tagHeight, containerWidth, containerHeight, totalTags, currentIndex) {
const maxAttempts = 200;
let attempts = 0;
// 计算网格大小
const avgTagSize = (tagWidth + tagHeight) / 2;
const { columns, rows } = calculateGridSize(totalTags, containerWidth, containerHeight, avgTagSize);
// 首先尝试网格均匀分布位置
while (attempts < maxAttempts * 0.7) {
attempts++;
// 计算理想的网格位置
const gridX = (currentIndex % columns) * (containerWidth / columns);
const gridY = Math.floor(currentIndex / columns) * (containerHeight / rows);
// 添加一些随机偏移,避免完全规则排列,但保持大致均匀
const randomOffset = 0.2; // 20%的随机偏移
const xOffset = (((Math.random() - 0.5) * containerWidth) / columns) * randomOffset;
const yOffset = (((Math.random() - 0.5) * containerHeight) / rows) * randomOffset;
// 计算最终位置,确保不会超出容器
const x = Math.max(0, Math.min(containerWidth - tagWidth, gridX + xOffset));
const y = Math.max(0, Math.min(containerHeight - tagHeight, gridY + yOffset));
const newTag = { x, y, width: tagWidth, height: tagHeight };
// 检查与所有已有标签是否碰撞
let colliding = false;
for (let i = 0; i < allTags.length; i++) {
if (isColliding(newTag, allTags[i])) {
colliding = true;
break;
}
}
if (!colliding) {
return { x, y };
}
}
// 如果网格分布尝试失败,回退到随机位置搜索
while (attempts < maxAttempts) {
attempts++;
// 随机生成一个位置
const x = Math.random() * (containerWidth - tagWidth);
const y = Math.random() * (containerHeight - tagHeight);
const newTag = { x, y, width: tagWidth, height: tagHeight };
// 检查与所有已有标签是否碰撞
let colliding = false;
for (let i = 0; i < allTags.length; i++) {
if (isColliding(newTag, allTags[i])) {
colliding = true;
break;
}
}
if (!colliding) {
return { x, y };
}
}
// 如果尝试次数用完仍然找不到完全不碰撞的位置,返回随机位置
return {
x: Math.random() * (containerWidth - tagWidth),
y: Math.random() * (containerHeight - tagHeight),
};
}
// 创建标签并添加动画
function createTags() {
const { containerWidth, containerHeight } = getContainerDimensions();
const totalTags = tags.length;
// 清空容器
container.innerHTML = "";
allTags.length = 0;
tagSizes.length = 0;
tags.forEach((tagText, index) => {
// 创建标签元素
const tag = document.createElement("div");
tag.className = "bubble-tag";
tag.textContent = tagText;
// 获取标签的实际尺寸
const { width, height } = getTagDimensions(tagText);
tagSizes.push({ width, height });
// 查找不碰撞的位置
const { x, y } = findNonCollidingPosition(width, height, containerWidth, containerHeight, totalTags, index);
tag.style.left = `${x}px`;
tag.style.top = `${y}px`;
// 保存标签信息
allTags.push({ x, y, width, height, element: tag });
// 随机大小
const randomSize = 0.9 + Math.random() * 0.4; // 稍微增大了最小尺寸
tag.style.transform = `scale(${randomSize})`;
// 随机动画延迟
const delay = Math.random() * 5;
tag.style.animationDelay = `${delay}s`;
// 随机动画持续时间
const duration = 10 + Math.random() * 10; // 延长动画周期,使效果更流畅
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
// 随机背景色 - 模仿截图中的颜色范围
const hue = 250 + Math.random() * 60; // 紫色到粉色的范围
const lightness = 85 + Math.random() * 10; // 亮度调整
tag.style.backgroundColor = `hsla(${hue}, 70%, ${lightness}%, 0.9)`;
// 添加到容器
container.appendChild(tag);
// 添加点击效果
tag.addEventListener("click", () => {
tag.style.animation = "none";
tag.style.transform = "scale(1.3)";
tag.style.zIndex = "10";
setTimeout(() => {
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
tag.style.animationDelay = "0s";
tag.style.zIndex = "1";
}, 300);
});
});
}
// 窗口大小变化时重新创建标签
window.addEventListener("resize", () => {
createTags();
});
// 初始创建标签
createTags();
</script>
</body>
</html>

287
song-request-station.html Normal file
View File

@@ -0,0 +1,287 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>标签气泡动效</title>
<style></style>
<link rel="stylesheet" href="./static/css/song-request-station.css" />
</head>
<body>
<!-- <div class="container" id="bubbleContainer"></div> -->
<div class="container">
<div class="container-box mar1200">
<img class="logo" src="./static/img/logo.png" alt="" />
<div class="header">
<img class="halo" src="./static/img/halo.png" />
<img class="record-black" src="./static/img/record-black.svg" />
<div class="record-circle"></div>
<img class="star-icon" src="./static/img/star-icon.png" alt="" />
<img class="bj-2" src="./static/img/song-request-bj-2.svg" alt="" />
<img class="love-little" src="./static/img/love-little.svg" alt="" />
<img class="music-icon" src="./static/img/music-icon.svg" alt="" />
<img class="bj" src="./static/img/song-request-bj.svg" alt="" />
<img class="love-big" src="./static/img/love-big.svg" alt="" />
<img class="music-score" src="./static/img/music-score.png" />
<img class="robot" src="./static/img/robot.png" />
<img class="text" src="./static/img/song-request-text.svg" />
<img class="face" src="./static/img/smiling-face.png" />
<img class="star-icon-2" src="./static/img/star-icon-2.png" />
<img class="ai-music" src="./static/img/ai-music.png" />
<img class="green-glow" src="./static/img/green-glow.png" />
<img class="shadow" src="./static/img/shadow.png" />
</div>
<div class="list-box">
<div class="list" id="bubbleContainer"></div>
</div>
</div>
</div>
<script>
// 标签数据
const tags = ["前端开发", "JavaScript", "CSS动画", "HTML5", "React", "Vue", "TypeScript", "Node.js", "UI设计", "用户体验", "响应式布局", "性能优化", "微信小程序", "PWA", "Canvas", "SVG", "WebGL", "数据可视化", "模块化", "组件化", "", "", "", "", ""];
// 获取容器
const container = document.getElementById("bubbleContainer");
// 创建空白标签数组,用于存储所有空白标签的信息
const emptyTags = [];
// 存储所有标签的位置和大小信息
const allTags = [];
const tagSizes = []; // 存储每个标签的实际尺寸
const defaultTagWidth = 120; // 默认标签宽度
const defaultTagHeight = 40; // 默认标签高度
// 计算容器尺寸
function getContainerDimensions() {
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
return { containerWidth, containerHeight };
}
// 获取标签的实际尺寸
function getTagDimensions(tagText) {
// 创建一个临时标签来测量实际尺寸
const tempTag = document.createElement("div");
tempTag.className = "bubble-tag";
tempTag.textContent = tagText;
tempTag.style.position = "absolute";
tempTag.style.visibility = "hidden";
tempTag.style.left = "-9999px";
tempTag.style.top = "-9999px";
document.body.appendChild(tempTag);
const width = tempTag.offsetWidth;
const height = tempTag.offsetHeight;
document.body.removeChild(tempTag);
// 确保至少有默认的尺寸
return {
width: Math.max(width, defaultTagWidth),
height: Math.max(height, defaultTagHeight),
};
}
// 检查两个标签是否碰撞
function isColliding(tag1, tag2, padding = 20) {
// 判断是否为空白标签
const isTag1Empty = tag1.isEmpty === true;
const isTag2Empty = tag2.isEmpty === true;
// 如果其中一个是空白标签,允许它们重叠
if (isTag1Empty || isTag2Empty) {
return false;
}
// 计算两个标签之间的距离
const dx = Math.abs(tag1.x + tag1.width / 2 - (tag2.x + tag2.width / 2));
const dy = Math.abs(tag1.y + tag1.height / 2 - (tag2.y + tag2.height / 2));
// 如果距离小于两个标签半径之和加上内边距,则发生碰撞
return dx < tag1.width / 2 + tag2.width / 2 + padding && dy < tag1.height / 2 + tag2.height / 2 + padding;
}
// 计算理想的网格大小以实现均匀分布
function calculateGridSize(totalTags, containerWidth, containerHeight, avgTagSize) {
// 估计每个标签需要的空间(包括间距)
const tagSpacing = avgTagSize + 60; // 标签直径 + 间距
// 计算大致的行数和列数
const idealColumns = Math.ceil(Math.sqrt((totalTags * containerWidth) / containerHeight));
const idealRows = Math.ceil(totalTags / idealColumns);
// 确保网格不会太拥挤
const actualColumns = Math.min(idealColumns, Math.floor(containerWidth / tagSpacing));
const actualRows = Math.min(idealRows, Math.floor(containerHeight / tagSpacing));
return { columns: Math.max(1, actualColumns), rows: Math.max(1, actualRows) };
}
// 为新标签找到一个不与其他标签碰撞的位置,优先使用均匀分布
function findNonCollidingPosition(tagWidth, tagHeight, containerWidth, containerHeight, totalTags, currentIndex) {
const maxAttempts = 200;
let attempts = 0;
// 计算网格大小
const avgTagSize = (tagWidth + tagHeight) / 2;
const { columns, rows } = calculateGridSize(totalTags, containerWidth, containerHeight, avgTagSize);
// 首先尝试网格均匀分布位置
while (attempts < maxAttempts * 0.7) {
attempts++;
// 计算理想的网格位置
const gridX = (currentIndex % columns) * (containerWidth / columns);
const gridY = Math.floor(currentIndex / columns) * (containerHeight / rows);
// 添加一些随机偏移,避免完全规则排列,但保持大致均匀
const randomOffset = 0.2; // 20%的随机偏移
const xOffset = (((Math.random() - 0.5) * containerWidth) / columns) * randomOffset;
const yOffset = (((Math.random() - 0.5) * containerHeight) / rows) * randomOffset;
// 计算最终位置,确保不会超出容器
const x = Math.max(0, Math.min(containerWidth - tagWidth, gridX + xOffset));
const y = Math.max(0, Math.min(containerHeight - tagHeight, gridY + yOffset));
const newTag = { x, y, width: tagWidth, height: tagHeight };
// 检查与所有已有标签是否碰撞
let colliding = false;
for (let i = 0; i < allTags.length; i++) {
if (isColliding(newTag, allTags[i])) {
colliding = true;
break;
}
}
if (!colliding) {
return { x, y };
}
}
// 如果网格分布尝试失败,回退到随机位置搜索
while (attempts < maxAttempts) {
attempts++;
// 随机生成一个位置
const x = Math.random() * (containerWidth - tagWidth);
const y = Math.random() * (containerHeight - tagHeight);
const newTag = { x, y, width: tagWidth, height: tagHeight };
// 检查与所有已有标签是否碰撞
let colliding = false;
for (let i = 0; i < allTags.length; i++) {
if (isColliding(newTag, allTags[i])) {
colliding = true;
break;
}
}
if (!colliding) {
return { x, y };
}
}
// 如果尝试次数用完仍然找不到完全不碰撞的位置,返回随机位置
return {
x: Math.random() * (containerWidth - tagWidth),
y: Math.random() * (containerHeight - tagHeight),
};
}
// 创建标签并添加动画
function createTags() {
const { containerWidth, containerHeight } = getContainerDimensions();
const totalTags = tags.length;
// 清空容器
container.innerHTML = "";
allTags.length = 0;
tagSizes.length = 0;
tags.forEach((tagText, index) => {
// 创建标签元素
const tag = document.createElement("div");
tag.className = "bubble-tag";
tag.textContent = tagText;
// 判断是否为空白标签
const isEmpty = tagText === "";
// 获取标签的实际尺寸
const { width, height } = getTagDimensions(tagText);
tagSizes.push({ width, height });
// 查找不碰撞的位置
const { x, y } = findNonCollidingPosition(width, height, containerWidth, containerHeight, totalTags, index);
tag.style.left = `${x}px`;
tag.style.top = `${y}px`;
// 保存标签信息
const tagInfo = { x, y, width, height, element: tag };
// 如果是空白标签,添加标记并设置不同的样式
if (isEmpty) {
tagInfo.isEmpty = true;
tag.style.backgroundColor = "transparent";
tag.style.zIndex = "0";
tag.style.border = "1px dashed rgba(150, 150, 255, 0.5)";
tag.style.pointerEvents = "none";
emptyTags.push(tagInfo);
} else {
tag.style.zIndex = "1";
}
allTags.push(tagInfo);
// 随机大小
const randomSize = 0.9 + Math.random() * 0.4; // 稍微增大了最小尺寸
tag.style.transform = `scale(${randomSize})`;
// 随机动画延迟
const delay = Math.random() * 5;
tag.style.animationDelay = `${delay}s`;
// 随机动画持续时间
const duration = 10 + Math.random() * 10; // 延长动画周期,使效果更流畅
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
// 非空白标签设置背景色
if (!isEmpty) {
// 随机背景色 - 模仿截图中的颜色范围
const hue = 250 + Math.random() * 60; // 紫色到粉色的范围
const lightness = 85 + Math.random() * 10; // 亮度调整
tag.style.backgroundColor = `hsla(${hue}, 70%, ${lightness}%, 0.9)`;
}
// 添加到容器
container.appendChild(tag);
// 非空白标签添加点击效果
if (!isEmpty) {
tag.addEventListener("click", () => {
tag.style.animation = "none";
tag.style.transform = "scale(1.3)";
tag.style.zIndex = "10";
setTimeout(() => {
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
tag.style.animationDelay = "0s";
tag.style.zIndex = "1";
}, 300);
});
}
});
}
// 窗口大小变化时重新创建标签
window.addEventListener("resize", () => {
createTags();
});
// 初始创建标签
createTags();
</script>
</body>
</html>

View File

@@ -0,0 +1,290 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 100%;
background-color: #333333;
/* 气泡浮动动画 */
/* 呼吸效果动画 */
}
.container .flexflex {
display: flex;
}
.container .flexcenter {
display: flex;
justify-content: center;
align-items: center;
}
.container .flexjcenter {
display: flex;
justify-content: center;
}
.container .flexacenter {
display: flex;
align-items: center;
}
.container .flex1 {
flex: 1;
}
.container .flexcolumn {
display: flex;
flex-direction: column;
}
.container .mar1200 {
width: 1200px;
margin: 0 auto;
}
.container * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
overflow: hidden;
}
.container .bubble-tag {
position: absolute;
padding: 8px 18px;
border-radius: 25px;
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
font-size: 14px;
cursor: pointer;
transition: transform 0.3s ease;
user-select: none;
z-index: 1;
}
.container .bubble-tag:hover {
transform: scale(1.15);
z-index: 10;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
@keyframes float {
0%,
100% {
transform: translateY(0) translateX(0) rotate(0deg);
}
25% {
transform: translateY(-15px) translateX(10px) rotate(1deg);
}
50% {
transform: translateY(-30px) translateX(5px) rotate(0deg);
}
75% {
transform: translateY(-15px) translateX(-10px) rotate(-1deg);
}
}
@keyframes pulse {
0% {
opacity: 0.8;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
100% {
opacity: 1;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
}
.container .container-box {
padding-top: 24px;
height: 100vh;
display: flex;
flex-direction: column;
}
.container .container-box .logo {
width: 121px;
height: 24px;
margin-bottom: 31px;
}
.container .container-box .header {
position: relative;
width: 1200px;
height: 320px;
border-radius: 20px;
margin-bottom: 20px;
}
.container .container-box .header::after {
content: "";
width: 1200px;
height: 320px;
background: linear-gradient(180deg, #7d4bf8 0%, #5241b0 100%);
border-radius: 20px;
position: absolute;
top: 0;
left: 0;
}
.container .container-box .header .halo {
width: 240px;
height: 214px;
position: absolute;
top: -71px;
left: -59px;
}
.container .container-box .header .star-icon {
position: absolute;
top: 46px;
left: 51px;
width: 56px;
height: 56px;
z-index: 1;
}
.container .container-box .header .love-little {
position: absolute;
top: 46px;
left: 51px;
width: 186px;
height: 160px;
transform: rotate(315deg);
top: 137px;
left: -10px;
z-index: 1;
}
.container .container-box .header .bj-2 {
width: 360px;
height: 128px;
position: absolute;
left: 25px;
bottom: 0;
z-index: 1;
}
.container .container-box .header .music-icon {
width: 34px;
height: 37px;
position: absolute;
top: 194px;
left: 40px;
transform: rotate(345deg);
z-index: 1;
}
.container .container-box .header .bj {
position: absolute;
bottom: 0;
right: 0;
width: 1064px;
height: 320px;
z-index: 1;
}
.container .container-box .header .love-big {
transform: rotate(44deg);
width: 321px;
height: 276px;
position: absolute;
top: 31px;
left: 69px;
z-index: 1;
}
.container .container-box .header .music-score {
width: 240px;
height: 240px;
position: absolute;
top: 1px;
left: 217px;
z-index: 1;
}
.container .container-box .header .robot {
width: 238px;
height: 238px;
position: absolute;
top: 42px;
left: 432px;
z-index: 1;
}
.container .container-box .header .text {
width: 224px;
height: 194px;
position: absolute;
top: 68px;
left: 694px;
z-index: 1;
}
.container .container-box .header .face {
width: 83px;
height: 83px;
position: absolute;
top: -27px;
left: 1080px;
z-index: 1;
}
.container .container-box .header .star-icon-2 {
width: 110px;
height: 110px;
position: absolute;
left: 953px;
top: 70px;
z-index: 1;
}
.container .container-box .header .ai-music {
width: 81px;
height: 28px;
position: absolute;
top: 282px;
left: 1106px;
z-index: 1;
}
.container .container-box .header .record-black {
width: 223px;
height: 233px;
position: absolute;
bottom: 0;
right: -1px;
z-index: 1;
}
.container .container-box .header .record-circle {
position: absolute;
top: 147px;
left: 1039px;
background-color: #72db86;
width: 110px;
height: 110px;
border-radius: 50%;
z-index: 1;
}
.container .container-box .header .record-circle::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #fff;
}
.container .container-box .header .green-glow {
width: 306px;
height: 62px;
position: absolute;
top: 285px;
left: 217px;
}
.container .container-box .header .shadow {
width: 240px;
height: 165px;
position: absolute;
top: 191px;
left: 997px;
}
.container .container-box .list-box {
width: 1200px;
height: 100%;
background-color: rgba(255, 255, 255, 0.10980392);
border-radius: 20px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding: 3px 0;
margin-bottom: 40px;
flex: 1;
}
.container .container-box .list-box .list {
width: 1194px;
height: 100%;
background: linear-gradient(180deg, #7d4bf8 0%, #5241b0 100%);
border-radius: 18px;
}

View File

@@ -0,0 +1,324 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 100%;
background-color: rgba(51, 51, 51, 1);
.flexflex {
display: flex;
}
.flexcenter {
display: flex;
justify-content: center;
align-items: center;
}
.flexjcenter {
display: flex;
justify-content: center;
}
.flexacenter {
display: flex;
align-items: center;
}
.flex1 {
flex: 1;
}
.flexcolumn {
display: flex;
flex-direction: column;
}
.mar1200 {
width: 1200px;
margin: 0 auto;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
overflow: hidden;
}
.bubble-tag {
position: absolute;
padding: 8px 18px;
border-radius: 25px;
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
font-size: 14px;
cursor: pointer;
transition: transform 0.3s ease;
user-select: none;
z-index: 1;
}
.bubble-tag:hover {
transform: scale(1.15);
z-index: 10;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
/* 气泡浮动动画 */
@keyframes float {
0%,
100% {
transform: translateY(0) translateX(0) rotate(0deg);
}
25% {
transform: translateY(-15px) translateX(10px) rotate(1deg);
}
50% {
transform: translateY(-30px) translateX(5px) rotate(0deg);
}
75% {
transform: translateY(-15px) translateX(-10px) rotate(-1deg);
}
}
/* 呼吸效果动画 */
@keyframes pulse {
0% {
opacity: 0.8;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
100% {
opacity: 1;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
}
.container-box {
padding-top: 24px;
height: 100vh;
display: flex;
flex-direction: column;
.logo {
width: 121px;
height: 24px;
margin-bottom: 31px;
}
.header {
position: relative;
width: 1200px;
height: 320px;
// background: linear-gradient(180deg, #7d4bf8 0%, #5241b0 100%);
border-radius: 20px;
margin-bottom: 20px;
&::after {
content: "";
width: 1200px;
height: 320px;
background: linear-gradient(180deg, #7d4bf8 0%, #5241b0 100%);
border-radius: 20px;
position: absolute;
top: 0;
left: 0;
}
.halo {
width: 240px;
height: 214px;
position: absolute;
top: -71px;
left: -59px;
}
.star-icon {
position: absolute;
top: 46px;
left: 51px;
width: 56px;
height: 56px;
z-index: 1;
}
.love-little {
position: absolute;
top: 46px;
left: 51px;
width: 186px;
height: 160px;
transform: rotate(315deg);
top: 137px;
left: -10px;
z-index: 1;
}
.bj-2 {
width: 360px;
height: 128px;
position: absolute;
left: 25px;
bottom: 0;
z-index: 1;
}
.music-icon {
width: 34px;
height: 37px;
position: absolute;
top: 194px;
left: 40px;
transform: rotate(345deg);
z-index: 1;
}
.bj {
position: absolute;
bottom: 0;
right: 0;
width: 1064px;
height: 320px;
z-index: 1;
}
.love-big {
transform: rotate(44deg);
width: 321px;
height: 276px;
position: absolute;
top: 31px;
left: 69px;
z-index: 1;
}
.music-score {
width: 240px;
height: 240px;
position: absolute;
top: 1px;
left: 217px;
z-index: 1;
}
.robot {
width: 238px;
height: 238px;
position: absolute;
top: 42px;
left: 432px;
z-index: 1;
}
.text {
width: 224px;
height: 194px;
position: absolute;
top: 68px;
left: 694px;
z-index: 1;
}
.face {
width: 83px;
height: 83px;
position: absolute;
top: -27px;
left: 1080px;
z-index: 1;
}
.star-icon-2 {
width: 110px;
height: 110px;
position: absolute;
left: 953px;
top: 70px;
z-index: 1;
}
.ai-music {
width: 81px;
height: 28px;
position: absolute;
top: 282px;
left: 1106px;
z-index: 1;
}
.record-black {
width: 223px;
height: 233px;
position: absolute;
bottom: 0;
right: -1px;
z-index: 1;
}
.record-circle {
position: absolute;
top: 147px;
left: 1039px;
background-color: rgb(114, 219, 134);
width: 110px;
height: 110px;
border-radius: 50%;
z-index: 1;
&::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #fff;
}
}
.green-glow {
width: 306px;
height: 62px;
position: absolute;
top: 285px;
left: 217px;
}
.shadow {
width: 240px;
height: 165px;
position: absolute;
top: 191px;
left: 997px;
}
}
.list-box {
width: 1200px;
height: 100%;
background-color: rgba(255, 255, 255, 0.109803921568627);
border-radius: 20px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
padding: 3px 0;
margin-bottom: 40px;
flex: 1;
.list {
width: 1194px;
height: 100%;
background: linear-gradient(180deg, #7d4bf8 0%, #5241b0 100%);
border-radius: 18px;
}
}
}
}

BIN
static/img/ai-music.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

BIN
static/img/green-glow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
static/img/halo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

12
static/img/love-big.svg Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="321px" height="276px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="80.0208971180524" y1="201.54595377948" x2="297.940183742093" y2="22.7556431456892" id="LinearGradient83">
<stop id="Stop84" stop-color="#ff8eba" offset="0" />
<stop id="Stop85" stop-color="#f4458c" offset="1" />
</linearGradient>
</defs>
<g transform="matrix(1 0 0 1 -429 -110 )">
<path d="M 168.381696428571 272.765625 C 166.232142857143 274.921875 163.604910714286 276 160.5 276 C 157.395089285714 276 154.767857142857 274.921875 152.618303571429 272.765625 L 40.8415178571429 164.59375 C 39.6473214285714 163.635416666667 38.0053013392857 162.078125 35.9154575892857 159.921875 C 33.8256138392857 157.765625 30.51171875 153.842447916667 25.9737723214286 148.15234375 C 21.4358258928571 142.462239583333 17.3755580357143 136.622395833333 13.79296875 130.6328125 C 10.2103794642857 124.643229166667 7.01590401785714 117.395833333333 4.20954241071429 108.890625 C 1.40318080357143 100.385416666667 0 92.1197916666666 0 84.09375 C 0 57.7395833333333 7.58314732142857 37.1354166666667 22.7494419642857 22.28125 C 37.9157366071429 7.42708333333333 58.8738839285714 0 85.6238839285714 0 C 93.0279017857143 0 100.581194196429 1.28776041666665 108.283761160714 3.86328125000001 C 115.986328125 6.43880208333331 123.151506696429 9.91276041666665 129.779296875 14.28515625 C 136.407087053571 18.6575520833333 142.109375 22.7604166666666 146.886160714286 26.59375 C 151.662946428571 30.4270833333333 156.200892857143 34.5 160.5 38.8125 C 164.799107142857 34.5 169.337053571429 30.4270833333333 174.113839285714 26.59375 C 178.890625 22.7604166666666 184.592912946429 18.6575520833333 191.220703125 14.28515625 C 197.848493303571 9.91276041666665 205.013671875 6.43880208333331 212.716238839286 3.86328125000001 C 220.418805803571 1.28776041666665 227.972098214286 0 235.376116071429 0 C 262.126116071429 0 283.084263392857 7.42708333333333 298.250558035714 22.28125 C 313.416852678571 37.1354166666667 321 57.7395833333333 321 84.09375 C 321 110.567708333333 307.326450892857 137.520833333333 279.979352678571 164.953125 L 168.381696428571 272.765625 Z " fill-rule="nonzero" fill="url(#LinearGradient83)" stroke="none" transform="matrix(1 0 0 1 429 110 )" />
</g>
</svg>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="186px" height="160px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="49.0234708755539" y1="118.474345856316" x2="180.341391096329" y2="24.280835020015" id="LinearGradient77">
<stop id="Stop78" stop-color="#a943e4" offset="0" />
<stop id="Stop79" stop-color="#f4458c" offset="1" />
</linearGradient>
</defs>
<g transform="matrix(1 0 0 1 -350 -216 )">
<path d="M 97.5669642857143 158.125 C 96.3214285714286 159.375 94.7991071428572 160 93 160 C 91.2008928571429 160 89.6785714285714 159.375 88.4330357142857 158.125 L 23.6651785714286 95.4166666666667 C 22.9732142857143 94.8611111111111 22.0217633928571 93.9583333333333 20.8108258928571 92.7083333333333 C 19.5998883928571 91.4583333333333 17.6796875 89.1840277777778 15.0502232142857 85.8854166666667 C 12.4207589285714 82.5868055555555 10.0680803571429 79.2013888888889 7.9921875 75.7291666666667 C 5.91629464285714 72.2569444444444 4.06529017857143 68.0555555555556 2.43917410714286 63.125 C 0.813058035714286 58.1944444444444 0 53.4027777777778 0 48.75 C 0 33.4722222222222 4.39397321428571 21.5277777777778 13.1819196428571 12.9166666666667 C 21.9698660714286 4.30555555555555 34.1138392857143 0 49.6138392857143 0 C 53.9040178571429 0 58.2806919642857 0.746527777777768 62.7438616071429 2.23958333333334 C 67.20703125 3.73263888888888 71.3588169642857 5.74652777777777 75.19921875 8.28125 C 79.0396205357143 10.8159722222222 82.34375 13.1944444444444 85.1116071428571 15.4166666666667 C 87.8794642857143 17.6388888888889 90.5089285714286 20 93 22.5 C 95.4910714285714 20 98.1205357142857 17.6388888888889 100.888392857143 15.4166666666667 C 103.65625 13.1944444444444 106.960379464286 10.8159722222222 110.80078125 8.28125 C 114.641183035714 5.74652777777777 118.79296875 3.73263888888888 123.256138392857 2.23958333333334 C 127.719308035714 0.746527777777768 132.095982142857 0 136.386160714286 0 C 151.886160714286 0 164.030133928571 4.30555555555555 172.818080357143 12.9166666666667 C 181.606026785714 21.5277777777778 186 33.4722222222222 186 48.75 C 186 64.0972222222222 178.077008928571 79.7222222222222 162.231026785714 95.625 L 97.5669642857143 158.125 Z " fill-rule="nonzero" fill="url(#LinearGradient77)" stroke="none" transform="matrix(1 0 0 1 350 216 )" />
</g>
</svg>

12
static/img/music-icon.svg Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="34px" height="37px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="17" y1="0" x2="17" y2="37" id="LinearGradient86">
<stop id="Stop87" stop-color="#fddf6d" offset="0" />
<stop id="Stop88" stop-color="#f3974a" offset="1" />
</linearGradient>
</defs>
<g transform="matrix(1 0 0 1 -400 -273 )">
<path d="M 33.3802083333333 0.622596153846156 C 33.7934027777778 1.03766025641025 34 1.54166666666667 34 2.13461538461539 L 34 27.0384615384615 C 34 27.7796474358974 33.7491319444444 28.4393028846154 33.2473958333333 29.0174278846154 C 32.7456597222222 29.5955528846154 32.1111111111111 30.0439703525641 31.34375 30.3626802884615 C 30.5763888888889 30.681390224359 29.8127170138889 30.9185697115385 29.052734375 31.07421875 C 28.2927517361111 31.2298677884615 27.5807291666667 31.3076923076923 26.9166666666667 31.3076923076923 C 26.2526041666667 31.3076923076923 25.5405815972222 31.2298677884615 24.7805989583333 31.07421875 C 24.0206163194444 30.9185697115385 23.2569444444444 30.681390224359 22.4895833333333 30.3626802884615 C 21.7222222222222 30.0439703525641 21.0876736111111 29.5955528846154 20.5859375 29.0174278846154 C 20.0842013888889 28.4393028846154 19.8333333333333 27.7796474358974 19.8333333333333 27.0384615384615 C 19.8333333333333 26.2972756410256 20.0842013888889 25.6376201923077 20.5859375 25.0594951923077 C 21.0876736111111 24.4813701923077 21.7222222222222 24.032952724359 22.4895833333333 23.7142427884615 C 23.2569444444444 23.3955328525641 24.0206163194444 23.1583533653846 24.7805989583333 23.0027043269231 C 25.5405815972222 22.8470552884615 26.2526041666667 22.7692307692308 26.9166666666667 22.7692307692308 C 28.4661458333333 22.7692307692308 29.8828125 23.0582932692308 31 23.6364182692308 L 31 11.6959134615385 L 14.1666666666667 16.9657451923077 L 14.1666666666667 32.7307692307692 C 14.1666666666667 33.4719551282051 13.9157986111111 34.1316105769231 13.4140625 34.7097355769231 C 12.9123263888889 35.2878605769231 12.2777777777778 35.7362780448718 11.5104166666667 36.0549879807692 C 10.7430555555556 36.3736979166667 9.97938368055556 36.6108774038462 9.21940104166667 36.7665264423077 C 8.45941840277778 36.9221754807692 7.74739583333333 37 7.08333333333333 37 C 6.41927083333333 37 5.70724826388889 36.9221754807692 4.947265625 36.7665264423077 C 4.18728298611111 36.6108774038462 3.42361111111111 36.3736979166667 2.65625 36.0549879807692 C 1.88888888888889 35.7362780448718 1.25434027777778 35.2878605769231 0.752604166666667 34.7097355769231 C 0.250868055555556 34.1316105769231 0 33.4719551282051 0 32.7307692307692 C 0 31.9895833333333 0.250868055555556 31.3299278846154 0.752604166666667 30.7518028846154 C 1.25434027777778 30.1736778846154 1.88888888888889 29.7252604166667 2.65625 29.4065504807692 C 3.42361111111111 29.0878405448718 4.18728298611111 28.8506610576923 4.947265625 28.6950120192308 C 5.70724826388889 28.5393629807692 6.41927083333333 28.4615384615385 7.08333333333333 28.4615384615385 C 8.6328125 28.4615384615385 10.0494791666667 28.7506009615385 12 29.3287259615385 L 12 7.82692307692308 C 11.3333333333333 7.36738782051282 11.4735243055556 6.94861778846154 11.75390625 6.57061298076923 C 12.0342881944444 6.19260817307692 12.3958333333333 5.92948717948718 12.8385416666667 5.78125 L 31.2552083333333 0.0889423076923074 C 31.4322916666667 0.0296474358974358 31.6388888888889 0 31.875 0 C 32.4652777777778 0 32.9670138888889 0.207532051282051 33.3802083333333 0.622596153846156 Z " fill-rule="nonzero" fill="url(#LinearGradient86)" stroke="none" transform="matrix(1 0 0 1 400 273 )" />
</g>
</svg>

BIN
static/img/music-score.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="223px" height="233px" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1 0 0 1 -1338 -166 )">
<path d="M 116.443349753695 0 C 164.076564175014 0 204.253703364059 27.3522430445635 222 67.5256649406986 L 222 165.474335053222 C 204.253703364059 205.647756955436 164.076564175014 233 116.443349753695 233 C 51.2350738916256 233 0 181.74 0 116.5 C 0 51.26 51.2350738916256 0 116.443349753695 0 Z " fill-rule="nonzero" fill="#000000" stroke="none" transform="matrix(1 0 0 1 1338 166 )" />
</g>
</svg>

BIN
static/img/robot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

BIN
static/img/shadow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
static/img/smiling-face.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="360px" height="128px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="130.108695652174" y1="128" x2="259.239130434783" y2="79.0588235294117" id="LinearGradient74">
<stop id="Stop75" stop-color="#2f1071" offset="0" />
<stop id="Stop76" stop-color="#611ccf" offset="1" />
</linearGradient>
</defs>
<g transform="matrix(1 0 0 1 -385 -271 )">
<path d="M 177 128 L 0 83.6535433070867 L 347 0 L 360 128 L 177 128 Z " fill-rule="nonzero" fill="url(#LinearGradient74)" stroke="none" transform="matrix(1 0 0 1 385 271 )" />
</g>
</svg>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1064px" height="320px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="31.9200000000012" y1="16.9411764705882" x2="1028.53333333334" y2="310.588235294118" id="LinearGradient80">
<stop id="Stop81" stop-color="#c22cc3" offset="0" />
<stop id="Stop82" stop-color="#db2cd9" offset="1" />
</linearGradient>
</defs>
<g transform="matrix(1 0 0 1 -496 -79 )">
<path d="M 184 320 L 0 271 L 111 0 L 1064 247 L 1064 300 C 1064 311.2 1055.2 320 1044 320 L 184 320 Z " fill-rule="nonzero" fill="url(#LinearGradient80)" stroke="none" transform="matrix(1 0 0 1 496 79 )" />
</g>
</svg>

File diff suppressed because one or more lines are too long

BIN
static/img/star-icon-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
static/img/star-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -179,6 +179,8 @@ const search = createApp({
let art = null;
// 开启播放 MV
const openPreview = (id) => {
closeAll();
ajax("https://pujianchaoyin.com/api/getMvDetail", {
id,
}).then((res) => {
@@ -234,10 +236,8 @@ const search = createApp({
const manageAudio = (src, area) => {
const audio = audioPlayer.value;
closeAll();
nextTick(() => {
setTimeout(() => {
if (audio?.src != src) audio.src = src;
console.log("audio", audio);
audio.play().then(() => {
if (area == "works") {
awardAudioList.value.forEach((item) => {
@@ -262,7 +262,7 @@ const search = createApp({
playData.value = { ...zeroOrderStudents.value, area };
}
});
});
}, 500);
};
// 重新播放

151
tag-bubbles copy.html Normal file
View File

@@ -0,0 +1,151 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标签气泡动效</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
overflow: hidden;
}
.container {
width: 100%;
height: 100vh;
position: relative;
padding: 20px;
}
.bubble-tag {
position: absolute;
padding: 8px 18px;
border-radius: 25px;
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
font-size: 14px;
cursor: pointer;
transition: transform 0.3s ease;
user-select: none;
z-index: 1;
}
.bubble-tag:hover {
transform: scale(1.15);
z-index: 10;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
/* 气泡浮动动画 */
@keyframes float {
0%, 100% {
transform: translateY(0) translateX(0) rotate(0deg);
}
25% {
transform: translateY(-15px) translateX(10px) rotate(1deg);
}
50% {
transform: translateY(-30px) translateX(5px) rotate(0deg);
}
75% {
transform: translateY(-15px) translateX(-10px) rotate(-1deg);
}
}
/* 呼吸效果动画 */
@keyframes pulse {
0% {
opacity: 0.8;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
100% {
opacity: 1;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
}
.title {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 24px;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 100;
}
</style>
</head>
<body>
<h1 class="title">标签气泡动效演示</h1>
<div class="container" id="bubbleContainer"></div>
<script>
// 标签数据
const tags = [
"前端开发", "JavaScript", "CSS动画", "HTML5",
"React", "Vue", "TypeScript", "Node.js",
"UI设计", "用户体验", "响应式布局", "性能优化",
"微信小程序", "PWA", "Canvas", "SVG",
"WebGL", "数据可视化", "模块化", "组件化"
];
// 获取容器
const container = document.getElementById('bubbleContainer');
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
// 创建标签并添加动画
tags.forEach((tagText, index) => {
// 创建标签元素
const tag = document.createElement('div');
tag.className = 'bubble-tag';
tag.textContent = tagText;
// 随机位置
const randomX = Math.random() * (containerWidth - 120);
const randomY = Math.random() * (containerHeight - 50);
tag.style.left = `${randomX}px`;
tag.style.top = `${randomY}px`;
// 随机大小
const randomSize = 0.8 + Math.random() * 0.4;
tag.style.transform = `scale(${randomSize})`;
// 随机动画延迟
const delay = Math.random() * 5;
tag.style.animationDelay = `${delay}s`;
// 随机动画持续时间
const duration = 8 + Math.random() * 8;
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
// 随机背景色
const hue = 260 + Math.random() * 40; // 紫色调范围
const lightness = 90 + Math.random() * 5;
tag.style.backgroundColor = `hsla(${hue}, 70%, ${lightness}%, 0.9)`;
// 添加到容器
container.appendChild(tag);
// 添加点击效果
tag.addEventListener('click', () => {
tag.style.animation = 'none';
tag.style.transform = 'scale(1.3)';
setTimeout(() => {
tag.style.animation = `float ${duration}s infinite ease-in-out, pulse 3s infinite alternate`;
tag.style.animationDelay = '0s';
}, 300);
});
});
</script>
</body>
</html>