55 lines
3.4 KiB
HTML
55 lines
3.4 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<link rel="stylesheet" href="/fonts/fonts.css">
|
|
<!-- Play feature graphic for shots.layout "bleed": the frame /frames/<lang>/feature/<shots.feature.frame> fills the
|
|
1024 x 500 canvas and the logo (shots.logo) or the name (shots.feature.title, else app.name) sits on the left with
|
|
a text shadow and a soft glow only: no band, no scrim. Play may lay its own play button over the centre. -->
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html, body { width: 1024px; height: 500px; overflow: hidden; background: #000; }
|
|
#c { position: relative; width: 1024px; height: 500px; overflow: hidden; }
|
|
#c > img.frame { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.logo { position: absolute; left: 44px; top: 50%; transform: translateY(-50%); max-width: 380px; max-height: 300px;
|
|
filter: drop-shadow(0 4px 6px rgba(4,18,38,.7)) drop-shadow(0 0 28px rgba(4,22,48,.5)); }
|
|
.name { position: absolute; left: 44px; top: 50%; transform: translateY(-50%); max-width: 420px; font-family: var(--tf); font-weight: var(--tw);
|
|
font-size: 84px; line-height: 1; color: #fff; text-wrap: balance;
|
|
text-shadow: 0 0 4px rgba(4,18,38,.92), 0 3px 7px rgba(4,18,38,.92), 0 0 26px rgba(4,22,48,.55), 0 0 50px rgba(4,22,48,.55); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="c"></div>
|
|
<script type="module">
|
|
const q = new URLSearchParams(location.search);
|
|
const lang = q.get('lang'), V = q.get('v') ?? '';
|
|
const job = await (await fetch(`/job.json?lang=${encodeURIComponent(lang)}&feature=1&v=${encodeURIComponent(V)}`)).json();
|
|
const R = document.documentElement.style;
|
|
const X = job.fonts.script;
|
|
R.setProperty('--tf', (job.fonts.titleStack ?? (X ? [X, job.fonts.title] : [job.fonts.title])).map(f => `'${f}'`).join(', ') + ', sans-serif');
|
|
R.setProperty('--tw', X ? 900 : job.fonts.titleWeight ?? 400);
|
|
document.documentElement.lang = lang;
|
|
const c = document.getElementById('c');
|
|
const src = `/frames/${encodeURIComponent(lang)}/feature/${encodeURIComponent(job.frame)}`;
|
|
const name = String(job.featureTitle ?? job.appName).replace(/&/g, '&').replace(/</g, '<');
|
|
c.innerHTML = `<img class="frame" src="${src}">` + (job.logo ? '<img class="logo" src="/logo">' : `<div class="name">${name}</div>`);
|
|
await document.fonts.ready;
|
|
await Promise.all([...document.images].map(i => i.decode().catch(() => {})));
|
|
const overflow = [];
|
|
const img = c.querySelector('img.frame');
|
|
// shots.feature.rotate: turn the frame like a scene frame's rotate; a quarter turn swaps the box so the image still covers the canvas
|
|
const rot = Number(job.rotate) || 0;
|
|
if (rot) {
|
|
const quarter = Math.abs(rot % 180) === 90, W = quarter ? 500 : 1024, H = quarter ? 1024 : 500;
|
|
Object.assign(img.style, { inset: 'auto', left: (512 - W / 2) + 'px', top: (250 - H / 2) + 'px', width: W + 'px', height: H + 'px', transform: `rotate(${rot}deg)` });
|
|
}
|
|
if (!img.naturalWidth) overflow.push(`feature frame ${job.frame} did not load`);
|
|
else if (img.naturalWidth < 1024) overflow.push(`feature frame ${img.naturalWidth}x${img.naturalHeight} is upscaled: capture it at least 1024 px wide`);
|
|
const t = c.querySelector('.name, .logo').getBoundingClientRect();
|
|
if (t.right > 1024 || t.top < 0 || t.bottom > 500) overflow.push('the name runs off the feature graphic');
|
|
document.body.dataset.overflow = JSON.stringify(overflow);
|
|
document.body.dataset.devices = '[]';
|
|
document.body.dataset.ready = '1';
|
|
</script>
|
|
</body>
|
|
</html>
|