UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

266 lines (265 loc) 11.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Canvas = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const remotion_1 = require("remotion"); const colors_1 = require("../helpers/colors"); const get_asset_metadata_1 = require("../helpers/get-asset-metadata"); const get_effective_translation_1 = require("../helpers/get-effective-translation"); const smooth_zoom_1 = require("../helpers/smooth-zoom"); const use_keybinding_1 = require("../helpers/use-keybinding"); const canvas_ref_1 = require("../state/canvas-ref"); const editor_guides_1 = require("../state/editor-guides"); const editor_zoom_gestures_1 = require("../state/editor-zoom-gestures"); const EditorGuides_1 = __importDefault(require("./EditorGuides")); const EditorRuler_1 = require("./EditorRuler"); const use_is_ruler_visible_1 = require("./EditorRuler/use-is-ruler-visible"); const Preview_1 = require("./Preview"); const ResetZoomButton_1 = require("./ResetZoomButton"); const layout_1 = require("./layout"); const container = { flex: 1, display: 'flex', overflow: 'hidden', position: 'relative', backgroundColor: colors_1.BACKGROUND, }; const resetZoom = { position: 'absolute', top: layout_1.SPACING_UNIT * 2, right: layout_1.SPACING_UNIT * 2, }; const ZOOM_PX_FACTOR = 0.003; const Canvas = ({ canvasContent, size }) => { const { setSize, size: previewSize } = (0, react_1.useContext)(remotion_1.Internals.PreviewSizeContext); const { editorZoomGestures } = (0, react_1.useContext)(editor_zoom_gestures_1.EditorZoomGesturesContext); const keybindings = (0, use_keybinding_1.useKeybinding)(); const config = remotion_1.Internals.useUnsafeVideoConfig(); const areRulersVisible = (0, use_is_ruler_visible_1.useIsRulerVisible)(); const { editorShowGuides } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext); const [assetResolution, setAssetResolution] = (0, react_1.useState)(null); const contentDimensions = (0, react_1.useMemo)(() => { if ((canvasContent.type === 'asset' || canvasContent.type === 'output') && assetResolution && assetResolution.type === 'found') { return assetResolution.dimensions; } if (config) { return { width: config.width, height: config.height }; } return null; }, [assetResolution, config, canvasContent]); const isFit = previewSize.size === 'auto'; const onWheel = (0, react_1.useCallback)((e) => { if (!editorZoomGestures) { return; } if (!size) { return; } if (!contentDimensions || contentDimensions === 'none') { return; } const wantsToZoom = e.ctrlKey || e.metaKey; if (!wantsToZoom && isFit) { return; } e.preventDefault(); setSize((prevSize) => { const scale = remotion_1.Internals.calculateScale({ canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size, }); // Zoom in/out if (wantsToZoom) { const oldSize = prevSize.size === 'auto' ? scale : prevSize.size; const smoothened = (0, smooth_zoom_1.smoothenZoom)(oldSize); const added = smoothened + e.deltaY * ZOOM_PX_FACTOR; const unsmoothened = (0, smooth_zoom_1.unsmoothenZoom)(added); const { centerX, centerY } = (0, get_effective_translation_1.getCenterPointWhileScrolling)({ size, clientX: e.clientX, clientY: e.clientY, compositionWidth: contentDimensions.width, compositionHeight: contentDimensions.height, scale, translation: prevSize.translation, }); const zoomDifference = unsmoothened - oldSize; const uvCoordinatesX = centerX / contentDimensions.width; const uvCoordinatesY = centerY / contentDimensions.height; const correctionLeft = -uvCoordinatesX * (zoomDifference * contentDimensions.width) + (1 - uvCoordinatesX) * zoomDifference * contentDimensions.width; const correctionTop = -uvCoordinatesY * (zoomDifference * contentDimensions.height) + (1 - uvCoordinatesY) * zoomDifference * contentDimensions.height; return { translation: (0, get_effective_translation_1.getEffectiveTranslation)({ translation: { x: prevSize.translation.x - correctionLeft / 2, y: prevSize.translation.y - correctionTop / 2, }, canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale, }), size: unsmoothened, }; } const effectiveTranslation = (0, get_effective_translation_1.getEffectiveTranslation)({ translation: prevSize.translation, canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale, }); // Pan return { ...prevSize, translation: (0, get_effective_translation_1.getEffectiveTranslation)({ translation: { x: effectiveTranslation.x + e.deltaX, y: effectiveTranslation.y + e.deltaY, }, canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale, }), }; }); }, [editorZoomGestures, contentDimensions, isFit, setSize, size]); (0, react_1.useEffect)(() => { const { current } = canvas_ref_1.canvasRef; if (!current) { return; } current.addEventListener('wheel', onWheel, { passive: false }); return () => // @ts-expect-error current.removeEventListener('wheel', onWheel, { passive: false, }); }, [onWheel]); const onReset = (0, react_1.useCallback)(() => { setSize(() => { return { translation: { x: 0, y: 0, }, size: 'auto', }; }); }, [setSize]); const onZoomIn = (0, react_1.useCallback)(() => { if (!contentDimensions || contentDimensions === 'none') { return; } if (!size) { return; } setSize((prevSize) => { const scale = remotion_1.Internals.calculateScale({ canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size, }); return { translation: { x: 0, y: 0, }, size: Math.min(smooth_zoom_1.MAX_ZOOM, scale * 2), }; }); }, [contentDimensions, setSize, size]); const onZoomOut = (0, react_1.useCallback)(() => { if (!contentDimensions || contentDimensions === 'none') { return; } if (!size) { return; } setSize((prevSize) => { const scale = remotion_1.Internals.calculateScale({ canvasSize: size, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size, }); return { translation: { x: 0, y: 0, }, size: Math.max(smooth_zoom_1.MIN_ZOOM, scale / 2), }; }); }, [contentDimensions, setSize, size]); (0, react_1.useEffect)(() => { const resetBinding = keybindings.registerKeybinding({ event: 'keydown', key: '0', commandCtrlKey: false, callback: onReset, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false, }); const zoomIn = keybindings.registerKeybinding({ event: 'keydown', key: '+', commandCtrlKey: false, callback: onZoomIn, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false, }); const zoomOut = keybindings.registerKeybinding({ event: 'keydown', key: '-', commandCtrlKey: false, callback: onZoomOut, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false, }); return () => { resetBinding.unregister(); zoomIn.unregister(); zoomOut.unregister(); }; }, [keybindings, onReset, onZoomIn, onZoomOut]); const fetchMetadata = (0, react_1.useCallback)(async () => { setAssetResolution(null); if (canvasContent.type === 'composition') { return; } const metadata = await (0, get_asset_metadata_1.getAssetMetadata)(canvasContent, canvasContent.type === 'asset'); setAssetResolution(metadata); }, [canvasContent]); (0, react_1.useEffect)(() => { if (canvasContent.type !== 'asset') { return; } const file = (0, remotion_1.watchStaticFile)(canvasContent.asset, () => { fetchMetadata(); }); return () => { file.cancel(); }; }, [canvasContent, fetchMetadata]); (0, react_1.useEffect)(() => { fetchMetadata(); }, [fetchMetadata]); return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { ref: canvas_ref_1.canvasRef, style: container, children: [size ? ((0, jsx_runtime_1.jsx)(Preview_1.VideoPreview, { canvasContent: canvasContent, contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution })) : null, isFit ? null : ((0, jsx_runtime_1.jsx)("div", { style: resetZoom, className: "css-reset", children: (0, jsx_runtime_1.jsx)(ResetZoomButton_1.ResetZoomButton, { onClick: onReset }) })), editorShowGuides && canvasContent.type === 'composition' && ((0, jsx_runtime_1.jsx)(EditorGuides_1.default, { canvasSize: size, contentDimensions: contentDimensions, assetMetadata: assetResolution }))] }), areRulersVisible && ((0, jsx_runtime_1.jsx)(EditorRuler_1.EditorRulers, { contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, containerRef: canvas_ref_1.canvasRef }))] })); }; exports.Canvas = Canvas;