refactor(stage-pages): share polaroid page (#2375)

This commit is contained in:
leafyy
2026-08-27 17:10:34 +08:00
committed by GitHub
parent debe4019d1
commit 795c43f0a7
4 changed files with 27 additions and 104 deletions
@@ -1,95 +0,0 @@
<script setup lang="ts">
import { Live2DCanvas, Live2DModel } from '@proj-airi/stage-ui-live2d/components/scenes/live2d'
import { useSettingsStageModel } from '@proj-airi/stage-ui/stores/settings/stage-model'
import { Screen } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { ref, watch } from 'vue'
const live2dCanvasRef = ref<InstanceType<typeof Live2DCanvas>>()
const live2dModelRef = ref<InstanceType<typeof Live2DModel>>()
const settingsStore = useSettingsStageModel()
const { stageModelSelected, stageModelSelectedUrl } = storeToRefs(settingsStore)
const motion = ref<string>('idle')
const motionGroupsList = ref<{
motionName: string
motionIndex: number
fileName: string
}[]>([])
function download(href: string, name: string) {
const link = document.createElement('a')
link.href = href
link.download = name
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
function handleSetMotion(motionName: string) {
live2dModelRef.value?.setMotion(motionName)
}
watch(live2dModelRef, (model) => {
motionGroupsList.value = model?.listMotionGroups() || []
}, { immediate: true })
function handleModelLoaded() {
if (live2dModelRef.value) {
live2dModelRef.value?.setMotion(motion.value)
motionGroupsList.value = live2dModelRef.value.listMotionGroups()
}
}
function handleShot() {
if (!live2dCanvasRef.value || !live2dModelRef.value)
return
const canvas = live2dCanvasRef.value.canvasElement()
const dataUrl = canvas!.toDataURL('image/png')
download(dataUrl, 'live2d-screenshot.png')
}
</script>
<template>
<div flex flex-col items-center gap-4>
<div h-full w-full>
<Screen v-slot="{ width, height }" relative min-h-70dvh>
<Live2DCanvas
ref="live2dCanvasRef"
v-slot="{ app }"
:width="width"
:height="height"
:resolution="3"
rounded-full
>
<Live2DModel
ref="live2dModelRef"
:model-id="stageModelSelected"
:model-src="stageModelSelectedUrl"
:app="app"
:width="width"
:height="height"
:focus-at="{ x: width / 2, y: height / 2 }"
@model-loaded="handleModelLoaded"
/>
</Live2DCanvas>
</Screen>
</div>
<div>
<select v-model="motion" rounded-lg px-3 py-2 @change="handleSetMotion(motion)">
<option v-for="motionItem in motionGroupsList" :key="motionItem.motionIndex" :value="motionItem.motionName">
{{ motionItem.fileName }}
</option>
</select>
</div>
<div border="2px solid black dark:white" flex items-center justify-center rounded-full p-1>
<button
class="h-15 w-15 md:h-18 md:w-18"
bg="black active:neutral-950 dark:white dark:active:neutral-50"
rounded-full outline-none transition-colors duration-200 ease-in-out
@click="handleShot"
/>
</div>
</div>
</template>
@@ -1,14 +1,13 @@
import type { Component } from 'vue'
import PolaroidPage from '@proj-airi/stage-pages/pages/devtools/polaroid.vue'
import { DisplayModelFormat, useDisplayModelsStore } from '@proj-airi/stage-ui/stores/display-models'
import { useSettingsStageModel } from '@proj-airi/stage-ui/stores/settings/stage-model'
import { createPinia } from 'pinia'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'
import { createApp } from 'vue'
import PocketPolaroid from '../../../../stage-pocket/src/pages/devtools/polaroid.vue'
import WebPolaroid from './polaroid.vue'
import 'virtual:uno.css'
let presetArchive: Blob
@@ -110,7 +109,7 @@ async function expectImportedModelPhoto(component: Component, modelId: string) {
}
describe('polaroid imported Live2D model', () => {
it('loads and captures an imported model on the web page', async () => {
it('loads and captures an imported model on the shared page', async () => {
// ROOT CAUSE:
//
// Polaroid passed only the object URL to the Live2D loader. An imported
@@ -120,10 +119,6 @@ describe('polaroid imported Live2D model', () => {
// The loader also kept its mutex locked when the first render had no model
// source. We fixed both paths by passing the selected ID and releasing the
// mutex across every early return.
await expectImportedModelPhoto(WebPolaroid, 'display-model-polaroid-web')
})
it('loads and captures an imported model on the pocket page', async () => {
await expectImportedModelPhoto(PocketPolaroid, 'display-model-polaroid-pocket')
await expectImportedModelPhoto(PolaroidPage, 'display-model-polaroid')
})
})
@@ -1,95 +0,0 @@
<script setup lang="ts">
import { Live2DCanvas, Live2DModel } from '@proj-airi/stage-ui-live2d/components/scenes/live2d'
import { useSettingsStageModel } from '@proj-airi/stage-ui/stores/settings/stage-model'
import { Screen } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { ref, watch } from 'vue'
const live2dCanvasRef = ref<InstanceType<typeof Live2DCanvas>>()
const live2dModelRef = ref<InstanceType<typeof Live2DModel>>()
const settingsStore = useSettingsStageModel()
const { stageModelSelected, stageModelSelectedUrl } = storeToRefs(settingsStore)
const motion = ref<string>('idle')
const motionGroupsList = ref<{
motionName: string
motionIndex: number
fileName: string
}[]>([])
function download(href: string, name: string) {
const link = document.createElement('a')
link.href = href
link.download = name
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
function handleSetMotion(motionName: string) {
live2dModelRef.value?.setMotion(motionName)
}
watch(live2dModelRef, (model) => {
motionGroupsList.value = model?.listMotionGroups() || []
}, { immediate: true })
function handleModelLoaded() {
if (live2dModelRef.value) {
live2dModelRef.value?.setMotion(motion.value)
motionGroupsList.value = live2dModelRef.value.listMotionGroups()
}
}
function handleShot() {
if (!live2dCanvasRef.value || !live2dModelRef.value)
return
const canvas = live2dCanvasRef.value.canvasElement()
const dataUrl = canvas!.toDataURL('image/png')
download(dataUrl, 'live2d-screenshot.png')
}
</script>
<template>
<div flex flex-col items-center gap-4>
<div h-full w-full>
<Screen v-slot="{ width, height }" relative min-h-70dvh>
<Live2DCanvas
ref="live2dCanvasRef"
v-slot="{ app }"
:width="width"
:height="height"
:resolution="3"
rounded-full
>
<Live2DModel
ref="live2dModelRef"
:model-id="stageModelSelected"
:model-src="stageModelSelectedUrl"
:app="app"
:width="width"
:height="height"
:focus-at="{ x: width / 2, y: height / 2 }"
@model-loaded="handleModelLoaded"
/>
</Live2DCanvas>
</Screen>
</div>
<div>
<select v-model="motion" rounded-lg px-3 py-2 @change="handleSetMotion(motion)">
<option v-for="motionItem in motionGroupsList" :key="motionItem.motionIndex" :value="motionItem.motionName">
{{ motionItem.fileName }}
</option>
</select>
</div>
<div border="2px solid black dark:white" flex items-center justify-center rounded-full p-1>
<button
class="h-15 w-15 md:h-18 md:w-18"
bg="black active:neutral-950 dark:white dark:active:neutral-50"
rounded-full outline-none transition-colors duration-200 ease-in-out
@click="handleShot"
/>
</div>
</div>
</template>