fix(zoom): address PR review — preview selected zoom + keyboard a11y

- VideoPlayback: while holding Preview, render the SELECTED zoom at full
  strength regardless of the playhead, instead of whatever findDominantRegion
  returns at currentTime (which is none/another zoom when the playhead is
  outside the selection). Uses getZoomScale/getRotation3D for the region's
  configured scale and 3D preset.
- SettingsPanel: require both onZoomPreviewStart && onZoomPreviewEnd to render
  the button (full lifecycle), and add keyboard support — Space/Enter keydown
  (repeat-guarded) starts preview, keyup/blur ends it.
This commit is contained in:
auberginewly
2026-05-19 11:02:28 +08:00
parent 24ce67b5a7
commit a686fa012a
2 changed files with 36 additions and 6 deletions
+18 -5
View File
@@ -944,13 +944,26 @@ export function SettingsPanel({
</div> </div>
</div> </div>
)} )}
{zoomEnabled && (onZoomPreviewStart || onZoomPreviewEnd) && ( {zoomEnabled && onZoomPreviewStart && onZoomPreviewEnd && (
<Button <Button
type="button" type="button"
onPointerDown={() => onZoomPreviewStart?.()} onPointerDown={() => onZoomPreviewStart()}
onPointerUp={() => onZoomPreviewEnd?.()} onPointerUp={() => onZoomPreviewEnd()}
onPointerLeave={() => onZoomPreviewEnd?.()} onPointerLeave={() => onZoomPreviewEnd()}
onPointerCancel={() => onZoomPreviewEnd?.()} onPointerCancel={() => onZoomPreviewEnd()}
onKeyDown={(e) => {
if ((e.key === " " || e.key === "Enter") && !e.repeat) {
e.preventDefault();
onZoomPreviewStart();
}
}}
onKeyUp={(e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
onZoomPreviewEnd();
}
}}
onBlur={() => onZoomPreviewEnd()}
className="h-7 w-full select-none rounded-md border border-white/[0.08] bg-white/[0.04] text-[10px] font-semibold text-slate-300 transition-all duration-150 ease-out hover:bg-white/[0.08] hover:text-slate-100 active:border-[#34B27B]/50 active:bg-[#34B27B] active:text-white cursor-pointer" className="h-7 w-full select-none rounded-md border border-white/[0.08] bg-white/[0.04] text-[10px] font-semibold text-slate-300 transition-all duration-150 ease-out hover:bg-white/[0.08] hover:text-slate-100 active:border-[#34B27B]/50 active:bg-[#34B27B] active:text-white cursor-pointer"
> >
{t("zoom.previewHold")} {t("zoom.previewHold")}
+18 -1
View File
@@ -59,6 +59,8 @@ import {
DEFAULT_CURSOR_SIZE, DEFAULT_CURSOR_SIZE,
DEFAULT_CURSOR_SMOOTHING, DEFAULT_CURSOR_SMOOTHING,
DEFAULT_ROTATION_3D, DEFAULT_ROTATION_3D,
getRotation3D,
getZoomScale,
isRotation3DIdentity, isRotation3DIdentity,
lerpRotation3D, lerpRotation3D,
rotation3DPerspective, rotation3DPerspective,
@@ -1301,7 +1303,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
let lastTransformIsIdentity = true; let lastTransformIsIdentity = true;
let lastPerspectiveValue = 0; let lastPerspectiveValue = 0;
const ticker = () => { const ticker = () => {
const { region, strength, blendedScale, rotation3D, transition } = findDominantRegion( let { region, strength, blendedScale, rotation3D, transition } = findDominantRegion(
zoomRegionsRef.current, zoomRegionsRef.current,
currentTimeRef.current, currentTimeRef.current,
{ {
@@ -1310,6 +1312,21 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
}, },
); );
// While holding Preview, show the SELECTED zoom (what the settings panel
// is editing) at full strength, independent of the playhead — otherwise
// the preview reflects whatever region sits at currentTime, which may be
// none or a different zoom when the playhead isn't inside the selection.
if (isPreviewingZoomRef.current && selectedZoomIdRef.current) {
const sel = zoomRegionsRef.current.find((r) => r.id === selectedZoomIdRef.current);
if (sel) {
region = sel;
strength = 1;
blendedScale = getZoomScale(sel);
rotation3D = getRotation3D(sel);
transition = null;
}
}
const defaultFocus = DEFAULT_FOCUS; const defaultFocus = DEFAULT_FOCUS;
let targetScaleFactor = 1; let targetScaleFactor = 1;
let targetFocus = defaultFocus; let targetFocus = defaultFocus;