@remotion/studio
Version:
APIs for interacting with the Remotion Studio
235 lines (234 loc) • 11.4 kB
JavaScript
;
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;