refactor: make Liang skin image-only
This commit is contained in:
@@ -24,7 +24,6 @@ import {
|
||||
const PACKAGE_ID = "dsh-client-liang-intensity-skin";
|
||||
const LOCALE_NAMESPACE = "liang.skin";
|
||||
const ASSET_PREFIX = `/plugins/${PACKAGE_ID}/assets`;
|
||||
const VIDEO_DURATION = 8.033;
|
||||
const BIND_EFFORT_KEY = "dsh-liang-intensity-skin.bind-effort";
|
||||
|
||||
const PORTRAIT_ANCHORS = [
|
||||
@@ -54,6 +53,8 @@ const PORTRAIT_ANCHORS = [
|
||||
{ level: 30, file: "stage-30.png" },
|
||||
] as const;
|
||||
|
||||
const ANCHOR_LEVELS = PORTRAIT_ANCHORS.map((anchor) => anchor.level);
|
||||
|
||||
interface SkinSettings {
|
||||
enabled: boolean;
|
||||
bindEffort: boolean;
|
||||
@@ -139,7 +140,6 @@ class SkinPresenter {
|
||||
private readonly scope: PreferenceStore;
|
||||
private readonly theme: ThemeService;
|
||||
private readonly root: HTMLDivElement;
|
||||
private readonly video: HTMLVideoElement;
|
||||
private readonly poster: HTMLImageElement;
|
||||
private readonly portrait: HTMLImageElement;
|
||||
private readonly preloads: HTMLImageElement[];
|
||||
@@ -161,21 +161,6 @@ class SkinPresenter {
|
||||
this.root.dataset.media = "sequence";
|
||||
this.root.setAttribute("aria-hidden", "true");
|
||||
|
||||
this.video = document.createElement("video");
|
||||
this.video.muted = true;
|
||||
this.video.playsInline = true;
|
||||
this.video.preload = "auto";
|
||||
// A video poster stays painted while a paused video is scrubbed in some
|
||||
// browsers, which would make every effort show the same portrait. Keep the
|
||||
// separate image element as the true load-error fallback instead.
|
||||
const webm = document.createElement("source");
|
||||
webm.src = `${ASSET_PREFIX}/liang-evolution.webm`;
|
||||
webm.type = "video/webm";
|
||||
const mp4 = document.createElement("source");
|
||||
mp4.src = `${ASSET_PREFIX}/liang-evolution.mp4`;
|
||||
mp4.type = "video/mp4";
|
||||
this.video.append(webm, mp4);
|
||||
|
||||
this.poster = document.createElement("img");
|
||||
this.poster.className = "liang-skin-poster";
|
||||
this.poster.src = `${ASSET_PREFIX}/liang-poster.png`;
|
||||
@@ -185,35 +170,26 @@ class SkinPresenter {
|
||||
this.portrait.className = "liang-skin-sequence-frame";
|
||||
this.portrait.alt = "";
|
||||
this.portrait.draggable = false;
|
||||
this.portrait.decoding = "async";
|
||||
this.portrait.addEventListener("error", this.handleSequenceError);
|
||||
|
||||
this.preloads = PORTRAIT_ANCHORS.map(({ file }) => {
|
||||
const image = new Image();
|
||||
image.decoding = "async";
|
||||
image.src = `${ASSET_PREFIX}/portrait-source-v2/${file}`;
|
||||
return image;
|
||||
});
|
||||
|
||||
this.root.append(this.video, this.portrait, this.poster);
|
||||
this.root.append(this.portrait, this.poster);
|
||||
document.body.prepend(this.root);
|
||||
|
||||
this.video.addEventListener("loadedmetadata", this.handleMetadata);
|
||||
this.video.addEventListener("error", this.handleVideoError);
|
||||
this.poster.addEventListener("error", this.handlePosterError);
|
||||
this.video.load();
|
||||
this.unsubscribe = scope.subscribe(() => this.syncSettings());
|
||||
this.syncSettings();
|
||||
}
|
||||
|
||||
private readonly handleMetadata = () => {
|
||||
this.seek(this.frame);
|
||||
};
|
||||
|
||||
private readonly handleVideoError = () => {
|
||||
this.root.dataset.media = "poster";
|
||||
};
|
||||
|
||||
private readonly handleSequenceError = () => {
|
||||
this.root.dataset.media = "video";
|
||||
this.root.dataset.media = "poster";
|
||||
};
|
||||
|
||||
private readonly handlePosterError = () => {
|
||||
@@ -279,7 +255,6 @@ class SkinPresenter {
|
||||
body.style.setProperty("--liang-hover", palette.hover);
|
||||
body.style.setProperty("--liang-portrait-opacity", palette.portraitOpacity);
|
||||
this.updatePortrait(palette.level);
|
||||
this.seek(this.frame);
|
||||
}
|
||||
|
||||
syncNativeTheme(theme: NativeThemeId = paletteForFrame(this.frame).stage === 5 ? "dark" : "light") {
|
||||
@@ -290,7 +265,7 @@ class SkinPresenter {
|
||||
private updatePortrait(level: number) {
|
||||
const { lowerIndex, upperIndex, mix } = portraitBlendForLevel(
|
||||
level,
|
||||
PORTRAIT_ANCHORS.map((anchor) => anchor.level),
|
||||
ANCHOR_LEVELS,
|
||||
);
|
||||
const lower = PORTRAIT_ANCHORS[lowerIndex];
|
||||
const upper = PORTRAIT_ANCHORS[upperIndex];
|
||||
@@ -299,13 +274,6 @@ class SkinPresenter {
|
||||
if (this.portrait.getAttribute("src") !== source) this.portrait.src = source;
|
||||
}
|
||||
|
||||
private seek(frame: number) {
|
||||
if (this.video.readyState < 1 || this.video.duration === 0) return;
|
||||
const duration = Number.isFinite(this.video.duration) ? this.video.duration : VIDEO_DURATION;
|
||||
const target = Math.min(Math.max(0, duration - 0.001), (frame / PREVIEW_MAX_FRAME) * duration);
|
||||
if (Math.abs(this.video.currentTime - target) > 0.025) this.video.currentTime = target;
|
||||
}
|
||||
|
||||
async choose(enabled: boolean) {
|
||||
this.setEnabled(enabled);
|
||||
try {
|
||||
@@ -321,9 +289,6 @@ class SkinPresenter {
|
||||
this.disposed = true;
|
||||
this.unsubscribe();
|
||||
if (this.raf !== 0) cancelAnimationFrame(this.raf);
|
||||
this.video.pause();
|
||||
this.video.removeEventListener("loadedmetadata", this.handleMetadata);
|
||||
this.video.removeEventListener("error", this.handleVideoError);
|
||||
this.portrait.removeEventListener("error", this.handleSequenceError);
|
||||
this.poster.removeEventListener("error", this.handlePosterError);
|
||||
for (const image of this.preloads) image.src = "";
|
||||
|
||||
@@ -13,7 +13,6 @@ body[data-liang-skin="on"] .liang-skin-backdrop {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.liang-skin-backdrop video,
|
||||
.liang-skin-backdrop img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -34,7 +33,6 @@ body[data-liang-skin="on"] .liang-skin-backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.liang-skin-backdrop[data-media="sequence"] video,
|
||||
.liang-skin-backdrop[data-media="sequence"] .liang-skin-poster {
|
||||
display: none;
|
||||
}
|
||||
@@ -44,12 +42,6 @@ body[data-liang-skin="on"] .liang-skin-backdrop {
|
||||
opacity: var(--liang-portrait-opacity, 0.82);
|
||||
}
|
||||
|
||||
.liang-skin-backdrop[data-media="video"] .liang-skin-sequence-frame,
|
||||
.liang-skin-backdrop[data-media="video"] .liang-skin-poster {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.liang-skin-backdrop[data-media="poster"] video,
|
||||
.liang-skin-backdrop[data-media="poster"] .liang-skin-sequence-frame {
|
||||
display: none;
|
||||
}
|
||||
@@ -58,7 +50,6 @@ body[data-liang-skin="on"] .liang-skin-backdrop {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.liang-skin-backdrop[data-media="color"] video,
|
||||
.liang-skin-backdrop[data-media="color"] img {
|
||||
display: none;
|
||||
}
|
||||
@@ -623,7 +614,6 @@ body[data-liang-skin="on"] .liang-effort-control__range::-webkit-slider-thumb {
|
||||
width: 92px;
|
||||
}
|
||||
|
||||
.liang-skin-backdrop video,
|
||||
.liang-skin-backdrop img {
|
||||
right: -18vw;
|
||||
width: 92vw;
|
||||
@@ -634,7 +624,6 @@ body[data-liang-skin="on"] .liang-effort-control__range::-webkit-slider-thumb {
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.liang-skin-backdrop,
|
||||
.liang-skin-backdrop video,
|
||||
.liang-skin-backdrop img,
|
||||
body[data-liang-skin="on"] button,
|
||||
body[data-liang-skin="on"] input,
|
||||
|
||||
@@ -9,8 +9,6 @@ export const SETTINGS_NAMESPACE = "liang-intensity-skin";
|
||||
export const ASSET_PREFIX = `/plugins/${PACKAGE_ID}/assets/`;
|
||||
|
||||
const ASSET_SPECS = [
|
||||
["liang-evolution.webm", "video/webm"],
|
||||
["liang-evolution.mp4", "video/mp4"],
|
||||
["liang-poster.png", "image/png"],
|
||||
["portrait-source-v2/stage-00.png", "image/png"],
|
||||
["portrait-source-v2/level-01.png", "image/png"],
|
||||
|
||||
Reference in New Issue
Block a user