add emoji to frontend

This commit is contained in:
bridge
2026-01-01 15:08:09 +08:00
parent f301d67493
commit 561f1efe21
32 changed files with 141 additions and 2 deletions

View File

@@ -55,8 +55,15 @@ useSharedTicker((delta) => {
} else {
currentY.value = destY
}
// Emoji bobbing animation
emojiTime += delta * 0.05
emojiBob.value = Math.sin(emojiTime) * 5
})
let emojiTime = 0
const emojiBob = ref(0)
function getTexture() {
const gender = (props.avatar.gender || 'male').toLowerCase()
let pid = props.avatar.pic_id
@@ -117,6 +124,57 @@ function handlePointerTap() {
name: props.avatar.name
})
}
const emojiStyle = {
fontFamily: '"Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", sans-serif',
fontSize: 70,
align: 'center',
} as any
const drawEmojiBg = (g: Graphics) => {
g.clear()
const w = 80
const h = 80
const r = 16
const halfW = w / 2
const halfH = h / 2
// 1. Draw all fills first (to cover background)
g.beginPath()
g.roundRect(-halfW, -halfH, w, h, r)
g.fill({ color: 0xffffff, alpha: 1.0 })
// Tail fill
g.beginPath()
g.moveTo(-halfW + 10, halfH) // Start at bottom-left area of body
g.lineTo(-halfW - 10, halfH + 20) // Point pointing down-left
g.lineTo(-halfW, halfH - 10) // Back to left edge of body
g.closePath()
g.fill({ color: 0xffffff, alpha: 1.0 })
// 2. Draw Strokes (Outlines)
// We draw the bubble body stroke
g.roundRect(-halfW, -halfH, w, h, r)
g.stroke({ width: 3, color: 0x000000, alpha: 1.0 })
// We draw the tail stroke
g.beginPath()
g.moveTo(-halfW + 10, halfH)
g.lineTo(-halfW - 10, halfH + 20)
g.lineTo(-halfW, halfH - 10)
g.stroke({ width: 3, color: 0x000000, alpha: 1.0 })
// 3. Clean up the intersection with a white patch
// We fill a small polygon over the line where tail meets body
g.beginPath()
g.moveTo(-halfW + 8, halfH - 2) // Inside body, near bottom
g.lineTo(-halfW - 2, halfH - 12) // Inside body, near left
g.lineTo(-halfW - 8, halfH + 16) // Towards tail tip (but not all the way)
g.lineTo(-halfW + 8, halfH + 2) // Towards tail base
g.closePath()
g.fill({ color: 0xffffff, alpha: 1.0 })
}
</script>
<template>
@@ -141,6 +199,22 @@ function handlePointerTap() {
@render="drawFallback"
/>
<!-- Emoji Bubble -->
<container
v-if="avatar.action_emoji"
:x="tileSize * 0.6"
:y="(getTexture() ? -tileSize * 3.5 : -tileSize * 1.2) + emojiBob"
:z-index="100"
>
<graphics @render="drawEmojiBg" />
<text
:text="avatar.action_emoji"
:style="emojiStyle"
:anchor="0.5"
:scale="1.0"
/>
</container>
<text
:text="avatar.name"
:style="nameStyle"

View File

@@ -76,6 +76,11 @@ async function handleClearObjective() {
</div>
<div class="content-scroll">
<!-- Action State Banner -->
<div v-if="!data.is_dead && data.action_state" class="action-banner">
{{ data.action_state }}
</div>
<!-- Stats Grid -->
<div class="stats-grid">
<StatItem label="境界" :value="data.realm" :sub-value="data.level" />
@@ -226,6 +231,17 @@ async function handleClearObjective() {
border: 1px solid #7a2a2a;
}
.action-banner {
background: rgba(23, 125, 220, 0.15);
color: #aaddff;
padding: 8px;
border-radius: 4px;
text-align: center;
font-size: 13px;
margin-bottom: 8px;
border: 1px solid rgba(23, 125, 220, 0.3);
}
.content-scroll {
flex: 1;
overflow-y: auto;

View File

@@ -36,6 +36,7 @@ export interface Item extends EffectEntity {
export interface AvatarSummary extends EntityBase, Coordinates {
action?: string;
action_emoji?: string;
gender?: string;
pic_id?: number;
is_dead?: boolean;
@@ -49,6 +50,7 @@ export interface AvatarDetail extends EntityBase {
nickname?: string;
appearance: string; // 外貌描述
is_dead?: boolean;
action_state?: string; // 当前正在进行的动作描述
death_info?: {
time: number;
reason: string;