fix web bugs

This commit is contained in:
bridge
2025-11-20 22:24:27 +08:00
parent dfe28bd8ba
commit f4f62052ff
3 changed files with 146 additions and 76 deletions

View File

@@ -0,0 +1,122 @@
<script setup lang="ts">
import { useTextures } from './composables/useTextures'
import { ref, watch, onMounted } from 'vue'
import { Graphics, Ticker } from 'pixi.js'
import { useApplication } from 'vue3-pixi'
const props = defineProps<{
avatar: any
tileSize: number
}>()
const { textures } = useTextures()
const app = useApplication()
// Target position (grid coordinates)
const targetX = ref(props.avatar.x)
const targetY = ref(props.avatar.y)
// Current render position (pixel coordinates)
const currentX = ref(props.avatar.x * props.tileSize + props.tileSize / 2)
const currentY = ref(props.avatar.y * props.tileSize + props.tileSize / 2)
// Watch for prop updates (server ticks)
watch(() => [props.avatar.x, props.avatar.y], ([newX, newY]) => {
targetX.value = newX
targetY.value = newY
})
// Animation Loop
onMounted(() => {
const ticker = new Ticker()
ticker.add((delta) => {
const destX = targetX.value * props.tileSize + props.tileSize / 2
const destY = targetY.value * props.tileSize + props.tileSize / 2
// Simple Lerp for smoothness
// Speed factor: 0.1 means it covers 10% of the remaining distance per frame
const speed = 0.1 * delta.deltaTime
if (Math.abs(destX - currentX.value) > 1) {
currentX.value += (destX - currentX.value) * speed
} else {
currentX.value = destX
}
if (Math.abs(destY - currentY.value) > 1) {
currentY.value += (destY - currentY.value) * speed
} else {
currentY.value = destY
}
})
ticker.start()
// Cleanup manually if needed, though Vue unmount should handle parent destruction
// Ideally we should attach to app.ticker, but local ticker is easier for per-component logic without memory leaks if managed well.
// Better approach: use onTick from vue3-pixi if available, or just requestAnimationFrame
})
function getTexture() {
const key = `${(props.avatar.gender || 'male').toLowerCase()}_${props.avatar.pic_id || 1}`
return textures.value[key]
}
function getScale() {
const tex = getTexture()
if (!tex) return 1
// Scale up: 2.5x tile size (was 1.8x)
return (props.tileSize * 2.5) / Math.max(tex.width, tex.height)
}
const drawFallback = (g: Graphics) => {
g.clear()
g.circle(0, 0, props.tileSize * 0.8) // Increased size
g.fill({ color: props.avatar.gender === 'female' ? 0xffaaaa : 0xaaaaff })
g.stroke({ width: 3, color: 0x000000 })
}
const nameStyle = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: 42, // Increased from 36
fontWeight: 'bold',
fill: 0xffffff,
stroke: { color: 0x000000, width: 6 }, // Thicker stroke
align: 'center',
dropShadow: {
color: '#000000',
blur: 2,
angle: Math.PI / 6,
distance: 2,
alpha: 0.8
}
}
</script>
<template>
<container
:x="currentX"
:y="currentY"
:z-index="Math.floor(currentY)"
>
<sprite
v-if="getTexture()"
:texture="getTexture()"
:anchor-x="0.5"
:anchor-y="0.9"
:scale="getScale()"
/>
<graphics
v-else
@render="drawFallback"
/>
<text
:text="avatar.name"
:style="nameStyle"
:anchor-x="0.5"
:anchor-y="0"
:y="10"
/>
</container>
</template>

View File

@@ -1,74 +1,18 @@
<script setup lang="ts">
import { useGameStore } from '../../stores/game'
import { useTextures } from './composables/useTextures'
import { computed } from 'vue'
import { Graphics } from 'pixi.js'
import AnimatedAvatar from './AnimatedAvatar.vue'
const store = useGameStore()
const { textures } = useTextures()
const TILE_SIZE = 64
function getTexture(avatar: any) {
const key = `${(avatar.gender || 'male').toLowerCase()}_${avatar.pic_id || 1}`
return textures.value[key]
}
function getScale(avatar: any) {
const tex = getTexture(avatar)
if (!tex) return 1
return (TILE_SIZE * 1.8) / Math.max(tex.width, tex.height)
}
// Fallback graphics draw function
const drawFallback = (g: Graphics, avatar: any) => {
g.clear()
g.circle(0, 0, TILE_SIZE * 0.6)
g.fill({ color: avatar.gender === 'female' ? 0xffaaaa : 0xaaaaff })
g.stroke({ width: 2, color: 0x000000 })
}
const nameStyle = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: 24,
fill: 0xffffff,
stroke: { color: 0x000000, width: 4 },
align: 'center'
}
</script>
<template>
<container sortable-children>
<container
<AnimatedAvatar
v-for="avatar in store.avatarList"
:key="avatar.id"
:x="avatar.x * TILE_SIZE + TILE_SIZE / 2"
:y="avatar.y * TILE_SIZE + TILE_SIZE / 2"
:z-index="avatar.y"
>
<!-- Avatar Sprite -->
<sprite
v-if="getTexture(avatar)"
:texture="getTexture(avatar)"
:anchor-x="0.5"
:anchor-y="0.8"
:scale="getScale(avatar)"
/>
<!-- Fallback Graphics -->
<graphics
v-else
@render="g => drawFallback(g, avatar)"
/>
<!-- Name Tag -->
<text
:text="avatar.name"
:style="nameStyle"
:anchor-x="0.5"
:anchor-y="1"
:y="-TILE_SIZE * 0.8"
/>
</container>
:avatar="avatar"
:tile-size="TILE_SIZE"
/>
</container>
</template>

View File

@@ -17,6 +17,7 @@ async function initMap() {
const res = await fetch('/api/map')
const data = await res.json()
const mapData = data.data
// Regions are already provided by backend
regions.value = data.regions || []
if (!mapData) return
@@ -69,9 +70,9 @@ onMounted(() => {
function getRegionStyle(type: string) {
const base = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: type === 'sect' ? 48 : 64,
fontSize: type === 'sect' ? 48 : 64,
fill: type === 'sect' ? 0xffcc00 : (type === 'city' ? 0xccffcc : 0xffffff),
stroke: { color: 0x000000, width: 8, join: 'round' },
stroke: { color: 0x000000, width: 8, join: 'round' },
align: 'center',
dropShadow: {
color: '#000000',
@@ -86,18 +87,21 @@ function getRegionStyle(type: string) {
</script>
<template>
<container ref="mapContainer">
<!-- Regions (Labels) -->
<text
v-for="r in regions"
:key="r.name"
:text="r.name"
:x="r.x * TILE_SIZE + TILE_SIZE / 2"
:y="r.y * TILE_SIZE + TILE_SIZE / 2"
:anchor="0.5"
:style="getRegionStyle(r.type)"
:z-index="100"
/>
<container>
<!-- Tile Layer -->
<container ref="mapContainer" />
<!-- Region Labels Layer (Above tiles) -->
<container :z-index="200">
<text
v-for="r in regions"
:key="r.name"
:text="r.name"
:x="r.x * TILE_SIZE + TILE_SIZE / 2"
:y="r.y * TILE_SIZE + TILE_SIZE / 2"
:anchor="0.5"
:style="getRegionStyle(r.type)"
/>
</container>
</container>
</template>