fix(character-art): resolve full character portrait from StarRailRes jsDelivr CDN and add robust portrait fallback handler

This commit is contained in:
CAtlas0784 committed 2026-10-17 13:15:50 +07:00
1 parent b5c4dbec9e
commit ed5e80de30
3 files changed
+82 -12

No files matched your search

+3 -2
View File
@@ -1506,7 +1506,7 @@ input:checked + .slider:before {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
object-position: center 25%;
transition: transform 0.4s ease;
}
@@ -2009,8 +2009,9 @@ input:checked + .slider:before {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
object-position: center 25%;
transition: transform 0.4s ease;
z-index: 1;
}
.overview-portrait-box:hover .overview-portrait-img {
+8 -3
View File
@@ -8,6 +8,7 @@ import {
getLightconeArtUrl,
getLightconeThumbUrl,
handleAvatarImgError,
handleAvatarPortraitError,
handleLcImgError,
GACHABASE_IMAGE_CACHE,
KNOWN_GACHABASE_ASSETS
@@ -15,6 +16,7 @@ import {
// Expose image resolvers & error handlers to window for inline onclick/onerror attributes
window.handleAvatarImgError = handleAvatarImgError;
window.handleAvatarPortraitError = handleAvatarPortraitError;
window.handleLcImgError = handleLcImgError;
window.getAvatarIconUrl = getAvatarIconUrl;
window.getLightconeArtUrl = getLightconeArtUrl;
@@ -2369,7 +2371,7 @@ function getAvatarSplashSrc(aid) {
if (meta?.images?.fullArt) return meta.images.fullArt;
if (GACHABASE_IMAGE_CACHE.avatarSplash[id]) return GACHABASE_IMAGE_CACHE.avatarSplash[id];
if (KNOWN_GACHABASE_ASSETS.avatarSplash[id]) return KNOWN_GACHABASE_ASSETS.avatarSplash[id];
return `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/character_portrait/${id}.png`;
return `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/character_portrait/${id}.png`;
}
@@ -2685,9 +2687,12 @@ function renderModalOverviewTab(avatarId) {
// Portrait Art
if (el.overviewPortraitImg) {
el.overviewPortraitImg.removeAttribute('data-err-step');
el.overviewPortraitImg.style.display = 'block';
el.overviewPortraitImg.style.objectFit = 'cover';
el.overviewPortraitImg.src = getAvatarSplashSrc(aid);
el.overviewPortraitImg.onerror = function() {
handleAvatarImgError(this, aid);
handleAvatarPortraitError(this, aid);
};
}
@@ -4553,7 +4558,7 @@ function renderShowcaseCard(avatarId) {
el.showcaseCardContainer.innerHTML = `
<!-- 1. Left Splash Art Column -->
<div class="showcase-splash-col">
<img class="showcase-splash-img" src="${primaryPortrait}" onerror="if(this.src!=='${fallbackPortrait}')this.src='${fallbackPortrait}';else handleAvatarImgError(this, ${aid});" alt="${meta.name}">
<img class="showcase-splash-img" src="${primaryPortrait}" onerror="handleAvatarPortraitError(this, ${aid})" alt="${meta.name}">
<div class="showcase-splash-overlay">
<div class="showcase-char-name">${meta.name}</div>
<div style="display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap;">
+71 -7
View File
@@ -17,8 +17,6 @@ export const KNOWN_GACHABASE_ASSETS = {
},
avatarSplash: {
1511: 'https://cdn.gachabase.net/hsr/assets/f8b5630e85022ab8b2761e4e3f614b42.png', // Aha Splash Art (2048x1914)
1513: 'https://cdn.gachabase.net/hsr/assets/65b90212f8623ad92243d739ea1fc9ec.png', // Waveflair Splash
1505: 'https://cdn.gachabase.net/hsr/assets/71cbe3943310cb233e8dbe6ef69caeb1.png', // Evanescia Splash
1502: 'https://cdn.gachabase.net/hsr/assets/8ca2530c42791593b72d54a850fce97e.png', // Yao Guang
1501: 'https://cdn.gachabase.net/hsr/assets/09ee6e2618ac2a0dab2aec8d73b0bb01.png', // Sparxie
},
@@ -384,7 +382,7 @@ export function getLightconeArtUrl(lcId) {
if (id === 23065) {
return './assets/lightcones/23065.png';
}
return `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/light_cone_preview/${id}.png`;
return `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/light_cone_preview/${id}.png`;
}
/**
@@ -401,9 +399,69 @@ export function getLightconeThumbUrl(lcId) {
return getLightconeArtUrl(lcId);
}
/**
* Robust error handler for Avatar Full Portrait / Splash Art
* Falls back through:
* 0. jsDelivr StarRailRes character_portrait (2048x2048 splash)
* 1. jsDelivr StarRailRes character_drawing (transparent full body art)
* 2. Raw GitHub character_portrait
* 3. Raw GitHub character_drawing
* 4. Gachabase splash cache
* 5. jsDelivr avatar icon (visual fallback rather than blank)
*/
export function handleAvatarPortraitError(img, avatarId) {
if (!img) return;
const id = parseInt(avatarId, 10);
const step = parseInt(img.getAttribute('data-err-step') || '0', 10);
if (step === 0) {
img.setAttribute('data-err-step', '1');
const portraitUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/character_portrait/${id}.png`;
if (img.src !== portraitUrl) {
img.src = portraitUrl;
return;
}
}
if (step <= 1) {
img.setAttribute('data-err-step', '2');
const drawingUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/character_drawing/${id}.png`;
if (img.src !== drawingUrl) {
img.src = drawingUrl;
return;
}
}
if (step <= 2) {
img.setAttribute('data-err-step', '3');
const rawPortrait = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/character_portrait/${id}.png`;
if (img.src !== rawPortrait) {
img.src = rawPortrait;
return;
}
}
if (step <= 3) {
img.setAttribute('data-err-step', '4');
const gachaSplash = GACHABASE_IMAGE_CACHE.avatarSplash?.[id] || KNOWN_GACHABASE_ASSETS.avatarSplash?.[id];
if (gachaSplash && img.src !== gachaSplash) {
img.src = gachaSplash;
return;
}
}
// Step 4+: Visual fallback to avatar icon rather than ever leaving the portrait black/blank
img.setAttribute('data-err-step', '5');
const iconUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${id}.png`;
if (img.src !== iconUrl) {
img.src = iconUrl;
img.style.objectFit = 'contain';
}
}
/**
* Robust error handler for Avatar images
* Falls back through: Local Asset -> StarRailRes -> Gachabase CDN -> Letter Placeholder
* Falls back through: jsDelivr StarRailRes -> Raw GitHub -> Gachabase CDN -> Letter Placeholder
*/
export function handleAvatarImgError(img, avatarId) {
if (!img) return;
@@ -412,9 +470,12 @@ export function handleAvatarImgError(img, avatarId) {
if (step === 0) {
img.setAttribute('data-err-step', '1');
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/avatar/${id}.png`;
img.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${id}.png`;
} else if (step === 1) {
img.setAttribute('data-err-step', '2');
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/avatar/${id}.png`;
} else if (step === 2) {
img.setAttribute('data-err-step', '3');
const gachaUrl = GACHABASE_IMAGE_CACHE.avatars[id] || KNOWN_GACHABASE_ASSETS.avatars[id];
if (gachaUrl && img.src !== gachaUrl) {
img.src = gachaUrl;
@@ -434,7 +495,7 @@ export function handleAvatarImgError(img, avatarId) {
/**
* Robust error handler for Lightcone images
* Falls back through: Local Asset -> StarRailRes -> Gachabase CDN -> Default Logo
* Falls back through: jsDelivr StarRailRes -> Raw GitHub -> Gachabase CDN -> Default Logo
*/
export function handleLcImgError(img, lcId) {
if (!img) return;
@@ -443,9 +504,12 @@ export function handleLcImgError(img, lcId) {
if (step === 0) {
img.setAttribute('data-err-step', '1');
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/light_cone_preview/${id}.png`;
img.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/light_cone_preview/${id}.png`;
} else if (step === 1) {
img.setAttribute('data-err-step', '2');
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/light_cone_preview/${id}.png`;
} else if (step === 2) {
img.setAttribute('data-err-step', '3');
const gachaUrl = GACHABASE_IMAGE_CACHE.lightcones[id] || KNOWN_GACHABASE_ASSETS.lightcones[id] || GACHABASE_IMAGE_CACHE.lightconeThumbs[id] || KNOWN_GACHABASE_ASSETS.lightconeThumbs[id];
if (gachaUrl && img.src !== gachaUrl) {
img.src = gachaUrl;