This commit is contained in:
2025-11-20 14:10:00 +08:00
parent bca67b7f25
commit 5e2f8ac169

View File

@@ -16,7 +16,7 @@ import * as PIXI from "pixi.js";
const appRef = ref(null); // 存储 PIXI 应用实例 const appRef = ref(null); // 存储 PIXI 应用实例
// 标签数据:包含名称、颜色、大小、位置(角度、半径) // 名称、颜色、大小、位置(角度、半径)
const mockTags = [ const mockTags = [
{ {
name: "医生", name: "医生",
@@ -34,7 +34,7 @@ const mockTags = [
size: 14, size: 14,
opacity: 1, opacity: 1,
angle: -Math.PI / 2, // 12点方向 angle: -Math.PI / 2, // 12点方向
radius: 60, radius: 68,
tailRotation: Math.PI / 2, // 拖尾向下 tailRotation: Math.PI / 2, // 拖尾向下
}, },
{ {
@@ -43,8 +43,8 @@ const mockTags = [
fontColor: 0xf86e6e, fontColor: 0xf86e6e,
size: 11.5, size: 11.5,
opacity: 1, opacity: 1,
angle: -Math.PI / 4, // 1点方向 angle: -Math.PI / 4.2, // 1点方向
radius: 115, radius: 125,
tailRotation: (3 * Math.PI) / 4, // 拖尾向左下 tailRotation: (3 * Math.PI) / 4, // 拖尾向左下
}, },
{ {
@@ -69,10 +69,10 @@ const mockTags = [
}, },
{ {
name: "程序员", name: "程序员",
bgColor: 0xff9d57, bgColor: 0xffd4b6,
fontColor: 0xffffff, fontColor: 0xffffff,
size: 14.5, size: 14.5,
opacity: 0.6, opacity: 1,
angle: Math.PI / 9, // 4点方向 angle: Math.PI / 9, // 4点方向
radius: 120, radius: 120,
tailRotation: (5 * Math.PI) / 4, // 拖尾向左上 tailRotation: (5 * Math.PI) / 4, // 拖尾向左上
@@ -113,7 +113,7 @@ const mockTags = [
fontColor: 0xffffff, fontColor: 0xffffff,
size: 15, size: 15,
opacity: 1, opacity: 1,
angle: (6.3 * Math.PI) / 6, // 10点方向 angle: (6.3 * Math.PI) / 5.9, // 10点方向
radius: 110, radius: 110,
tailRotation: Math.PI / 4, // 拖尾向右下 tailRotation: Math.PI / 4, // 拖尾向右下
}, },
@@ -123,8 +123,8 @@ const mockTags = [
fontColor: 0xfbc55f, fontColor: 0xfbc55f,
size: 13, size: 13,
opacity: 1, opacity: 1,
angle: (7.2 * Math.PI) / 6, // 11点方向 angle: (7.2 * Math.PI) / 5.9, // 11点方向
radius: 115, radius: 120,
tailRotation: Math.PI / 4, // 拖尾向右下 tailRotation: Math.PI / 4, // 拖尾向右下
}, },
]; ];
@@ -135,7 +135,6 @@ onMounted(async () => {
const canvas = document.getElementById("matchCanvas"); const canvas = document.getElementById("matchCanvas");
const sw = canvas.clientWidth; const sw = canvas.clientWidth;
const sh = canvas.clientHeight; const sh = canvas.clientHeight;
const app = new PIXI.Application({ const app = new PIXI.Application({
backgroundAlpha: 0, backgroundAlpha: 0,
antialias: true, antialias: true,
@@ -148,11 +147,11 @@ onMounted(async () => {
canvas.appendChild(app.view); canvas.appendChild(app.view);
// 标签容器(管理所有标签) // 标签容器(
const tagsContainer = new PIXI.Container(); const tagsContainer = new PIXI.Container();
app.stage.addChild(tagsContainer); app.stage.addChild(tagsContainer);
// 存储已放置标签的信息(位置、尺寸、浮动动画参数) // 存储已放置标签的信息
const placedTags = []; const placedTags = [];
for (let i = 0; i < mockTags.length; i++) { for (let i = 0; i < mockTags.length; i++) {
@@ -167,15 +166,12 @@ onMounted(async () => {
let floatSpeed = 0.01 + Math.random() * 0.02; let floatSpeed = 0.01 + Math.random() * 0.02;
let floatRange = 2 + Math.random() * 2; let floatRange = 2 + Math.random() * 2;
// 为标签添加彗星拖尾效果 // 拖尾效果
if (radius > 0) { if (radius > 0) {
// 中心标签不需要拖尾
const tail = createCometTail(tagData.bgColor, tailRotation, tag); const tail = createCometTail(tagData.bgColor, tailRotation, tag);
// 使用 addChildAt
// 修正:使用 addChildAt 将拖尾添加到最底层
tag.addChildAt(tail, 0); tag.addChildAt(tail, 0);
// 拖尾动画
// 为拖尾添加单独的动画
app.ticker.add(() => { app.ticker.add(() => {
if (tail.updateTail) { if (tail.updateTail) {
tail.updateTail(); tail.updateTail();
@@ -183,7 +179,7 @@ onMounted(async () => {
}); });
} }
// 使用PIXI ticker进行动画 // 上下浮动动画
app.ticker.add(() => { app.ticker.add(() => {
floatOffset += floatSpeed; floatOffset += floatSpeed;
tag.y = originalY + Math.sin(floatOffset) * floatRange; tag.y = originalY + Math.sin(floatOffset) * floatRange;
@@ -194,19 +190,11 @@ onMounted(async () => {
} }
}); });
// 销毁时清理资源 // 创建拖尾
onUnmounted(() => {
if (appRef.value) {
appRef.value.destroy(true, true);
appRef.value = null;
}
});
// 创建彗星拖尾效果
function createCometTail(bgColor, tailRotation, tag) { function createCometTail(bgColor, tailRotation, tag) {
const tailGroup = new PIXI.Container(); const tailGroup = new PIXI.Container();
// 拖尾直接放在标签中心位置 // 拖尾位置
tailGroup.x = 0; tailGroup.x = 0;
tailGroup.y = 0; tailGroup.y = 0;
@@ -222,7 +210,7 @@ function createCometTail(bgColor, tailRotation, tag) {
let breathPhase = Math.random() * Math.PI * 2; let breathPhase = Math.random() * Math.PI * 2;
const breathSpeed = 0.04; const breathSpeed = 0.04;
// 更新拖尾呼吸动画 // 更新拖尾呼吸动画
tailGroup.updateTail = () => { tailGroup.updateTail = () => {
breathPhase += breathSpeed; breathPhase += breathSpeed;
const breathScale = 0.85 + 0.15 * Math.sin(breathPhase); const breathScale = 0.85 + 0.15 * Math.sin(breathPhase);
@@ -230,13 +218,12 @@ function createCometTail(bgColor, tailRotation, tag) {
// 绘制梯形拖尾 // 绘制梯形拖尾
const currentLength = baseLength * breathScale; const currentLength = baseLength * breathScale;
// 计算拖尾的四个顶点 // 计算拖尾的四个顶点
// 长边在标签中心水平方向(与标签长边平行) // 长边在标签中心 水平方向
const startLeft = { x: -startWidth / 2, y: 0 }; const startLeft = { x: -startWidth / 2, y: 0 };
const startRight = { x: startWidth / 2, y: 0 }; const startRight = { x: startWidth / 2, y: 0 };
// 短边在拖尾方向保持水平 // 短边在拖尾方向 保持水平
const endCenter = { const endCenter = {
x: Math.cos(tailRotation) * currentLength, x: Math.cos(tailRotation) * currentLength,
y: Math.sin(tailRotation) * currentLength, y: Math.sin(tailRotation) * currentLength,
@@ -276,7 +263,7 @@ function createCometTail(bgColor, tailRotation, tag) {
y: startRight.y * (1 - nextProgress) + endRight.y * nextProgress, y: startRight.y * (1 - nextProgress) + endRight.y * nextProgress,
}; };
// 透明度从0.4渐变到0 // 透明度渐变
const segmentAlpha = 0.4 * (1 - progress); const segmentAlpha = 0.4 * (1 - progress);
tail.beginFill(bgColor, segmentAlpha); tail.beginFill(bgColor, segmentAlpha);
@@ -289,19 +276,19 @@ function createCometTail(bgColor, tailRotation, tag) {
} }
}; };
// 初始绘制 // 绘制
tailGroup.updateTail(); tailGroup.updateTail();
return tailGroup; return tailGroup;
} }
// 创建单个标签(背景+文本) // 创建标签
function createTag(tagData, x, y, placedTags, app, index) { function createTag(tagData, x, y, placedTags, app, index) {
const tagGroup = new PIXI.Container(); const tagGroup = new PIXI.Container();
tagGroup.x = x; tagGroup.x = x;
tagGroup.y = y; tagGroup.y = y;
// 创建文本测量宽度 // 创建文本测量宽度
const text = new PIXI.Text(tagData.name, { const text = new PIXI.Text(tagData.name, {
fontFamily: "Arial", fontFamily: "Arial",
fontSize: tagData.size, fontSize: tagData.size,
@@ -309,7 +296,6 @@ function createTag(tagData, x, y, placedTags, app, index) {
}); });
text.anchor.set(0.5); text.anchor.set(0.5);
// 根据文字个数动态计算宽度
const padding = 10; const padding = 10;
const charWidth = tagData.size * 1.5; const charWidth = tagData.size * 1.5;
const charHeight = tagData.size * 1.3; const charHeight = tagData.size * 1.3;
@@ -320,7 +306,7 @@ function createTag(tagData, x, y, placedTags, app, index) {
const height = charHeight + padding; const height = charHeight + padding;
// 背景(圆角矩形) // 背景
const bg = new PIXI.Graphics(); const bg = new PIXI.Graphics();
bg.beginFill(tagData.bgColor, tagData.opacity ?? 1); bg.beginFill(tagData.bgColor, tagData.opacity ?? 1);
bg.drawRoundedRect(-width / 2, -height / 2, width, height, 20); bg.drawRoundedRect(-width / 2, -height / 2, width, height, 20);
@@ -332,6 +318,13 @@ function createTag(tagData, x, y, placedTags, app, index) {
return tagGroup; return tagGroup;
} }
onUnmounted(() => {
if (appRef.value) {
appRef.value.destroy(true, true);
appRef.value = null;
}
});
</script> </script>
<style scoped> <style scoped>