mcpField
R
remotion-data-viz

remotion-data-viz

github.com/phamthanhnghia创意与媒体360 次安装14 周前更新
代码仓库

关于 remotion-data-viz

用于将动态数据可视化构建为 Remotion composition 的技能,面向制作 React 视频内容的开发者。它提供基于帧的确定性动画模式,涵盖计数器、图表揭示、recharts 集成和按数据确定时长。

主要功能

  1. 01根据当前帧驱动数值动画
  2. 02构建计数器和图表揭示效果
  3. 03以确定性数值使用 recharts
  4. 04根据数据长度设置时长

适用场景

  1. 01在视频中制作动态计数器
  2. 02揭示柱状图、折线图或圆环图
  3. 03关闭内部动画以使用 recharts
  4. 04根据数据集大小设置 composition 时长

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phamthanhnghia/remotion-agent-skills --skill remotion-data-viz

安装后在 Claude Code 中输入/remotion-data-viz确认已加载。

附带文件references/chart-patterns.md

详细介绍

Remotion 数据可视化

以 Remotion 合成实现的动画数据可视化——帧确定、基于 React、可跳转。

关于合成契约,请参阅 remotion-core。关于动画基元,请参阅 remotion-animation。

核心原则

Remotion 中的数据可视化会从第 0 帧动画到 durationInFrames。所有图表数值均通过 useCurrentFrame() 得出——不会因尺寸调整或数据变化而重新渲染,只会将帧确定性地映射到视觉状态。

常见模式

动画计数器
import {useCurrentFrame, useVideoConfig, interpolate} from 'remotion';

type CounterProps = {
  from: number;
  to: number;
  durationInFrames: number;
  prefix?: string;
  suffix?: string;
};

const AnimatedCounter: React.FC<CounterProps> = ({from, to, durationInFrames, prefix = '', suffix = ''}) => {
  const frame = useCurrentFrame();
  const value = interpolate(frame, [0, durationInFrames], [from, to], {
    extrapolateRight: 'clamp',
    easing: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t, // ease-in-out-quad
  });

  return (
    <div style={{fontSize: 120, fontWeight: 700, color: '#fff', fontVariantNumeric: 'tabular-nums'}}>
      {prefix}{Math.round(value).toLocaleString()}{suffix}
    </div>
  );
};
柱状图显现
import {interpolate, useCurrentFrame} from 'remotion';

type BarProps = {label: string; value: number; maxValue: number; index: number; color: string};

const AnimatedBar: React.FC<BarProps> = ({label, value, maxValue, index, color}) => {
  const frame = useCurrentFrame();
  const STAGGER = 5;
  const DURATION = 40;

  const progress = interpolate(
    frame,
    [index * STAGGER, index * STAGGER + DURATION],
    [0, 1],
    {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}
  );

  const barWidth = (value / maxValue) * 600 * progress;

  return (
    <div style={{marginBottom: 20}}>
      <div style={{color: '#ccc', fontSize: 24, marginBottom: 8}}>{label}</div>
      <div style={{height: 48, backgroundColor: '#1e1e2e', borderRadius: 8, overflow: 'hidden'}}>
        <div
          style={{
            height: '100%',
            width: barWidth,
            backgroundColor: color,
            borderRadius: 8,
            display: 'flex',
            alignItems: 'center',
            paddingLeft: 16,
          }}
        >
          <span style={{color: 'white', fontWeight: 700}}>{Math.round(value * progress).toLocaleString()}</span>
        </div>
      </div>
    </div>
  );
};
折线图绘制(SVG)
import {interpolate, useCurrentFrame} from 'remotion';

const LineChart: React.FC<{data: number[]; width: number; height: number}> = ({data, width, height}) => {
  const frame = useCurrentFrame();
  const {durationInFrames} = useVideoConfig();

  const progress = interpolate(frame, [0, durationInFrames * 0.8], [0, 1], {extrapolateRight: 'clamp'});

  const maxVal = Math.max(...data);
  const points = data
    .slice(0, Math.ceil(data.length * progress))
    .map((v, i) => `${(i / (data.length - 1)) * width},${height - (v / maxVal) * height}`)
    .join(' ');

  return (
    <svg width={width} height={height}>
      <polyline
        points={points}
        fill="none"
        stroke="#6366f1"
        strokeWidth={4}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
};
饼图 / 环形图显现
const DonutChart: React.FC<{segments: {value: number; color: string}[]; size: number}> = ({segments, size}) => {
  const frame = useCurrentFrame();
  const {durationInFrames} = useVideoConfig();

  const progress = interpolate(frame, [0, durationInFrames * 0.7], [0, 1], {extrapolateRight: 'clamp'});
  const radius = size / 2 - 20;
  const cx = size / 2;
  const cy = size / 2;
  const total = segments.reduce((s, seg) => s + seg.value, 0);

  let cumulative = 0;
  const paths = segments.map((seg, i) => {
    const startAngle = (cumulative / total) * 2 * Math.PI - Math.PI / 2;
    cumulative += seg.value;
    const endAngle = (cumulative / total) * 2 * Math.PI * progress - Math.PI / 2;

    const x1 = cx + radius * Math.cos(startAngle);
    const y1 = cy + radius * Math.sin(startAngle);
    const x2 = cx + radius * Math.cos(endAngle);
    const y2 = cy + radius * Math.sin(endAngle);
    const largeArc = endAngle - startAngle > Math.PI ? 1 : 0;

    return (
      <path
        key={i}
        d={`M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`}
        fill={seg.color}
      />
    );
  });

  return <svg width={size} height={size}>{paths}</svg>;
};

使用 recharts

npm install recharts

重要: recharts 内部使用 requestAnimationFrame 和 DOM 测量。将 spring() 和 interpolate() 用于计算图表数据值,然后再传给 recharts——不要依赖 recharts 的内部动画(它无法确保帧确定性)。

import {BarChart, Bar, XAxis, YAxis} from 'recharts';

const MyChart: React.FC = () => {
  const frame = useCurrentFrame();
  const progress = interpolate(frame, [0, 60], [0, 1], {extrapolateRight: 'clamp'});

  const data = [
    {name: 'A', value: Math.round(1200 * progress)},
    {name: 'B', value: Math.round(800 * progress)},
    {name: 'C', value: Math.round(1600 * progress)},
  ];

  return (
    <BarChart width={600} height={400} data={data}>
      <XAxis dataKey="name" />
      <YAxis />
      <Bar dataKey="value" fill="#6366f1" isAnimationActive={false} />
    </BarChart>
  );
};

务必在 recharts 组件上设置 isAnimationActive={false}——其内部动画无法确保帧确定性。

为数据驱动的合成使用 calculateMetadata

当合成时长取决于数据长度时,请使用 calculateMetadata:

import {Composition} from 'remotion';

type DataVizProps = {data: {label: string; value: number}[]};

const dataViz = {
  component: DataVizScene,
  calculateMetadata: async ({props}: {props: DataVizProps}) => {
    const fps = 30;
    const framesPerBar = 20;
    const durationInFrames = props.data.length * framesPerBar + 60; // padding
    return {durationInFrames, fps};
  },
};

参考资料

任务阅读
动画计数器、柱状图、折线图、饼图模式references/chart-patterns.md
recharts 集成(isAnimationActive、数据绑定)references/recharts.md
使用 React 渲染进行 D3 计算(比例尺、路径)references/d3-in-remotion.md
使用 calculateMetadata 设置动态时长remotion-core/references/calculate-metadata.md
使用 spring/interpolate 实现数据显现remotion-keyframes

评论

支持 Markdown