web: the preset tester steps through every frame in its slot
This commit is contained in:
@@ -162,6 +162,7 @@ export function Landing() {
|
|||||||
const tools = useRef<HTMLSpanElement>(null);
|
const tools = useRef<HTMLSpanElement>(null);
|
||||||
const account = useRef<HTMLSpanElement>(null);
|
const account = useRef<HTMLSpanElement>(null);
|
||||||
const [presetId, setPresetId] = useState(PRESETS[0].id);
|
const [presetId, setPresetId] = useState(PRESETS[0].id);
|
||||||
|
const [testerIdx, setTesterIdx] = useState(0);
|
||||||
const [temp, setTemp] = useState(0);
|
const [temp, setTemp] = useState(0);
|
||||||
const [sat, setSat] = useState(100);
|
const [sat, setSat] = useState(100);
|
||||||
const [con, setCon] = useState(100);
|
const [con, setCon] = useState(100);
|
||||||
@@ -254,9 +255,17 @@ export function Landing() {
|
|||||||
// The QR card's payload is the curated photo's own look, as the link the
|
// The QR card's payload is the curated photo's own look, as the link the
|
||||||
// phone app downloads it from. The card's mock address stands in while the
|
// phone app downloads it from. The card's mock address stands in while the
|
||||||
// section holds nothing that has a look to hand out.
|
// section holds nothing that has a look to hand out.
|
||||||
|
// The tester cycles every still the curator put in its slot, so a recipe
|
||||||
|
// can be checked against more than one frame. The built-in sample stands
|
||||||
|
// in on its own while the slot holds nothing.
|
||||||
|
const curated = photos.filter((p) => p.slots.includes('tester')).map((p) => api.photoUrl(p.id));
|
||||||
|
const testers = curated.length ? curated : [SAMPLE(1043)];
|
||||||
const qr = pick('qr');
|
const qr = pick('qr');
|
||||||
return {
|
return {
|
||||||
tester: src('tester', SAMPLE(1043)),
|
testers,
|
||||||
|
// A plain step count on top of one random start keeps the frame random
|
||||||
|
// per load without an effect that would reset it on every re-render.
|
||||||
|
testerStart: Math.floor(Math.random() * testers.length),
|
||||||
creator: src('creator', SAMPLE(1015)),
|
creator: src('creator', SAMPLE(1015)),
|
||||||
qr: qr ? api.photoUrl(qr.id) : SAMPLE(1069),
|
qr: qr ? api.photoUrl(qr.id) : SAMPLE(1069),
|
||||||
qrLink: qr?.hasPreset ? new URL(api.presetUrl(qr.id), window.location.origin).href : `${window.location.origin}/app`,
|
qrLink: qr?.hasPreset ? new URL(api.presetUrl(qr.id), window.location.origin).href : `${window.location.origin}/app`,
|
||||||
@@ -622,7 +631,33 @@ export function Landing() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="lp-view">
|
<div className="lp-view">
|
||||||
<Shot src={slotPhoto.tester} alt={`${preset.name} preview`} filter={preset.filter} />
|
<Shot
|
||||||
|
src={slotPhoto.testers[(slotPhoto.testerStart + testerIdx) % slotPhoto.testers.length]}
|
||||||
|
alt={`${preset.name} preview`}
|
||||||
|
filter={preset.filter}
|
||||||
|
/>
|
||||||
|
{slotPhoto.testers.length > 1 && (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="lp-arrow lp-arrow-prev"
|
||||||
|
data-key="lp-tester-prev"
|
||||||
|
aria-label={c({ en: 'Previous frame', vi: 'Khung trước' })}
|
||||||
|
onClick={() => setTesterIdx((i) => (i - 1 + slotPhoto.testers.length) % slotPhoto.testers.length)}
|
||||||
|
>
|
||||||
|
{'\u2039'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="lp-arrow lp-arrow-next"
|
||||||
|
data-key="lp-tester-next"
|
||||||
|
aria-label={c({ en: 'Next frame', vi: 'Khung sau' })}
|
||||||
|
onClick={() => setTesterIdx((i) => (i + 1) % slotPhoto.testers.length)}
|
||||||
|
>
|
||||||
|
{'\u203a'}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className="lp-hud">
|
<div className="lp-hud">
|
||||||
<span>f/<b>1.8</b></span>
|
<span>f/<b>1.8</b></span>
|
||||||
<span>SS <b>1/500s</b></span>
|
<span>SS <b>1/500s</b></span>
|
||||||
|
|||||||
@@ -343,6 +343,29 @@ html { scroll-behavior: smooth; scroll-padding-top: 116px; }
|
|||||||
.lp-tab[aria-pressed="true"] { border-color: var(--lp-amber); color: var(--lp-ink-amber); background: var(--accent-soft); }
|
.lp-tab[aria-pressed="true"] { border-color: var(--lp-amber); color: var(--lp-ink-amber); background: var(--accent-soft); }
|
||||||
.lp-view { position: relative; border-radius: 16px; overflow: hidden; border: 1px solid var(--lp-line); background: #000; }
|
.lp-view { position: relative; border-radius: 16px; overflow: hidden; border: 1px solid var(--lp-line); background: #000; }
|
||||||
.lp-view img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; transition: filter 380ms ease; }
|
.lp-view img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; transition: filter 380ms ease; }
|
||||||
|
.lp-arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 2;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--lp-line);
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
color: var(--lp-text);
|
||||||
|
font: inherit;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
|
||||||
|
}
|
||||||
|
.lp-arrow:hover, .lp-arrow:focus-visible { background: rgba(0, 0, 0, 0.85); border-color: var(--lp-amber); color: var(--lp-amber); }
|
||||||
|
.lp-arrow-prev { left: 10px; }
|
||||||
|
.lp-arrow-next { right: 10px; }
|
||||||
.lp-hud {
|
.lp-hud {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: auto 0 0 0;
|
inset: auto 0 0 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user