Remotion — إنشاء فيديوهات باستخدام React و TypeScript (دليل 2026)

AI Bot
بواسطة AI Bot ·

جاري تحميل مشغل تحويل النص إلى كلام الصوتي...

فيديوهات، لكن على طريقة React. Remotion يمكّنك من إنشاء رسوم متحركة ومقدمات وتصورات بيانات وإنتاجات فيديو كاملة — بالكامل في TypeScript. بدون After Effects. بدون محررات خط زمني. فقط كود.

ما ستتعلمه

بنهاية هذا الدليل، ستتمكن من:

  • فهم بنية Remotion ومفاهيمه الأساسية
  • إعداد مشروع Remotion من الصفر باستخدام TypeScript
  • بناء تركيبات متحركة باستخدام مكونات React
  • التعامل مع النوابض والاستيفاء والصوت
  • إنشاء فيديو مبني على البيانات يعرض محتوى ديناميكي
  • تصدير الفيديو كملف MP4
  • نشر خط أنابيب للتصيير الآلي للفيديوهات

المتطلبات المسبقة

قبل البدء، تأكد من توفر:

  • Node.js 20+ مثبت (node --version)
  • معرفة بـ React و TypeScript (hooks، مكونات، JSX)
  • محرر أكواد — يُنصح بـ VS Code
  • ffmpeg مثبت للتصيير (brew install ffmpeg على macOS)
  • فهم أساسي لتحريكات CSS والتحويلات

لماذا Remotion؟

أدوات تحرير الفيديو التقليدية ممتازة للمشاريع الفردية. لكن ماذا لو احتجت إلى:

  • إنشاء مئات الفيديوهات المخصصة لحملات تسويقية
  • بناء تصورات بيانية تتحدث تلقائياً
  • تصميم مقدمات وخواتم متسقة عبر كل محتواك
  • إنتاج مقاطع وسائل التواصل على نطاق واسع مع نصوص ديناميكية

يحل Remotion هذه المشكلة بمعاملة إطارات الفيديو كمكونات React. كل إطار هو دالة في الزمن — تكتب JSX، ويحوّله Remotion إطاراً تلو الآخر إلى ملف فيديو حقيقي.

المفاهيم الأساسية

المفهومالوصف
Compositionتعريف فيديو بالعرض والارتفاع ومعدل الإطارات والمدة
Sequenceغلاف لتأخير ظهور المكون في الخط الزمني
useCurrentFrame()خطاف يُرجع رقم الإطار الحالي
useVideoConfig()خطاف يُرجع بيانات الفيديو الوصفية
interpolate()يربط نطاق إطارات بنطاق قيم خرج
spring()منحنى تحريك فيزيائي لحركة طبيعية

الخطوة 1: إعداد المشروع

أنشئ مشروع Remotion جديد باستخدام القالب الرسمي:

npx create-video@latest my-video --template blank
cd my-video

هذا ينشئ مشروع TypeScript مُعَدّ بالكامل. لنلقِ نظرة على الهيكل:

my-video/
├── src/
│   ├── Root.tsx          # نقطة الدخول — تسجيل التركيبات
│   ├── Composition.tsx   # مكون الفيديو الأول
│   └── index.ts          # مدخل Remotion
├── remotion.config.ts    # إعدادات Remotion
├── package.json
└── tsconfig.json

ثبّت التبعيات وشغّل خادم المعاينة:

npm install
npm start

هذا يفتح Remotion Studio على http://localhost:3000 — بيئة معاينة في المتصفح تتيح لك التنقل عبر الخط الزمني في الوقت الفعلي.


الخطوة 2: فهم التركيبة

افتح src/Root.tsx. هنا تُسجَّل تركيبات الفيديو:

import { Composition } from "remotion";
import { MyComposition } from "./Composition";
 
export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="MyVideo"
        component={MyComposition}
        durationInFrames={150}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};

الخصائص الرئيسية:

  • id — معرّف فريد يُستخدم عند التصيير
  • durationInFrames — إجمالي الإطارات (150 إطار عند 30fps = 5 ثوانٍ)
  • fps — إطارات في الثانية (30 قياسي، 60 لحركة سلسة)
  • width/height — دقة الفيديو بالبكسل

الخطوة 3: بناء أول تحريك

استبدل src/Composition.tsx ببطاقة عنوان متحركة:

import { useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
import React from "react";
 
export const MyComposition: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  // تحريك نابضي للعنوان
  const titleScale = spring({
    frame,
    fps,
    config: {
      damping: 12,
      stiffness: 200,
      mass: 0.5,
    },
  });
 
  // إظهار العنوان الفرعي بعد 20 إطار
  const subtitleOpacity = interpolate(frame, [20, 40], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
 
  // تحريك العنوان الفرعي للأعلى
  const subtitleY = interpolate(frame, [20, 40], [30, 0], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
 
  return (
    <div
      style={{
        flex: 1,
        background: "linear-gradient(135deg, #0f0c29, #302b63, #24243e)",
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        fontFamily: "Inter, sans-serif",
      }}
    >
      <h1
        style={{
          fontSize: 80,
          color: "white",
          fontWeight: 900,
          transform: `scale(${titleScale})`,
          textAlign: "center",
        }}
      >
        مرحباً، Remotion
      </h1>
 
      <p
        style={{
          fontSize: 32,
          color: "#a8a8ff",
          opacity: subtitleOpacity,
          transform: `translateY(${subtitleY}px)`,
          marginTop: 20,
        }}
      >
        فيديوهات مدعومة بـ React
      </p>
    </div>
  );
};

احفظ الملف وتحقق من Remotion Studio — سترى تحريكاً سلساً حيث يظهر العنوان بتأثير نابضي، يتبعه عنوان فرعي يتلاشى ويتحرك للأعلى.

كيف يعمل

  1. useCurrentFrame() يُرجع الإطار الحالي (0، 1، 2، ... 149)
  2. spring() ينشئ منحنى فيزيائي يبدأ من 0 ويستقر عند 1
  3. interpolate() يربط نطاقات الإطارات بقيم الخرج — الإطار 20-40 يربط الشفافية من 0 إلى 1
  4. extrapolateLeft/Right: "clamp" يمنع القيم من تجاوز 0 أو 1

الخطوة 4: استخدام التسلسلات للتوقيت

تتيح لك التسلسلات تركيب أقسام متحركة متعددة بتوقيت دقيق:

import {
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  spring,
  Sequence,
  AbsoluteFill,
} from "remotion";
import React from "react";
 
const Title: React.FC<{ text: string }> = ({ text }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  const scale = spring({ frame, fps, config: { damping: 12 } });
  const opacity = interpolate(frame, [0, 15], [0, 1], {
    extrapolateRight: "clamp",
  });
 
  return (
    <AbsoluteFill
      style={{
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <h1
        style={{
          fontSize: 72,
          color: "white",
          transform: `scale(${scale})`,
          opacity,
          fontWeight: 800,
        }}
      >
        {text}
      </h1>
    </AbsoluteFill>
  );
};
 
const FadeOut: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const frame = useCurrentFrame();
  const opacity = interpolate(frame, [0, 10], [1, 0], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
 
  return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;
};
 
export const MyComposition: React.FC = () => {
  return (
    <AbsoluteFill
      style={{
        background: "linear-gradient(135deg, #0f0c29, #302b63, #24243e)",
      }}
    >
      {/* المشهد 1: المقدمة (الإطارات 0-59) */}
      <Sequence from={0} durationInFrames={60}>
        <Title text="مرحباً" />
      </Sequence>
 
      {/* المشهد 2: الرسالة الرئيسية (الإطارات 45-119) */}
      <Sequence from={45} durationInFrames={75}>
        <Title text="ابنِ فيديوهات بالكود" />
      </Sequence>
 
      {/* المشهد 3: الخاتمة مع تلاشي (الإطارات 110-150) */}
      <Sequence from={110} durationInFrames={40}>
        <FadeOut>
          <Title text="!هيّا بنا" />
        </FadeOut>
      </Sequence>
    </AbsoluteFill>
  );
};

لاحظ كيف يُعيد Sequence تعيين useCurrentFrame() إلى 0 داخل أبنائه. هذا يعني أن كل مكون يبدأ تحريكه من الإطار 0، بغض النظر عن موعد ظهوره في الخط الزمني.


الخطوة 5: إضافة الصوت

يدعم Remotion الصوت مع مزامنة دقيقة على مستوى الإطار:

import { Audio, Sequence, staticFile } from "remotion";
 
// ضع ملف الصوت في مجلد /public
// مثال: public/music.mp3
 
export const MyComposition: React.FC = () => {
  return (
    <AbsoluteFill style={{ background: "#0f0c29" }}>
      {/* موسيقى خلفية تبدأ من الإطار 0 */}
      <Audio src={staticFile("music.mp3")} volume={0.3} />
 
      {/* مؤثر صوتي عند الإطار 30 */}
      <Sequence from={30}>
        <Audio src={staticFile("whoosh.mp3")} volume={0.8} />
      </Sequence>
 
      {/* المحتوى المرئي */}
      <Sequence from={0}>
        <Title text="!مع الصوت" />
      </Sequence>
    </AbsoluteFill>
  );
};

يمكنك أيضاً التحكم في مستوى الصوت ديناميكياً:

<Audio
  src={staticFile("music.mp3")}
  volume={(f) =>
    interpolate(f, [0, 30, 120, 150], [0, 0.5, 0.5, 0], {
      extrapolateLeft: "clamp",
      extrapolateRight: "clamp",
    })
  }
/>

الخطوة 6: فيديوهات مبنية على البيانات

هنا يتألق Remotion حقاً. لنبنِ فيديو يعرض بيانات ديناميكية — تخيّل إنشاء فيديوهات إحصائيات أسبوعية تلقائياً:

import React from "react";
import {
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  spring,
  Sequence,
  AbsoluteFill,
} from "remotion";
 
interface StatsProps {
  title: string;
  stats: Array<{ label: string; value: number; color: string }>;
}
 
const AnimatedBar: React.FC<{
  label: string;
  value: number;
  maxValue: number;
  color: string;
  delay: number;
}> = ({ label, value, maxValue, color, delay }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  const progress = spring({
    frame: frame - delay,
    fps,
    config: { damping: 15, stiffness: 100 },
  });
 
  const width = interpolate(progress, [0, 1], [0, (value / maxValue) * 100]);
 
  const labelOpacity = interpolate(frame, [delay, delay + 10], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
 
  return (
    <div style={{ marginBottom: 24, opacity: labelOpacity }}>
      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          marginBottom: 8,
          fontSize: 24,
          color: "white",
          fontFamily: "Inter, sans-serif",
        }}
      >
        <span>{label}</span>
        <span style={{ fontWeight: 700 }}>
          {Math.round(value * progress)}
        </span>
      </div>
      <div
        style={{
          height: 32,
          background: "rgba(255,255,255,0.1)",
          borderRadius: 16,
          overflow: "hidden",
        }}
      >
        <div
          style={{
            height: "100%",
            width: `${width}%`,
            background: `linear-gradient(90deg, ${color}, ${color}aa)`,
            borderRadius: 16,
          }}
        />
      </div>
    </div>
  );
};
 
export const StatsVideo: React.FC<StatsProps> = ({ title, stats }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
 
  const titleScale = spring({ frame, fps, config: { damping: 12 } });
  const maxValue = Math.max(...stats.map((s) => s.value));
 
  return (
    <AbsoluteFill
      style={{
        background: "linear-gradient(180deg, #1a1a2e, #16213e)",
        padding: 80,
        justifyContent: "center",
      }}
    >
      <h1
        style={{
          fontSize: 56,
          color: "white",
          fontWeight: 900,
          marginBottom: 60,
          transform: `scale(${titleScale})`,
          fontFamily: "Inter, sans-serif",
        }}
      >
        {title}
      </h1>
 
      {stats.map((stat, i) => (
        <AnimatedBar
          key={stat.label}
          label={stat.label}
          value={stat.value}
          maxValue={maxValue}
          color={stat.color}
          delay={i * 15 + 10}
        />
      ))}
    </AbsoluteFill>
  );
};

سجّل التركيبة مع خصائص افتراضية في Root.tsx:

<Composition
  id="WeeklyStats"
  component={StatsVideo}
  durationInFrames={180}
  fps={30}
  width={1080}
  height={1080}
  defaultProps={{
    title: "أداء هذا الأسبوع",
    stats: [
      { label: "مستخدمون جدد", value: 1247, color: "#6366f1" },
      { label: "مشاهدات الصفحة", value: 8432, color: "#22d3ee" },
      { label: "التحويلات", value: 342, color: "#f43f5e" },
      { label: "الإيرادات ($)", value: 5890, color: "#10b981" },
    ],
  }}
/>

الآن لديك مكون فيديو إحصائيات قابل لإعادة الاستخدام. غيّر الخصائص وستحصل على فيديو مختلف تماماً.


الخطوة 7: تحميل الخطوط المخصصة

استخدم @remotion/google-fonts أو حمّل الخطوط يدوياً:

npm install @remotion/google-fonts
import { loadFont } from "@remotion/google-fonts/Inter";
 
const { fontFamily } = loadFont();
 
// استخدمه في مكوناتك
<h1 style={{ fontFamily }}>مُنسّق بـ Inter</h1>

للخطوط المحلية، ضعها في مجلد public/ واستخدم staticFile():

import { staticFile } from "remotion";
 
const fontFace = new FontFace("MyFont", `url(${staticFile("fonts/MyFont.woff2")})`);
 
React.useEffect(() => {
  document.fonts.add(fontFace);
  fontFace.load();
}, []);

الخطوة 8: تصيير الفيديو

التصيير عبر سطر الأوامر

صدّر الفيديو كملف MP4:

npx remotion render MyVideo out/my-video.mp4

خيارات التخصيص:

npx remotion render MyVideo out/my-video.mp4 \
  --codec h264 \
  --quality 80 \
  --concurrency 4
العلامةالوصف
--codech264 (MP4)، vp8/vp9 (WebM)، prores (MOV)
--quality0-100، جودة JPEG للإطارات
--concurrencyعدد خيوط التصيير المتوازية
--propsسلسلة JSON لتجاوز الخصائص الافتراضية

التصيير مع خصائص مخصصة

npx remotion render WeeklyStats out/stats-week-14.mp4 \
  --props='{"title":"إحصائيات الأسبوع 14","stats":[{"label":"المستخدمون","value":2000,"color":"#6366f1"}]}'

الخطوة 9: التصيير عبر واجهة Node.js البرمجية

للأنابيب الآلية، استخدم الواجهة البرمجية:

import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";
import path from "path";
 
async function renderVideo() {
  const bundleLocation = await bundle({
    entryPoint: path.resolve("./src/index.ts"),
  });
 
  const composition = await selectComposition({
    serveUrl: bundleLocation,
    id: "WeeklyStats",
    inputProps: {
      title: "تقرير أبريل 2026",
      stats: [
        { label: "الإيرادات", value: 12500, color: "#10b981" },
        { label: "المستخدمون", value: 3400, color: "#6366f1" },
      ],
    },
  });
 
  await renderMedia({
    composition,
    serveUrl: bundleLocation,
    codec: "h264",
    outputLocation: "out/april-report.mp4",
    onProgress: ({ progress }) => {
      console.log(`التصيير: ${Math.round(progress * 100)}%`);
    },
  });
 
  console.log("!تم تصيير الفيديو بنجاح");
}
 
renderVideo();

الخطوة 10: تقنيات متقدمة

دوال التسهيل

يوفر Remotion منحنيات تسهيل مدمجة:

import { Easing, interpolate } from "remotion";
 
const value = interpolate(frame, [0, 30], [0, 1], {
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
  extrapolateRight: "clamp",
});

التحريكات المتتابعة

أنشئ تأثيرات دخول متتابعة بإزاحة التأخيرات:

const items = ["React", "TypeScript", "Remotion"];
 
return (
  <>
    {items.map((item, i) => {
      const delay = i * 10;
      const opacity = interpolate(frame, [delay, delay + 15], [0, 1], {
        extrapolateLeft: "clamp",
        extrapolateRight: "clamp",
      });
      const x = interpolate(frame, [delay, delay + 15], [-50, 0], {
        extrapolateLeft: "clamp",
        extrapolateRight: "clamp",
      });
 
      return (
        <p
          key={item}
          style={{
            opacity,
            transform: `translateX(${x}px)`,
            fontSize: 36,
            color: "white",
          }}
        >
          {item}
        </p>
      );
    })}
  </>
);

استخدام الصور والفيديوهات

يمكن لـ Remotion تركيب الصور وتضمين فيديوهات أخرى:

import { Img, Video, staticFile } from "remotion";
 
// صورة ثابتة
<Img src={staticFile("logo.png")} style={{ width: 200 }} />
 
// تضمين فيديو
<Video src={staticFile("background.mp4")} />

استكشاف الأخطاء وإصلاحها

المشاكل الشائعة

"ffmpeg not found" ثبّت ffmpeg: brew install ffmpeg (macOS) أو apt install ffmpeg (Ubuntu).

تصيير بطيء زِد التوازي بـ --concurrency 8.

الخطوط لا تُحمَّل استخدم delayRender() لإيقاف التصيير مؤقتاً حتى تُحمَّل الخطوط:

import { delayRender, continueRender } from "remotion";
 
const [handle] = React.useState(() => delayRender());
 
React.useEffect(() => {
  const font = new FontFace("MyFont", `url(${staticFile("font.woff2")})`);
  font.load().then(() => {
    document.fonts.add(font);
    continueRender(handle);
  });
}, [handle]);

أفكار لمشاريع

  1. قوالب مقدمات YouTube — تحريكات ذات علامة تجارية لقناتك
  2. مولّد قصص وسائل التواصل — ملء معلومات المنتج ديناميكياً في قوالب القصص
  3. فيديوهات شرح الكود — تحريك ظهور الكود سطراً سطراً
  4. مؤقتات العد التنازلي — عد تنازلي متحرك لتاريخ إطلاق
  5. أوديوغرامات البودكاست — تصورات موجات صوتية متزامنة مع الصوت

الخطوات التالية

  • استكشف وثائق Remotion للميزات المتقدمة مثل OffthreadVideo وتصيير Lambda وتضمين Player
  • تعرّف على قوالب Remotion الجاهزة للإنتاج
  • تعلّم عن Remotion Lambda لتصيير الفيديوهات على نطاق واسع على AWS
  • ادمج مع Motion (Framer Motion) لتحريكات واجهة معقدة داخل فيديوهاتك

الخلاصة

يحوّل Remotion إنشاء الفيديو من عملية يدوية تعتمد على أدوات إبداعية إلى سير عمل قابل للبرمجة والتكرار والتوسع. بمعاملة كل إطار كمكون React، تحصل على القوة الكاملة لـ TypeScript وحزم npm وتركيب المكونات — مطبّقة على الفيديو.

سواء كنت تبني محتوى تسويقي آلي أو تصورات بيانات أو قوالب ذات علامة تجارية، يمنحك Remotion تجربة المطور التي تعرفها وتحبها بالفعل. ابدأ بتحريكات بسيطة، ركّبها في تسلسلات، وقريباً ستصدّر فيديوهات بجودة إنتاجية بالكامل من الكود.


هل تريد قراءة المزيد من الدروس التعليمية؟ تحقق من أحدث درس تعليمي لدينا على إطار عمل Mastra للذكاء الاصطناعي: بناء وكلاء وسير عمل ذكية بـ TypeScript.

ناقش مشروعك معنا

نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.

دعنا نجد أفضل الحلول لاحتياجاتك.

مقالات ذات صلة

بناء تطبيقات تعاونية محلية أولاً باستخدام Yjs و React

تعلم كيفية بناء تطبيقات تعاونية تعمل في الوقت الفعلي حتى بدون اتصال بالإنترنت باستخدام تقنية CRDTs ومكتبة Yjs مع React. يغطي هذا الدليل مزامنة البيانات بدون تعارضات، والهندسة المعمارية المحلية أولاً، وبناء محرر مستندات مشترك من الصفر.

30 د قراءة·

إضافة المصادقة لتطبيق Next.js 15 باستخدام Auth.js v5: البريد الإلكتروني وOAuth والتحكم بالأدوار

تعلم كيفية إضافة نظام مصادقة جاهز للإنتاج لتطبيق Next.js 15 باستخدام Auth.js v5. يغطي هذا الدليل الشامل تسجيل الدخول عبر Google OAuth وبيانات الاعتماد بالبريد الإلكتروني وكلمة المرور والمسارات المحمية والتحكم بالوصول حسب الأدوار.

30 د قراءة·