CanvasTake · Canvas / Web Audio / GIF
Как экспортировать canvas-анимацию в GIF с CanvasTake
Автор: Grundy · Проверено: 15 октября 2026
Для GIF не нужно записывать экран в реальном времени. В CanvasTake 0.3.0 анимацию можно отрисовать по временной шкале и передать кадры экспортёру. Итоговая длительность определяется задержками кадров, а не скоростью компьютера. GIF не поддерживает звук: для озвучки нужен видеоформат.
1. Установите версию с GIF API
Нужна @grundyjs/canvas-take 0.3.0 или новее. Пример ниже использует отдельные импорты /gif и /gif/gifenc. Второй предоставляет готовый адаптер с включённым кодеком; вручную устанавливать gifenc не требуется. Обычный импорт видеорекордера не загружает GIF-кодек.
npm install @grundyjs/canvas-take@^0.3.02. Подготовьте отрисовку по времени
Создайте 2D canvas, например 800 × 450. Функция drawTimeline(time) в примере — ваша функция: она должна полностью перерисовать canvas для заданного времени в миллисекундах. Это не экспорт CanvasTake. Сбрасывайте фон каждого кадра и не изменяйте размеры холста в ходе экспорта. Можно использовать существующий renderer AlgiViz.
3. Последовательно добавьте кадры
Шаг 50 мс даёт 20 кадров в секунду. Цикл создаёт 120 кадров для первых шести секунд; последний кадр удерживается ещё две секунды. Всего 121 кадр и 8 секунд GIF. Обязательно await для каждого addFrame: параллельные операции не поддерживаются. repeat: 0 означает бесконечное повторение.
import { createGifExporter } from '@grundyjs/canvas-take/gif';
import { createGifencEncoder } from '@grundyjs/canvas-take/gif/gifenc';
const controller = new AbortController();
const gif = createGifExporter({
encoder: createGifencEncoder({ maxColors: 128, quality: 'high' }),
repeat: 0,
signal: controller.signal,
});
try {
for (let time = 0; time < 6000; time += 50) {
drawTimeline(time); // Your deterministic renderer draws into canvas.
await gif.addFrame(canvas, 50);
}
drawTimeline(6000);
await gif.addFrame(canvas, 2000); // Hold the final frame for two seconds.
const blob = await gif.finish(); // image/gif; 8 seconds, 121 frames.
// Download/preview blob; revoke any object URL when no longer needed.
} finally {
gif.dispose();
}4. Покажите или сохраните Blob
finish() возвращает Blob типа image/gif. Код ниже вставьте сразу после await gif.finish(), внутри try, где доступен blob. Для скачивания создайте ссылку на object URL. Пока ссылка или предпросмотр используются, URL нужен; освободите его при удалении интерфейса. dispose() экспортёра вызывается в finally и не отменяет уже полученный Blob.
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "animation.gif";
link.textContent = "Download GIF";
document.body.append(link);
// When removing the preview/download UI:
// link.remove();
// URL.revokeObjectURL(url);5. Подберите качество и ограничьте объём
GIF использует палитру до 256 цветов; 128 — компромисс, а не универсальная настройка. Длительности округляются до 10 мс, некоторые проигрыватели увеличивают короткие задержки. Прозрачность сводится к фону, по умолчанию белому. Ограничивайте разрешение и число кадров: сжатые данные и Blob занимают память. Адаптер выполняет работу на вызывающем потоке; автоматического Worker нет. Для отмены вызовите controller.abort() из обработчика кнопки. Проверьте размер файла, плавность, цвета и финальную паузу.