@remotion/motion-blur
Version:
Motion blur effect for Remotion
105 lines (104 loc) • 3.81 kB
JavaScript
// 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
};