UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

235 lines (234 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.zoomAndPreserveCursor = exports.viewportClientXToScrollContentX = exports.getFrameFromX = exports.getFrameWhileScrollingRight = exports.getFrameIncrementFromWidth = exports.getScrollPositionForCursorOnRightEdge = exports.getScrollPositionForCursorOnLeftEdge = exports.scrollToTimelineXOffset = exports.ensureFrameIsInViewport = exports.isCursorInViewport = exports.getFrameWhileScrollingLeft = exports.canScrollTimelineIntoDirection = void 0; const remotion_1 = require("remotion"); const timeline_layout_1 = require("../../helpers/timeline-layout"); const timeline_refs_1 = require("./timeline-refs"); const TimelineSlider_1 = require("./TimelineSlider"); const canScrollTimelineIntoDirection = () => { const current = timeline_refs_1.scrollableRef.current; const { scrollWidth, scrollLeft, clientWidth } = current; const canScrollRight = scrollWidth - scrollLeft - clientWidth > timeline_layout_1.TIMELINE_PADDING; const canScrollLeft = scrollLeft > timeline_layout_1.TIMELINE_PADDING; return { canScrollRight, canScrollLeft }; }; exports.canScrollTimelineIntoDirection = canScrollTimelineIntoDirection; const SCROLL_INCREMENT = 200; const calculateFrameWhileScrollingRight = ({ durationInFrames, width, scrollLeft, }) => { var _a; return ((0, exports.getFrameFromX)({ clientX: scrollLeft, durationInFrames, width, extrapolate: 'clamp', }) + Math.ceil((((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) - timeline_layout_1.TIMELINE_PADDING) / getFrameIncrement(durationInFrames))); }; const getFrameWhileScrollingLeft = ({ durationInFrames, width, }) => { var _a, _b; const nextFrame = (0, exports.getFrameFromX)({ clientX: ((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollLeft) - SCROLL_INCREMENT, durationInFrames, width, extrapolate: 'clamp', }); const currentFrame = (0, exports.getFrameFromX)({ clientX: (_b = timeline_refs_1.scrollableRef.current) === null || _b === void 0 ? void 0 : _b.scrollLeft, durationInFrames, width, extrapolate: 'clamp', }); // Should go back at least 1 frame, but not less than 0 return Math.max(0, Math.min(currentFrame - 1, nextFrame)); }; exports.getFrameWhileScrollingLeft = getFrameWhileScrollingLeft; const isCursorInViewport = ({ frame, durationInFrames, }) => { var _a, _b; var _c, _d; const width = (_c = (_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollWidth) !== null && _c !== void 0 ? _c : 0; const scrollLeft = (_d = (_b = timeline_refs_1.scrollableRef.current) === null || _b === void 0 ? void 0 : _b.scrollLeft) !== null && _d !== void 0 ? _d : 0; const scrollPosOnRightEdge = (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: frame, durationInFrames, }); const scrollPosOnLeftEdge = (0, exports.getScrollPositionForCursorOnLeftEdge)({ nextFrame: frame, durationInFrames, }); const currentFrameRight = calculateFrameWhileScrollingRight({ durationInFrames, scrollLeft, width, }); return !(scrollPosOnRightEdge >= (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: currentFrameRight, durationInFrames, }) || scrollPosOnLeftEdge < scrollLeft); }; exports.isCursorInViewport = isCursorInViewport; const ensureFrameIsInViewport = ({ direction, durationInFrames, frame, }) => { var _a, _b, _c; var _d, _e; (_a = TimelineSlider_1.redrawTimelineSliderFast.current) === null || _a === void 0 ? void 0 : _a.draw(frame); const width = (_d = (_b = timeline_refs_1.scrollableRef.current) === null || _b === void 0 ? void 0 : _b.scrollWidth) !== null && _d !== void 0 ? _d : 0; const scrollLeft = (_e = (_c = timeline_refs_1.scrollableRef.current) === null || _c === void 0 ? void 0 : _c.scrollLeft) !== null && _e !== void 0 ? _e : 0; if (direction === 'fit-left') { const currentFrameLeft = (0, exports.getFrameFromX)({ clientX: scrollLeft, durationInFrames, width, extrapolate: 'clamp', }); const scrollPos = (0, exports.getScrollPositionForCursorOnLeftEdge)({ nextFrame: frame, durationInFrames, }); const needsToScrollLeft = scrollPos <= (0, exports.getScrollPositionForCursorOnLeftEdge)({ nextFrame: currentFrameLeft, durationInFrames, }); if (needsToScrollLeft) { (0, exports.scrollToTimelineXOffset)(scrollPos); } } if (direction === 'fit-right') { const currentFrameRight = calculateFrameWhileScrollingRight({ durationInFrames, scrollLeft, width, }); const scrollPos = (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: frame, durationInFrames, }); const needsToScrollRight = scrollPos >= (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: currentFrameRight, durationInFrames, }); if (needsToScrollRight) { (0, exports.scrollToTimelineXOffset)(scrollPos); } } if (direction === 'page-right' || direction === 'page-left') { if (!(0, exports.isCursorInViewport)({ frame, durationInFrames })) { (0, exports.scrollToTimelineXOffset)(direction === 'page-left' ? (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: frame, durationInFrames, }) : (0, exports.getScrollPositionForCursorOnLeftEdge)({ nextFrame: frame, durationInFrames, })); } } if (direction === 'center') { const scrollPosOnRightEdge = (0, exports.getScrollPositionForCursorOnRightEdge)({ nextFrame: frame, durationInFrames, }); const scrollPosOnLeftEdge = (0, exports.getScrollPositionForCursorOnLeftEdge)({ nextFrame: frame, durationInFrames, }); (0, exports.scrollToTimelineXOffset)((scrollPosOnLeftEdge + scrollPosOnRightEdge) / 2); } }; exports.ensureFrameIsInViewport = ensureFrameIsInViewport; const scrollToTimelineXOffset = (scrollPos) => { var _a; (_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scroll({ left: scrollPos, }); }; exports.scrollToTimelineXOffset = scrollToTimelineXOffset; const getScrollPositionForCursorOnLeftEdge = ({ nextFrame, durationInFrames, }) => { const frameIncrement = getFrameIncrement(durationInFrames); const scrollPos = frameIncrement * nextFrame; return scrollPos; }; exports.getScrollPositionForCursorOnLeftEdge = getScrollPositionForCursorOnLeftEdge; const getScrollPositionForCursorOnRightEdge = ({ nextFrame, durationInFrames, }) => { var _a, _b; const frameIncrement = getFrameIncrement(durationInFrames); const framesRemaining = durationInFrames - 1 - nextFrame; const fromRight = framesRemaining * frameIncrement + timeline_layout_1.TIMELINE_PADDING; const scrollPos = ((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollWidth) - fromRight - ((_b = timeline_refs_1.scrollableRef.current) === null || _b === void 0 ? void 0 : _b.clientWidth) + timeline_layout_1.TIMELINE_PADDING + 4; // clearfix; return scrollPos; }; exports.getScrollPositionForCursorOnRightEdge = getScrollPositionForCursorOnRightEdge; const getFrameIncrement = (durationInFrames) => { var _a; var _b; const width = (_b = (_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollWidth) !== null && _b !== void 0 ? _b : 0; return (0, exports.getFrameIncrementFromWidth)(durationInFrames, width); }; const getFrameIncrementFromWidth = (durationInFrames, width) => { return (width - timeline_layout_1.TIMELINE_PADDING * 2) / (durationInFrames - 1); }; exports.getFrameIncrementFromWidth = getFrameIncrementFromWidth; const getFrameWhileScrollingRight = ({ durationInFrames, width, }) => { var _a, _b; const nextFrame = calculateFrameWhileScrollingRight({ durationInFrames, width, scrollLeft: ((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollLeft) + SCROLL_INCREMENT, }); const currentFrame = calculateFrameWhileScrollingRight({ durationInFrames, width, scrollLeft: (_b = timeline_refs_1.scrollableRef.current) === null || _b === void 0 ? void 0 : _b.scrollLeft, }); // Should scroll by at least 1 frame, but not overshoot duration return Math.min(durationInFrames - 1, Math.max(nextFrame, currentFrame + 1)); }; exports.getFrameWhileScrollingRight = getFrameWhileScrollingRight; const getFrameFromX = ({ clientX, durationInFrames, width, extrapolate, }) => { const pos = clientX - timeline_layout_1.TIMELINE_PADDING; const frame = Math.round((0, remotion_1.interpolate)(pos, [0, width - timeline_layout_1.TIMELINE_PADDING * 2], [0, durationInFrames - 1], { extrapolateLeft: extrapolate, extrapolateRight: extrapolate, })); return frame; }; exports.getFrameFromX = getFrameFromX; /** * Horizontal position inside the scrollable timeline content (0 … scrollWidth) * for a viewport `clientX`, so pinch-anchoring matches the pointer (not a * rounded frame index). */ const viewportClientXToScrollContentX = ({ clientX, scrollEl, }) => { const rect = scrollEl.getBoundingClientRect(); const clampedClientX = Math.min(Math.max(clientX, rect.left), rect.right); return clampedClientX + scrollEl.scrollLeft - rect.left; }; exports.viewportClientXToScrollContentX = viewportClientXToScrollContentX; const zoomAndPreserveCursor = ({ oldZoom, newZoom, currentFrame, currentDurationInFrames, anchorFrame, anchorContentX, }) => { const ratio = newZoom / oldZoom; if (ratio === 1) { return; } const { current } = timeline_refs_1.scrollableRef; if (!current) { return; } const frameIncrement = getFrameIncrement(currentDurationInFrames); const frameForScroll = anchorFrame !== null && anchorFrame !== void 0 ? anchorFrame : currentFrame; const prevCursorPosition = anchorContentX !== null ? Math.min(Math.max(anchorContentX, 0), current.scrollWidth) : frameIncrement * frameForScroll + timeline_layout_1.TIMELINE_PADDING; const newCursorPosition = ratio * (prevCursorPosition - timeline_layout_1.TIMELINE_PADDING) + timeline_layout_1.TIMELINE_PADDING; current.scrollLeft += newCursorPosition - prevCursorPosition; // Playhead position is synced in `TimelineSlider` `useLayoutEffect` using // measured `sliderAreaRef.clientWidth` so it matches layout after zoom // (avoids fighting React `style` with stale `timelineWidth` during pinch). }; exports.zoomAndPreserveCursor = zoomAndPreserveCursor;