Animated doll preview

This commit is contained in:
2025-12-20 22:55:05 +08:00
parent a8a578ca3d
commit 7bc73defbc
2 changed files with 155 additions and 33 deletions

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import { onDestroy } from "svelte";
import { onMount, onDestroy } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import { fade } from "svelte/transition";
export let bodyColor: string;
export let outlineColor: string;
@@ -10,6 +9,77 @@
let previewBase64: string | null = null;
let error: string | null = null;
let debounceTimer: number | null = null;
let currentSprite = { x: -3, y: -3 }; // idle sprite initially
let currentSetIndex = 0;
let frameIndex = 0;
let frameTimer: number;
let switchTimeout: number;
// Sprite constants from DesktopPet.svelte
const spriteSets: Record<string, [number, number][]> = {
idle: [[-3, -3]],
alert: [[-7, -3]],
scratchSelf: [
[-5, 0],
[-6, 0],
[-7, 0],
],
scratchWallN: [
[0, 0],
[0, -1],
],
scratchWallS: [
[-7, -1],
[-6, -2],
],
scratchWallE: [
[-2, -2],
[-2, -3],
],
scratchWallW: [
[-4, 0],
[-4, -1],
],
tired: [[-3, -2]],
sleeping: [
[-2, 0],
[-2, -1],
],
N: [
[-1, -2],
[-1, -3],
],
NE: [
[0, -2],
[0, -3],
],
E: [
[-3, 0],
[-3, -1],
],
SE: [
[-5, -1],
[-5, -2],
],
S: [
[-6, -3],
[-7, -2],
],
SW: [
[-5, -3],
[-6, -1],
],
W: [
[-4, -2],
[-4, -3],
],
NW: [
[-1, 0],
[-1, -1],
],
};
const setNames = Object.keys(spriteSets);
function generatePreview() {
error = null;
@@ -39,26 +109,69 @@
}, 300); // Adjust debounce delay as needed (300ms is a common starting point)
}
function updateFrame() {
const setName = setNames[currentSetIndex];
const frames = spriteSets[setName];
const sprite = frames[frameIndex % frames.length];
currentSprite = { x: sprite[0] * 32, y: sprite[1] * 32 };
}
function switchSet() {
currentSetIndex = (currentSetIndex + 1) % setNames.length;
startSet();
}
function startSet() {
const setName = setNames[currentSetIndex];
const frames = spriteSets[setName];
frameIndex = 0;
updateFrame();
const frameDuration = frames.length > 0 ? 1000 / frames.length : 1000;
frameTimer = setInterval(() => {
frameIndex++;
updateFrame();
}, frameDuration);
switchTimeout = setTimeout(() => {
clearInterval(frameTimer);
switchSet();
}, 3000);
}
$: if (bodyColor && outlineColor) {
debouncedGeneratePreview();
}
onMount(() => {
startSet();
});
onDestroy(() => {
if (debounceTimer) clearTimeout(debounceTimer);
if (frameTimer) clearInterval(frameTimer);
if (switchTimeout) clearTimeout(switchTimeout);
});
</script>
<div class="size-32">
<div class="scale-150 p-2">
<div class="size-8">
{#if error}
<div class="size-full flex justify-center items-center text-xs text-error">
<div
class="size-full flex justify-center items-center text-xs text-error"
>
Error
</div>
{:else if previewBase64}
<div
class="size-full bg-cover bg-center pixelated"
style="background-image: url('data:image/gif;base64,{previewBase64}')"
class="pixelated"
style="
width: 32px;
height: 32px;
background-image: url('data:image/gif;base64,{previewBase64}');
background-position: {currentSprite.x}px {currentSprite.y}px;
"
></div>
{:else}
<div class="size-full skeleton" style:background-color={bodyColor}></div>
{/if}
</div>
</div>

View File

@@ -4,7 +4,7 @@
import type { DollDto } from "../../../types/bindings/DollDto";
import type { CreateDollDto } from "../../../types/bindings/CreateDollDto";
import type { UpdateDollDto } from "../../../types/bindings/UpdateDollDto";
import DollPreview from './DollPreview.svelte';
import DollPreview from "./DollPreview.svelte";
let dolls: DollDto[] = [];
let loading = false;
@@ -142,11 +142,14 @@
{:else}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{#each dolls as doll (doll.id)}
<div class="card bg-base-200 shadow-sm">
<div class="card-body p-4">
<div class="card border border-base-200 bg-base-100">
<div class="card-body">
<h3 class="card-title text-base">{doll.name}</h3>
<div class="flex justify-center mb-2">
<DollPreview bodyColor={doll.configuration.colorScheme.body} outlineColor={doll.configuration.colorScheme.outline} />
<DollPreview
bodyColor={doll.configuration.colorScheme.body}
outlineColor={doll.configuration.colorScheme.outline}
/>
</div>
<div class="flex gap-2 text-sm text-base-content/70">
<div class="flex items-center gap-1">
@@ -199,7 +202,10 @@
/>
</div>
<div class="flex justify-center mt-4">
<DollPreview bodyColor={newDollColorBody} outlineColor={newDollColorOutline} />
<DollPreview
bodyColor={newDollColorBody}
outlineColor={newDollColorOutline}
/>
</div>
<div class="form-control w-full mt-2">
<label class="label">
@@ -264,7 +270,10 @@
/>
</div>
<div class="flex justify-center mt-4">
<DollPreview bodyColor={editDollColorBody} outlineColor={editDollColorOutline} />
<DollPreview
bodyColor={editDollColorBody}
outlineColor={editDollColorOutline}
/>
</div>
<div class="form-control w-full mt-2">
<label class="label">