UNPKG

@remotion/motion-blur

Version:

Motion blur effect for Remotion

105 lines (104 loc) 3.81 kB
// src/CameraMotionBlur.tsx import { AbsoluteFill, Freeze, useCurrentFrame } from "remotion"; import { jsx } from "react/jsx-runtime"; var 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))); }; var CameraMotionBlur = ({ children, shutterAngle = 180, samples = 10 }) => { const currentFrame = 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 /* @__PURE__ */ jsx(AbsoluteFill, { style: { isolation: "isolate" }, children: new Array(actualSamples).fill(true).map((_, i) => { const sample = i + 1; const sampleFrameOffset = shutterFraction * (sample / actualSamples); return /* @__PURE__ */ jsx(AbsoluteFill, { style: { mixBlendMode: "plus-lighter", filter: `opacity(${1 / actualSamples})` }, children: /* @__PURE__ */ jsx(Freeze, { frame: currentFrame - sampleFrameOffset + 1, children }) }, `frame-${i.toString()}`); }) }); }; // src/Trail.tsx import { AbsoluteFill as AbsoluteFill2, Freeze as Freeze2, useCurrentFrame as useCurrentFrame2 } from "remotion"; import { jsx as jsx2, jsxs } from "react/jsx-runtime"; var Trail = ({ children, layers, lagInFrames, trailOpacity }) => { const frame = useCurrentFrame2(); if (typeof layers !== "number" || Number.isNaN(layers) || !Number.isFinite(layers)) { throw new TypeError(`"layers" must be a number, but got ${JSON.stringify(layers)}`); } if (layers % 1 !== 0) { throw new TypeError(`"layers" must be an integer, but got ${JSON.stringify(layers)}`); } if (layers < 0) { throw new TypeError(`"layers" must be non-negative, but got ${JSON.stringify(layers)}`); } if (typeof trailOpacity !== "number" || Number.isNaN(trailOpacity) || !Number.isFinite(trailOpacity)) { throw new TypeError(`"trailOpacity" must be a number, but got ${JSON.stringify(trailOpacity)}`); } if (typeof lagInFrames !== "number" || Number.isNaN(lagInFrames) || !Number.isFinite(lagInFrames)) { throw new TypeError(`"lagInFrames" must be a number, but got ${JSON.stringify(lagInFrames)}`); } return /* @__PURE__ */ jsxs(AbsoluteFill2, { children: [ new Array(layers).fill(true).map((_, i) => { return /* @__PURE__ */ jsx2(AbsoluteFill2, { style: { opacity: trailOpacity - (layers - i) / layers * trailOpacity }, children: /* @__PURE__ */ jsx2(Freeze2, { frame: frame - lagInFrames * (layers - i), children }) }, `frame-${i.toString()}`); }), children ] }); }; export { Trail, CameraMotionBlur };