feat(zoom): add hold-to-preview button for zoom focus editing
When a zoom region is selected and paused, the editor shows the full un-zoomed frame for focus-point placement. This adds a press-and-hold "Preview" button so editors can momentarily see the zoomed result at the current focus + depth — like a before/after compare — without entering playback. - VideoPlayback: new transient isPreviewingZoom prop; shouldShowUnzoomedView now also requires !isPreviewingZoom, so the zoom transform is applied at the playhead while previewing - VideoEditor: isPreviewingZoom state wired to VideoPlayback and to onZoomPreviewStart/End handlers - SettingsPanel: hold button in the zoom controls (pointer down/up/leave/ cancel) - i18n: zoom.previewHold added across all 11 locales Prototype for #612 — placement (panel vs overlay) and hold-vs-toggle still open for maintainer direction.
This commit is contained in:
@@ -230,6 +230,8 @@ interface SettingsPanelProps {
|
||||
selectedZoomCustomScale?: number | null;
|
||||
onZoomCustomScaleChange?: (scale: number) => void;
|
||||
onZoomCustomScaleCommit?: () => void;
|
||||
onZoomPreviewStart?: () => void;
|
||||
onZoomPreviewEnd?: () => void;
|
||||
selectedZoomFocusMode?: ZoomFocusMode | null;
|
||||
onZoomFocusModeChange?: (mode: ZoomFocusMode) => void;
|
||||
selectedZoomFocus?: ZoomFocus | null;
|
||||
@@ -359,6 +361,8 @@ export function SettingsPanel({
|
||||
selectedZoomCustomScale,
|
||||
onZoomCustomScaleChange,
|
||||
onZoomCustomScaleCommit,
|
||||
onZoomPreviewStart,
|
||||
onZoomPreviewEnd,
|
||||
selectedZoomFocusMode,
|
||||
onZoomFocusModeChange,
|
||||
selectedZoomFocus,
|
||||
@@ -940,6 +944,18 @@ export function SettingsPanel({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{zoomEnabled && (onZoomPreviewStart || onZoomPreviewEnd) && (
|
||||
<Button
|
||||
type="button"
|
||||
onPointerDown={() => onZoomPreviewStart?.()}
|
||||
onPointerUp={() => onZoomPreviewEnd?.()}
|
||||
onPointerLeave={() => onZoomPreviewEnd?.()}
|
||||
onPointerCancel={() => 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"
|
||||
>
|
||||
{t("zoom.previewHold")}
|
||||
</Button>
|
||||
)}
|
||||
{zoomEnabled &&
|
||||
selectedZoomFocusMode !== "auto" &&
|
||||
selectedZoomFocus &&
|
||||
|
||||
@@ -194,6 +194,7 @@ export default function VideoEditor() {
|
||||
const durationRef = useRef(duration);
|
||||
durationRef.current = duration;
|
||||
const [selectedZoomId, setSelectedZoomId] = useState<string | null>(null);
|
||||
const [isPreviewingZoom, setIsPreviewingZoom] = useState(false);
|
||||
const [selectedTrimId, setSelectedTrimId] = useState<string | null>(null);
|
||||
const [selectedSpeedId, setSelectedSpeedId] = useState<string | null>(null);
|
||||
const [selectedAnnotationId, setSelectedAnnotationId] = useState<string | null>(null);
|
||||
@@ -2112,6 +2113,7 @@ export default function VideoEditor() {
|
||||
cursorMotionBlur={cursorMotionBlur}
|
||||
cursorClickBounce={cursorClickBounce}
|
||||
cursorClipToBounds={cursorClipToBounds}
|
||||
isPreviewingZoom={isPreviewingZoom}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2147,6 +2149,8 @@ export default function VideoEditor() {
|
||||
}
|
||||
onZoomCustomScaleChange={handleZoomCustomScaleChange}
|
||||
onZoomCustomScaleCommit={handleZoomCustomScaleCommit}
|
||||
onZoomPreviewStart={() => setIsPreviewingZoom(true)}
|
||||
onZoomPreviewEnd={() => setIsPreviewingZoom(false)}
|
||||
selectedZoomFocusMode={
|
||||
selectedZoomId
|
||||
? (zoomRegions.find((z) => z.id === selectedZoomId)?.focusMode ?? "manual")
|
||||
|
||||
@@ -149,6 +149,9 @@ interface VideoPlaybackProps {
|
||||
cursorMotionBlur?: number;
|
||||
cursorClickBounce?: number;
|
||||
cursorClipToBounds?: boolean;
|
||||
// When true, render the selected zoom at the playhead even while paused —
|
||||
// lets the editor preview the zoom effect without leaving the focus-edit view.
|
||||
isPreviewingZoom?: boolean;
|
||||
}
|
||||
|
||||
export interface VideoPlaybackRef {
|
||||
@@ -270,6 +273,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
cursorMotionBlur = DEFAULT_CURSOR_MOTION_BLUR,
|
||||
cursorClickBounce = DEFAULT_CURSOR_CLICK_BOUNCE,
|
||||
cursorClipToBounds = false,
|
||||
isPreviewingZoom = false,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@@ -341,6 +345,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
const cursorMotionBlurRef = useRef(cursorMotionBlur);
|
||||
const cursorClickBounceRef = useRef(cursorClickBounce);
|
||||
const cursorClipToBoundsRef = useRef(cursorClipToBounds);
|
||||
const isPreviewingZoomRef = useRef(isPreviewingZoom);
|
||||
const motionBlurStateRef = useRef<MotionBlurState>(createMotionBlurState());
|
||||
const onTimeUpdateRef = useRef(onTimeUpdate);
|
||||
const onPlayStateChangeRef = useRef(onPlayStateChange);
|
||||
@@ -832,6 +837,10 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
cursorClipToBoundsRef.current = cursorClipToBounds;
|
||||
}, [cursorClipToBounds]);
|
||||
|
||||
useEffect(() => {
|
||||
isPreviewingZoomRef.current = isPreviewingZoom;
|
||||
}, [isPreviewingZoom]);
|
||||
|
||||
// Sync cursor overlay config when settings change
|
||||
useEffect(() => {
|
||||
const overlay = cursorOverlayRef.current;
|
||||
@@ -1309,7 +1318,8 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
// If a zoom is selected but video is not playing, show default unzoomed view
|
||||
const selectedId = selectedZoomIdRef.current;
|
||||
const hasSelectedZoom = selectedId !== null;
|
||||
const shouldShowUnzoomedView = hasSelectedZoom && !isPlayingRef.current;
|
||||
const shouldShowUnzoomedView =
|
||||
hasSelectedZoom && !isPlayingRef.current && !isPreviewingZoomRef.current;
|
||||
|
||||
if (region && strength > 0 && !shouldShowUnzoomedView) {
|
||||
const zoomScale = blendedScale ?? ZOOM_DEPTH_SCALES[region.depth];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "اضغط مع الاستمرار للمعاينة",
|
||||
"level": "مستوى التكبير",
|
||||
"selectRegion": "حدد منطقة التكبير للتعديل",
|
||||
"deleteZoom": "حذف التكبير",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Hold to preview",
|
||||
"level": "Zoom Level",
|
||||
"customScale": "Custom Zoom",
|
||||
"selectRegion": "Select a zoom region to adjust",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Mantener para previsualizar",
|
||||
"level": "Nivel de zoom",
|
||||
"selectRegion": "Selecciona una región de zoom para ajustar",
|
||||
"deleteZoom": "Eliminar zoom",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Maintenir pour prévisualiser",
|
||||
"level": "Niveau de zoom",
|
||||
"selectRegion": "Sélectionnez une région de zoom à ajuster",
|
||||
"deleteZoom": "Supprimer le zoom",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "押している間プレビュー",
|
||||
"level": "ズーム倍率",
|
||||
"selectRegion": "ズーム範囲を選択して調整",
|
||||
"deleteZoom": "ズームを削除",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "누르고 있으면 미리보기",
|
||||
"level": "줌 레벨",
|
||||
"selectRegion": "조정할 줌 구간을 선택하세요",
|
||||
"deleteZoom": "줌 삭제",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Удерживайте для предпросмотра",
|
||||
"level": "Уровень масштабирования",
|
||||
"selectRegion": "Выберите область масштабирования для настройки",
|
||||
"deleteZoom": "Удалить масштабирование",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Önizlemek için basılı tutun",
|
||||
"level": "Yakınlaştırma Seviyesi",
|
||||
"selectRegion": "Ayarlamak için bir yakınlaştırma bölgesi seçin",
|
||||
"deleteZoom": "Yakınlaştırmayı Sil",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "Giữ để xem trước",
|
||||
"level": "Mức độ thu phóng",
|
||||
"selectRegion": "Chọn vùng thu phóng để điều chỉnh",
|
||||
"deleteZoom": "Xóa thu phóng",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "按住预览",
|
||||
"level": "缩放级别",
|
||||
"selectRegion": "选择要调整的缩放区域",
|
||||
"deleteZoom": "删除缩放",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"zoom": {
|
||||
"previewHold": "按住預覽",
|
||||
"level": "縮放級別",
|
||||
"selectRegion": "選擇要調整的縮放區域",
|
||||
"deleteZoom": "刪除縮放",
|
||||
|
||||
Reference in New Issue
Block a user