Route iOS/WebKit to transparent WebP instead of opaque VP9 WebM
WebKit (all iOS browsers + desktop Safari) plays VP9 WebM but renders its alpha as opaque black. SUPPORTS_WEBM_ALPHA now excludes iOS/Safari so those devices fall through to the animated WebP fallback, which IS transparent. Desktop Chromium/Gecko keep the WebM path.
This commit is contained in:
+14
-7
@@ -95,9 +95,17 @@
|
|||||||
const $ = (s) => document.querySelector(s);
|
const $ = (s) => document.querySelector(s);
|
||||||
const TYPE_COLORS = { red: 0xff3b5c, yellow: 0xffc23b, blue: 0x3bb6ff };
|
const TYPE_COLORS = { red: 0xff3b5c, yellow: 0xffc23b, blue: 0x3bb6ff };
|
||||||
|
|
||||||
// Same VP9-alpha capability check as the hunt.
|
// VP9-alpha WebM transparency works on desktop Chromium/Gecko but NOT on
|
||||||
|
// WebKit: all iOS browsers (Safari, Chrome, Firefox) and desktop Safari play
|
||||||
|
// the WebM but render its alpha as opaque black. Those devices must fall
|
||||||
|
// through to the animated WebP (which IS transparent), so exclude WebKit here.
|
||||||
const SUPPORTS_WEBM_ALPHA = (() => {
|
const SUPPORTS_WEBM_ALPHA = (() => {
|
||||||
try {
|
try {
|
||||||
|
const ua = navigator.userAgent;
|
||||||
|
const isIOS = /iPad|iPhone|iPod/.test(ua) ||
|
||||||
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); // iPadOS 13+ poses as Mac
|
||||||
|
const isSafari = /AppleWebKit/.test(ua) && !/Chrome|Chromium|Edg|OPR/.test(ua);
|
||||||
|
if (isIOS || isSafari) return false;
|
||||||
const v = document.createElement('video');
|
const v = document.createElement('video');
|
||||||
return !!v.canPlayType && v.canPlayType('video/webm; codecs="vp9"') !== '';
|
return !!v.canPlayType && v.canPlayType('video/webm; codecs="vp9"') !== '';
|
||||||
} catch { return false; }
|
} catch { return false; }
|
||||||
@@ -215,11 +223,10 @@
|
|||||||
vid.muted = true; vid.loop = true; vid.playsInline = true; vid.autoplay = true; vid.preload = 'auto';
|
vid.muted = true; vid.loop = true; vid.playsInline = true; vid.autoplay = true; vid.preload = 'auto';
|
||||||
vid.src = webm;
|
vid.src = webm;
|
||||||
const tex = new THREE.VideoTexture(vid);
|
const tex = new THREE.VideoTexture(vid);
|
||||||
// VP9-alpha WebMs carry straight (non-premultiplied) alpha. Leave the
|
// Desktop Chromium/Gecko only (WebKit excluded above). RGBAFormat keeps
|
||||||
// texture in the default (linear/no-color-conversion) space — forcing
|
// the alpha channel on frame upload; no SRGB override (it can crush the
|
||||||
// SRGBColorSpace here makes three.js crush the alpha to black. The
|
// alpha); premultipliedAlpha off so transparent regions stay see-through.
|
||||||
// material below blends on the video's own alpha with premultipliedAlpha
|
tex.format = THREE.RGBAFormat;
|
||||||
// off so transparent regions stay see-through.
|
|
||||||
tex.minFilter = THREE.LinearFilter; tex.magFilter = THREE.LinearFilter; tex.generateMipmaps = false;
|
tex.minFilter = THREE.LinearFilter; tex.magFilter = THREE.LinearFilter; tex.generateMipmaps = false;
|
||||||
const mat = new THREE.MeshBasicMaterial({
|
const mat = new THREE.MeshBasicMaterial({
|
||||||
map: tex, transparent: true, side: THREE.DoubleSide,
|
map: tex, transparent: true, side: THREE.DoubleSide,
|
||||||
@@ -272,7 +279,7 @@
|
|||||||
const hasWebm = !!(data.webm_path || data.webm);
|
const hasWebm = !!(data.webm_path || data.webm);
|
||||||
const hasImg = !!(data.image_path || data.image || data.webp_path || data.webp);
|
const hasImg = !!(data.image_path || data.image || data.webp_path || data.webp);
|
||||||
const kind = hasWebm
|
const kind = hasWebm
|
||||||
? ' · WebM' + (SUPPORTS_WEBM_ALPHA ? '' : ' (no VP9-alpha fallback)')
|
? (SUPPORTS_WEBM_ALPHA ? ' · WebM' : ' · WebP (WebKit fallback)')
|
||||||
: hasImg ? ' · image' : ' · procedural wisp';
|
: hasImg ? ' · image' : ' · procedural wisp';
|
||||||
$('#pv-hint').textContent = `${data.name}${kind}`;
|
$('#pv-hint').textContent = `${data.name}${kind}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user