Refactor save and state selection logic; improve empty cover image aspect ratio handling

This commit is contained in:
zurdi
2025-12-17 10:41:12 +00:00
parent 287806267c
commit d0101a4220
3 changed files with 41 additions and 49 deletions

View File

@@ -66,7 +66,7 @@ function onCardClick(save: SaveSchema, event: MouseEvent) {
</script>
<template>
<v-row class="ma-2" no-gutters>
<v-row class="my-2 mx-4" no-gutters>
<v-col class="pa-1">
<v-btn-group divided density="default">
<v-btn
@@ -107,7 +107,7 @@ function onCardClick(save: SaveSchema, event: MouseEvent) {
</v-btn-group>
</v-col>
</v-row>
<v-row v-if="rom.user_saves.length > 0" class="ma-2" no-gutters>
<v-row v-if="rom.user_saves.length > 0" class="my-2 mx-4" no-gutters>
<v-col
v-for="save in rom.user_saves"
:key="save.id"
@@ -125,52 +125,35 @@ function onCardClick(save: SaveSchema, event: MouseEvent) {
@click="(e: MouseEvent) => onCardClick(save, e)"
>
<v-card-text class="pa-2">
<v-row no-gutters>
<v-col cols="12">
<v-img
rounded
:src="
save.screenshot?.download_path ??
getEmptyCoverImage(save.file_name)
<v-slide-x-transition>
<v-btn-group
v-if="isHovering"
class="position-absolute"
density="compact"
style="bottom: 4px; right: 4px"
>
<v-btn drawer :href="save.download_path" download size="small">
<v-icon>mdi-download</v-icon>
</v-btn>
<v-btn
v-if="scopes.includes('assets.write')"
drawer
size="small"
@click="
emitter?.emit('showDeleteSavesDialog', {
rom: props.rom,
saves: [save],
})
"
>
<v-slide-x-transition>
<v-btn-group
v-if="isHovering"
class="position-absolute"
density="compact"
style="bottom: 4px; right: 4px"
>
<v-btn
drawer
:href="save.download_path"
download
size="small"
>
<v-icon>mdi-download</v-icon>
</v-btn>
<v-btn
v-if="scopes.includes('assets.write')"
drawer
size="small"
@click="
emitter?.emit('showDeleteSavesDialog', {
rom: props.rom,
saves: [save],
})
"
>
<v-icon class="text-romm-red"> mdi-delete </v-icon>
</v-btn>
</v-btn-group>
</v-slide-x-transition>
</v-img>
</v-col>
</v-row>
<v-row class="py-2 text-caption" no-gutters>
<v-icon class="text-romm-red"> mdi-delete </v-icon>
</v-btn>
</v-btn-group>
</v-slide-x-transition>
<v-row class="pa-1 text-caption" no-gutters>
{{ save.file_name }}
</v-row>
<v-row class="ga-1" no-gutters>
<v-row class="ga-1 pa-1" no-gutters>
<v-col v-if="save.emulator" cols="12">
<v-chip size="x-small" color="orange" label>
{{ save.emulator }}

View File

@@ -68,7 +68,7 @@ function onCardClick(state: StateSchema, event: MouseEvent) {
</script>
<template>
<v-row class="ma-2" no-gutters>
<v-row class="my-2 mx-4" no-gutters>
<v-col class="pa-1">
<v-btn-group divided density="default">
<v-btn
@@ -109,7 +109,7 @@ function onCardClick(state: StateSchema, event: MouseEvent) {
</v-btn-group>
</v-col>
</v-row>
<v-row v-if="rom.user_states.length > 0" class="ma-2" no-gutters>
<v-row v-if="rom.user_states.length > 0" class="my-2 mx-4" no-gutters>
<v-col
v-for="state in rom.user_states"
:key="state.id"
@@ -133,8 +133,9 @@ function onCardClick(state: StateSchema, event: MouseEvent) {
rounded
:src="
state.screenshot?.download_path ??
getEmptyCoverImage(state.file_name)
getEmptyCoverImage(state.file_name, 16 / 9)
"
:aspect-ratio="16 / 9"
>
<v-slide-x-transition>
<v-btn-group

View File

@@ -67,11 +67,19 @@ export function getUnmatchedCoverImage(name: string): string {
return strToObjUrl(svgString);
}
export function getEmptyCoverImage(name: string): string {
export function getEmptyCoverImage(
name: string,
aspectRatio: number = 2 / 3,
): string {
const tbgs = translatedBGs(name);
const bgr = bgRotation(name);
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 800"><g fill="none" mask="url(#a)"><path fill="#553E98" d="M0 0h600v800H0z"/><path fill="#371f69" d="M0 580c120 10 180-130 270-190s220-70 290-150c80-90 140-210 120-320S520-250 420-310C340 30 250 0 160-20S-10-50-90-20s-150 70-200 140-60 150-85 230c-30 100-130 200-90 290s190 70 270 130c45-340 85-200 195-190" style="transform-origin:center;transform:translate(${tbgs.left.x}px,${tbgs.left.y}px) rotate(${bgr}deg);"/><path fill="#FF9B85" d="M600 1060c100 30 230 40 310-40s30-210 70-310c35-90 130-150 140-240 10-100-10-220-90-290s-200-40-300-60c-90-20-180-60-270-30S310 200 240 260C170 330 50 380 40 480s110 160 170 240c50 70 90 130 150 180 70 60 140 140 230 160" style="transform-origin:center;transform:translate(${tbgs.right.x}px,${tbgs.right.y}px) rotate(${bgr}deg);"/></g><defs><mask id="a"><path fill="#fff" d="M0 0h600v800H0z"/></mask></defs></svg>`;
const width = 600;
const height = width / aspectRatio;
const designHeight = 800; // Original design height
const yOffset = (designHeight - height) / 2;
const svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 ${yOffset} ${width} ${height}"><g fill="none" mask="url(#a)"><path fill="#553E98" d="M0 0h${width}v${designHeight}H0z"/><path fill="#371f69" d="M0 580c120 10 180-130 270-190s220-70 290-150c80-90 140-210 120-320S520-250 420-310C340 30 250 0 160-20S-10-50-90-20s-150 70-200 140-60 150-85 230c-30 100-130 200-90 290s190 70 270 130c45-340 85-200 195-190" style="transform-origin:center;transform:translate(${tbgs.left.x}px,${tbgs.left.y}px) rotate(${bgr}deg);"/><path fill="#FF9B85" d="M600 1060c100 30 230 40 310-40s30-210 70-310c35-90 130-150 140-240 10-100-10-220-90-290s-200-40-300-60c-90-20-180-60-270-30S310 200 240 260C170 330 50 380 40 480s110 160 170 240c50 70 90 130 150 180 70 60 140 140 230 160" style="transform-origin:center;transform:translate(${tbgs.right.x}px,${tbgs.right.y}px) rotate(${bgr}deg);"/></g><defs><mask id="a"><path fill="#fff" d="M0 0h${width}v${designHeight}H0z"/></mask></defs></svg>`;
return strToObjUrl(svgString);
}