Tài liệu này mô tả thiết kế, hiện thực và cách mở rộng hệ thống dựng hình chuyển cảnh (transition rendering system) trong media-render.
Các hiệu ứng chuyển cảnh trong OpenCut hoạt động dựa trên cơ chế đăng ký (registry-driven), tách biệt hoàn toàn logic hiệu ứng hình ảnh khỏi bộ điều phối dòng thời gian chính.
+--------------------------------------------------------------------------+
| CanvasRenderer |
+--------------------------------------------------------------------------+
│
▼
+--------------------------------------------------------------------------+
| TransitionNode (resolve) |
| - Xác định clip ra 'fromNode' (outgoing) & clip vào 'toNode' (incoming) |
| - Tính toán tiến trình transition (0.0 -> 1.0) và áp dụng đường cong |
| - Kích hoạt giải mã sớm khung hình của 'toNode' (vì clip này chưa tới |
| thời điểm bắt đầu thực tế trên timeline chính) |
+--------------------------------------------------------------------------+
│
▼
+--------------------------------------------------------------------------+
| TransitionNode (render) |
| - Vẽ clip cũ ra bề mặt offscreen canvas tạm thời 'fromCanvas' |
| - Vẽ clip mới ra bề mặt offscreen canvas tạm thời 'toCanvas' |
| - Gọi hàm xử lý render hiệu ứng đăng ký trong Registry trên context đầu ra|
| - Trả về canvas đầu ra như một lớp tổng hợp duy nhất cho compositor |
+--------------------------------------------------------------------------+
Hệ thống được định nghĩa tại media-render/src/transitions/types.ts:
Định nghĩa metadata của transition, các tham số cấu hình và bộ dựng hình:
export interface TransitionDefinition {
type: string; // Khóa đăng ký (phải khớp với TransitionElement.transitionType)
name: string; // Tên hiển thị thân thiện trên UI
group: string; // Phân nhóm hiệu ứng (ví dụ: Basic, Slide, Zoom)
keywords: string[];
defaultDuration?: number; // Thời lượng mặc định (giây)
easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out"; // Đường cong nội suy
params: TransitionParamDefinition[]; // Lược đồ cấu hình tham số tùy chỉnh
renderer: TransitionRenderer;
}Thực thi các lệnh vẽ đồ họa Canvas 2D để pha trộn hai bề mặt hình ảnh:
export interface TransitionRenderContext {
fromCanvas: Canvas | null; // Khung hình clip cũ kết thúc
toCanvas: Canvas | null; // Khung hình clip mới bắt đầu
progress: number; // Tiến trình chuyển cảnh (0.0 đến 1.0, đã qua easing)
params: Record<string, any>; // Các giá trị tham số tùy chỉnh
width: number;
height: number;
output: CanvasRenderingContext2D; // Canvas context đích để vẽ kết quả lên đó
}
export interface TransitionRenderer {
render(ctx: TransitionRenderContext): void;
}Để thêm một hiệu ứng chuyển cảnh mới (ví dụ: "wipe_right"):
Tạo một tệp tin mới trong thư mục src/transitions/definitions/ (ví dụ wipe.ts):
import type { TransitionDefinition } from "../types";
export const wipeRightDefinition: TransitionDefinition = {
type: "wipe_right",
name: "Wipe Right",
group: "Slide",
keywords: ["wipe", "right", "slide", "reveal"],
defaultDuration: 0.5,
easing: "ease-in-out",
params: [],
renderer: {
render({ fromCanvas, toCanvas, progress, width, height, output }) {
output.clearRect(0, 0, width, height);
const boundaryX = progress * width;
// Vẽ clip cũ ở vùng bên trái màn hình
if (fromCanvas) {
output.save();
output.beginPath();
output.rect(boundaryX, 0, width - boundaryX, height);
output.clip();
output.drawImage(fromCanvas, 0, 0, width, height);
output.restore();
}
// Vẽ clip mới ở vùng bên phải màn hình
if (toCanvas) {
output.save();
output.beginPath();
output.rect(0, 0, boundaryX, height);
output.clip();
output.drawImage(toCanvas, 0, 0, width, height);
output.restore();
}
}
}
};Import và khai báo đăng ký hiệu ứng mới trong src/transitions/definitions/index.ts:
import { wipeRightDefinition } from "./wipe";
export function registerDefaultTransitions(): void {
registerTransitions(
// ... các transition khác
wipeRightDefinition
);
}Chỉ với 2 bước trên, bộ kết xuất sẽ tự động nhận diện và vẽ hiệu ứng "wipe_right" bất cứ khi nào nó xuất hiện trong dòng thời gian manifest!