use v-img with webp and fallback in console mode

This commit is contained in:
Georges-Antoine Assi
2025-09-12 15:36:59 -04:00
parent e6de576930
commit c5758bceeb
8 changed files with 73 additions and 36 deletions

View File

@@ -75,7 +75,6 @@
--console-system-card-text: var(--console-text-primary);
/* Game Cards */
--console-game-card-bg: linear-gradient(180deg, #1f2430 0%, #2b3242 100%);
--console-game-card-text: var(--console-text-primary);
--console-game-card-focus-border: var(--console-accent-secondary);
--console-game-card-star: var(--console-accent-secondary);

View File

@@ -75,7 +75,6 @@
--console-system-card-text: var(--console-text-secondary);
/* Game Cards */
--console-game-card-bg: linear-gradient(180deg, #212121 0%, #303030 100%);
--console-game-card-text: var(--console-text-secondary);
--console-game-card-focus-border: var(--console-accent-secondary);
--console-game-card-star: var(--console-accent-secondary);

View File

@@ -14,9 +14,11 @@ import { ROUTES } from "@/plugins/router";
import type { CollectionType } from "@/stores/collections";
import storeGalleryView from "@/stores/galleryView";
import storeHeartbeat from "@/stores/heartbeat";
import { getCollectionCoverImage, getFavoriteCoverImage } from "@/utils/covers";
const EXTENSION_REGEX = /\.png|\.jpg|\.jpeg$/;
import {
getCollectionCoverImage,
getFavoriteCoverImage,
EXTENSION_REGEX,
} from "@/utils/covers";
const props = withDefaults(
defineProps<{

View File

@@ -3,9 +3,11 @@ import { computed, ref, watchEffect } from "vue";
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
import { type CollectionType } from "@/stores/collections";
import storeHeartbeat from "@/stores/heartbeat";
import { getCollectionCoverImage, getFavoriteCoverImage } from "@/utils/covers";
const EXTENSION_REGEX = /\.png|\.jpg|\.jpeg$/;
import {
getCollectionCoverImage,
getFavoriteCoverImage,
EXTENSION_REGEX,
} from "@/utils/covers";
const props = withDefaults(
defineProps<{

View File

@@ -26,9 +26,11 @@ import storePlatforms from "@/stores/platforms";
import storeRoms from "@/stores/roms";
import { type SimpleRom } from "@/stores/roms";
import type { Events } from "@/types/emitter";
import { getMissingCoverImage, getUnmatchedCoverImage } from "@/utils/covers";
const EXTENSION_REGEX = /\.png|\.jpg|\.jpeg$/;
import {
getMissingCoverImage,
getUnmatchedCoverImage,
EXTENSION_REGEX,
} from "@/utils/covers";
const props = withDefaults(
defineProps<{

View File

@@ -14,9 +14,11 @@ import {
} from "@/console/composables/useElementRegistry";
import type { CollectionType } from "@/stores/collections";
import storeHeartbeat from "@/stores/heartbeat";
import { getCollectionCoverImage, getFavoriteCoverImage } from "@/utils/covers";
const EXTENSION_REGEX = /\.png|\.jpg|\.jpeg$/;
import {
getCollectionCoverImage,
getFavoriteCoverImage,
EXTENSION_REGEX,
} from "@/utils/covers";
const props = defineProps<{
collection: CollectionType;

View File

@@ -1,11 +1,18 @@
<script setup lang="ts">
import { computed, onMounted, useTemplateRef, watch } from "vue";
import Skeleton from "@/components/common/Game/Card/Skeleton.vue";
import {
recentElementRegistry,
gamesListElementRegistry,
} from "@/console/composables/useElementRegistry";
import storeCollections from "@/stores/collections";
import storeHeartbeat from "@/stores/heartbeat";
import type { SimpleRom } from "@/stores/roms";
import {
EXTENSION_REGEX,
getMissingCoverImage,
getUnmatchedCoverImage,
} from "@/utils/covers";
const props = defineProps<{
rom: SimpleRom;
@@ -16,13 +23,32 @@ const props = defineProps<{
registry?: "recent" | "gamesList";
}>();
const coverSrc = computed(
() =>
props.rom.path_cover_large ||
props.rom.path_cover_small ||
props.rom.url_cover ||
"",
const heartbeatStore = storeHeartbeat();
const isWebpEnabled = computed(
() => heartbeatStore.value.TASKS?.ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP,
);
const largeCover = computed(() => {
const pathCoverLarge = isWebpEnabled.value
? props.rom.path_cover_large?.replace(EXTENSION_REGEX, ".webp")
: props.rom.path_cover_large;
return pathCoverLarge || "";
});
const smallCover = computed(() => {
const pathCoverSmall = isWebpEnabled.value
? props.rom.path_cover_small?.replace(EXTENSION_REGEX, ".webp")
: props.rom.path_cover_small;
return pathCoverSmall || "";
});
const fallbackCoverImage = computed(() =>
props.rom.is_identified
? getMissingCoverImage(props.rom.name || props.rom.slug || "")
: getUnmatchedCoverImage(props.rom.name || props.rom.slug || ""),
);
const emit = defineEmits([
"click",
"mouseenter",
@@ -43,8 +69,8 @@ const isFavorited = computed(() => {
watch(
() => props.selected,
(isSelected) => {
if (isSelected && coverSrc.value) {
emit("select", coverSrc.value);
if (isSelected && largeCover.value) {
emit("select", largeCover.value);
} else if (isSelected) {
emit("deselect");
}
@@ -75,23 +101,26 @@ onMounted(() => {
@click="emit('click')"
@focus="emit('focus')"
>
<div
class="w-full h-[350px] relative overflow-hidden rounded"
:style="{ background: 'var(--console-game-card-bg)' }"
>
<img
v-if="coverSrc"
<div class="w-full h-[350px] relative overflow-hidden rounded">
<v-img
cover
class="w-full h-full object-cover"
:src="coverSrc"
:src="largeCover || fallbackCoverImage"
:alt="rom.name || 'Game'"
@load="emit('loaded')"
@error="emit('loaded')"
/>
<div
v-else
class="w-full h-full"
:style="{ background: 'var(--console-game-card-bg)' }"
/>
>
<template #placeholder>
<v-img cover eager :src="smallCover || fallbackCoverImage">
<template #placeholder>
<Skeleton :platform-id="rom.platform_id" type="image" />
</template>
</v-img>
</template>
<template #error>
<v-img cover eager :src="fallbackCoverImage" />
</template>
</v-img>
<!-- Selected highlight radial glow -->
<div
class="absolute inset-0 opacity-0 pointer-events-none"
@@ -116,7 +145,7 @@ onMounted(() => {
</div>
<div
v-if="!coverSrc"
v-if="!largeCover && !smallCover"
class="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-b from-transparent to-black/75 text-[var(--console-game-card-text)] text-sm leading-tight z-10"
>
<div class="font-medium truncate">

View File

@@ -1,3 +1,5 @@
export const EXTENSION_REGEX = /\.png|\.jpg|\.jpeg$/;
function hashString(str: string) {
let h = 0;
for (let i = 0; i < str.length; i++) {