用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tools-only/X-Skills --skill adaptation-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | adaptation-patterns |
| description | Universal patterns for converting ReactBits components to Remotion frame-based rendering |
| metadata | {"tags":"remotion, adaptation, patterns, interpolate, useCurrentFrame"} |
All ReactBits animations must be converted from event/time-driven to frame-driven rendering. Remotion renders each frame independently -- animations must be deterministic functions of the frame number.
Many ReactBits components trigger on scroll visibility. In Remotion, trigger at a specific frame.
// ReactBits (original)
const [inView, setInView] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
setInView(entry.isIntersecting);
});
observer.observe(ref.current);
}, []);
// Remotion (adapted)
const frame = useCurrentFrame();
const triggerFrame = 15; // start at 0.5s @ 30fps
const inView = frame >= triggerFrame;
For GSAP ScrollTrigger with scrub (scroll-linked progress):
// ReactBits: gsap.fromTo(el, from, to) with ScrollTrigger scrub: true
// Remotion:
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const progress = interpolate(frame, [startFrame, endFrame], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// Apply properties using progress (0 to 1)
const opacity = interpolate(progress, [0, 1], [0, 1]);
const y = interpolate(progress, [0, 1], [100, 0]);
// ReactBits: setInterval(callback, intervalMs)
// Remotion:
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const intervalFrames = Math.round((intervalMs / 1000) * fps);
const currentIndex = Math.floor(frame / intervalFrames) % totalItems;
For typewriter effects:
const framesPerChar = Math.round((typingSpeed / 1000) * fps);
const charsVisible = Math.min(
Math.floor(frame / framesPerChar),
text.length
);
const displayText = text.slice(0, charsVisible);
// ReactBits (Framer Motion)
useAnimationFrame((time, delta) => {
progress.current += delta * speed;
});
// Remotion
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const progress = (frame / fps) * speed;
For Canvas 2D components:
// ReactBits: requestAnimationFrame loop
// Remotion: render once per frame
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
useEffect(() => {
const ctx = canvasRef.current.getContext('2d');
const time = frame / fps;
drawFrame(ctx, time); // your draw function
}, [frame]);
For optional mouse-enhancement effects (ScrambledText, shader backgrounds with mouse toggle):
const frame = useCurrentFrame();
const { fps, width, height } = useVideoConfig();
const t = frame / fps;
// Circular path
const cursorX = width / 2 + (width * 0.3) * Math.sin(t * 1.5);
const cursorY = height / 2 + (height * 0.2) * Math.cos(t * 1.2);
// Or keyframed path
const cursorX = interpolate(frame, [0, 30, 60, 90], [100, 500, 300, 700]);
const cursorY = interpolate(frame, [0, 30, 60, 90], [200, 100, 400, 200]);
For OGL/Three.js shader components:
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Time uniform (replaces performance.now() * 0.001 or clock.getElapsedTime())
uniforms.iTime.value = frame / fps;
// or for OGL: program.uniforms.uTime.value = frame / fps;
// Mouse uniform (fixed or animated)
uniforms.iMouse.value.set(0.5, 0.5); // center
// or animated:
uniforms.iMouse.value.set(
0.5 + 0.3 * Math.sin(frame / fps),
0.5 + 0.2 * Math.cos(frame / fps * 0.7)
);
Remotion may render frames out of order or multiple times. Math.random() produces non-deterministic results. Use a seeded PRNG:
function seededRandom(seed: number): number {
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
// Use frame number + element index as seed
const rand = seededRandom(frame * 1000 + charIndex);
For noise-based effects, use a deterministic noise function (simplex/perlin) with frame as input instead of Math.random().
// useMotionValue + useTransform -> interpolate
const x = interpolate(frame, [0, durationInFrames], [0, 100]);
// useSpring -> spring()
import { spring } from 'remotion';
const scale = spring({ frame, fps, config: { damping: 12, stiffness: 200 } });
// AnimatePresence enter/exit -> Sequence + interpolate
<Sequence from={0} durationInFrames={30}>
{/* entering content */}
</Sequence>
| GSAP Easing | Remotion Equivalent |
|---|---|
power1.out | Easing.out(Easing.quad) |
power2.out | Easing.out(Easing.cubic) |
power3.out | Easing.out(Easing.cubic) |
power4.out | Easing.out(Easing.bezier(0.25, 0.46, 0.45, 0.94)) |
back.inOut(2) | Easing.inOut(Easing.back(2)) |
elastic.out(1, 0.3) | Easing.out(Easing.elastic(1)) |
expo.out | Easing.out(Easing.exp) |
import { Easing, interpolate } from 'remotion';
const value = interpolate(frame, [startFrame, endFrame], [0, 1], {
easing: Easing.out(Easing.cubic),
extrapolateRight: 'clamp',
});
Many ReactBits text animations use staggered reveals. In Remotion:
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const staggerFrames = Math.round((staggerMs / 1000) * fps);
const durationFrames = Math.round(duration * fps);
{characters.map((char, i) => {
const charStart = startFrame + i * staggerFrames;
const progress = interpolate(
frame,
[charStart, charStart + durationFrames],
[0, 1],
{ extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
);
const opacity = progress;
const y = interpolate(progress, [0, 1], [40, 0]);
return (
<span key={i} style={{ opacity, transform: `translateY(${y}px)` }}>
{char}
</span>
);
})}
Remove these in all ReactBits-to-Remotion adaptations:
IntersectionObserver -- always visibleResizeObserver -- use useVideoConfig() for fixed dimensionswindow.addEventListener('resize') -- fixed dimensionsdocument.fonts.load() -- use @remotion/google-fonts or staticFile()prefers-reduced-motion -- not applicable in videowindow.devicePixelRatio -- use fixed DPR or Remotion's config:hover / :active CSS states -- not applicable in videonavigator.userAgent mobile detection -- not applicable