@remotion/motion-blur
Version:
Motion blur effect for Remotion
58 lines (57 loc) • 2.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CameraMotionBlur = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const remotion_1 = require("remotion");
/**
* If the current frame is 0, then it is rendered with low opacity,
* and the trailing elements have frame numbers -1, -2, -3, -4, etc.
* To fix this, we instead reduce the number of samples to not go into negative territory.
*/
const getNumberOfSamples = ({ shutterFraction, samples, currentFrame, }) => {
const maxOffset = shutterFraction * samples;
const maxTimeReverse = currentFrame - maxOffset;
const factor = Math.min(1, Math.max(0, maxTimeReverse / maxOffset + 1));
return Math.max(1, Math.round(Math.min(factor * samples, samples)));
};
/*
* @description Produces natural looking motion blur similar to what would be produced by a film camera.
* @see [Documentation](https://www.remotion.dev/docs/motion-blur/camera-motion-blur)
*/
const CameraMotionBlur = ({ children, shutterAngle = 180, samples = 10, }) => {
const currentFrame = (0, remotion_1.useCurrentFrame)();
if (typeof samples !== 'number' ||
Number.isNaN(samples) ||
!Number.isFinite(samples)) {
throw new TypeError(`"samples" must be a number, but got ${JSON.stringify(samples)}`);
}
if (samples % 1 !== 0) {
throw new TypeError(`"samples" must be an integer, but got ${JSON.stringify(samples)}`);
}
if (samples < 0) {
throw new TypeError(`"samples" must be non-negative, but got ${JSON.stringify(samples)}`);
}
if (typeof shutterAngle !== 'number' ||
Number.isNaN(shutterAngle) ||
!Number.isFinite(shutterAngle)) {
throw new TypeError(`"shutterAngle" must be a number, but got ${JSON.stringify(shutterAngle)}`);
}
if (shutterAngle < 0 || shutterAngle > 360) {
throw new TypeError(`"shutterAngle" must be between 0 and 360, but got ${JSON.stringify(shutterAngle)}`);
}
const shutterFraction = shutterAngle / 360;
const actualSamples = getNumberOfSamples({
currentFrame,
samples,
shutterFraction,
});
return ((0, jsx_runtime_1.jsx)(remotion_1.AbsoluteFill, { style: { isolation: 'isolate' }, children: new Array(actualSamples).fill(true).map((_, i) => {
const sample = i + 1;
const sampleFrameOffset = shutterFraction * (sample / actualSamples);
return ((0, jsx_runtime_1.jsx)(remotion_1.AbsoluteFill, { style: {
mixBlendMode: 'plus-lighter',
filter: `opacity(${1 / actualSamples})`,
}, children: (0, jsx_runtime_1.jsx)(remotion_1.Freeze, { frame: currentFrame - sampleFrameOffset + 1, children: children }) }, `frame-${i.toString()}`));
}) }));
};
exports.CameraMotionBlur = CameraMotionBlur;