Перейти к содержанию

Анимация по ключевым кадрам

play_keyframes() анимирует конструкцию по таймлайну в виде JSON или dict. Используйте этот метод, когда нужна воспроизводимая анимация «состояние в момент T», которую можно создать в интерфейсе, сохранить как JSON, показать в точке каретки и отрендерить в видео.

Минимальный формат

scene.loadGGB("scene.ggb", style="style.json", export={"size": {"width": 800, "height": 600}})

scene.play_keyframes({
    "version": 2,
    "keyframes": [
        {"t": 0, "values": {"A": [0, 0], "x": 35}},
        {"t": 2, "values": {"A": [4, 2], "x": 110}, "easing": "smooth"},
        {"t": 4, "values": {"A": [0, 0], "x": 35}},
    ],
})

Время ключевого кадра задаётся в секундах и должно строго возрастать. Первый кадр применяется до начала воспроизведения, поэтому первый отрендеренный кадр соответствует таймлайну, а не обязательно сохранённому состоянию редактора GeoGebra.

"version": 2 включает текущий формат. Если версия не указана, используется устаревший v1: обычная анимация значений остаётся полностью совместимой, но v1 не поддерживает треки стилей, эффекты видимости, камеру и события.

Что можно анимировать

Перед созданием таймлайна вызовите scene.get_independent_elements(). Метод возвращает свободные входы, которыми можно управлять через values.

Для сцен, загруженных из GeoGebra:

  • свободные точки: [x, y];
  • точки на траектории: {"tparam": ...}; точки на окружности и эллипсе также принимают "direction": "short" | "cw" | "ccw";
  • точку на траектории можно передать и как [x, y]: AnimaGeo спроецирует координаты обратно в параметр траектории;
  • числа и углы: числа с плавающей точкой;
  • логические значения: true/false в JSON, True/False в Python.

В сценах, построенных через DSL, созданные командами точки не являются независимыми по имени. Управляйте ими через переменные:

cx = scene.addVar("cx", 2.0)
cy = scene.addVar("cy", 4.0)
scene.putCode("C = Point(cx, cy)")

scene.play_keyframes({"version": 2, "keyframes": [
    {"t": 0, "values": {"cx": 2.0, "cy": 4.0}},
    {"t": 2, "values": {"cx": 4.0, "cy": 3.0}},
]})

Плавность

Поле "easing" ключевого кадра управляет интервалом, который ведёт к этому кадру. Значение по умолчанию для всего таймлайна задаётся так:

{"version": 2, "defaults": {"easing": "linear"}, "keyframes": [...]}

Поддерживаемые имена:

linear, smooth, in, out, in_out, smootherstep, ease_in_sine, ease_out_sine, ease_in_out_sine, ease_in_cubic, ease_out_cubic, ease_in_out_cubic, rush_into, rush_from, ease_out_back, ease_out_elastic, ease_out_bounce.

Веб-превью и Python-рендерер используют один и тот же список.

Треки стилей

В v2 каждый ключевой кадр может содержать styles. Стиль можно назначать любому элементу, а не только независимому.

scene.play_keyframes({"version": 2, "keyframes": [
    {"t": 0, "styles": {"c1": {"fill_opacity": 0.0, "stroke": "#2581b5"}}},
    {"t": 2, "styles": {"c1": {"fill_opacity": 0.6, "stroke": "#d05456"}}},
    {"t": 3, "styles": {"c1": {"stroke": None}}},
]})

JSON null или Python None возвращает ключ стиля к значению до начала анимации. Цвета интерполируются перцептивно, если с обеих сторон заданы hex-значения; остальные форматы цвета переключаются дискретно. Дискретные ключи, например label_visible, label_anchor, point_shape, tick_count, label_text и z_index, меняются в середине интервала.

Анимируемые ключи стиля:

  • числа (интерполируются): stroke_opacity, fill_opacity, stroke_width_px, stroke_dash_period_px, size_px, font_size_px, arc_size_px, arc_shift_px, right_angle_size_px, tick_length_px, tick_width_px, tick_shift_px, arrow_length_px, arrow_width_px, label_radial_offset_px
  • цвета (интерполируются): stroke, fill, label_color
  • label_offset_px: [x, y], интерполируется по компонентам
  • stroke_dash_ratio: интерполируется между числами; переключается, если одна из сторон None (сплошная линия)
  • label_text: заменяется в середине интервала, не смешивается
  • переключаются в середине интервала: point_shape, stroke_linecap, tick_count, tick_style, angle_range, right_angle_marker, label_anchor, label_visible, label_mode, z_index, z_index_fill

Любой другой ключ вызывает ошибку.

Подписи в ключевых кадрах

Снимки раскладки подписей работают, только если оба флага overlay.label_placement.enabled и overlay.label_placement.keyframe_snapshots равны true. Предварительный проход применяет стили каждого ключевого кадра до измерения, поэтому анимированные размеры шрифта и дуг, текст и видимость влияют на раскладку подписей.

Когда снимки включены, они определяют положение всех подписей: дорожка стиля label_offset_px игнорируется и не конфликтует с интерполяцией снимков. Подпись, которая появляется внутри интервала (скрыт её элемент или она сама в начале интервала), сразу стоит на автоматически найденном месте. При выключенном размещении подписи сохраняют импортированные или заданные вручную смещения, снимки не строятся. apply_keyframes_at() ставит подписи по тем же снимкам (считает только два ключевых кадра вокруг нужного момента).

Видимость и эффекты

visible — абсолютная карта видимости для каждого кадра. show и hide принимаются как сокращённая запись v2 и сводятся к visible.

scene.play_keyframes({"version": 2, "keyframes": [
    {"t": 0, "visible": {"seg": False}},
    {"t": 1.5, "visible": {"seg": True},
     "enter": {"seg": {"effect": "create", "duration": 0.8}}},
    {"t": 3, "visible": {"seg": False}, "exit": {"seg": "fade"}},
]})

Эффекты появления: fade, none, create, grow, write. Эффекты исчезновения: fade, none, uncreate, shrink. Они проигрываются внутри интервала ключевого кадра с заданной длительностью; v2 не добавляет задержку 0,4 секунды из v1 для show/hide. grow и shrink масштабируют геометрию элемента относительно её собственного центра; подпись остаётся на месте и плавно гаснет, а не масштабируется.

Поэтапное построение

Для анимации в стиле «протокол построения»:

scene.reveal_construction(lag=0.3, duration=0.5)

Сцена проходит по элементам в порядке зависимостей, выбирает подходящие по типу эффекты появления и проигрывает сгенерированные кадры v2. Передайте play=False, чтобы изучить или встроить полученный таймлайн, не проигрывая его сразу.

Ключевые кадры камеры

"@camera" — зарезервированный псевдоэлемент v2 внутри values:

scene.play_keyframes({"version": 2, "keyframes": [
    {"t": 0, "values": {"@camera": {"center": [0, 0], "width": 14}}},
    {"t": 2, "values": {"@camera": {"center": [1, 1], "width": 6}}},
]})

Так анимируется рамка камеры Manim: геометрия и декоративные элементы с пиксельными размерами масштабируются вместе. Это кинематографический pan/zoom, а не сохраняющий пиксельные размеры ZoomIn из GeoGebra.

События акцентирования

Поле events ключевого кадра проигрывает однократные акценты с автоматическим возвратом состояния внутри ведущего к кадру интервала:

scene.play_keyframes({"version": 2, "keyframes": [
    {"t": 0},
    {"t": 2, "events": [
        {"effect": "indicate", "targets": ["B"], "at": 0.4, "duration": 0.6},
        {"effect": "circumscribe", "targets": ["poly"], "at": 0.4, "duration": 0.6},
    ]},
]})

Эффекты: indicate, flash, circumscribe. targets — список имён элементов. at и duration задаются в секундах от начала текущего интервала.

Статическое превью

apply_keyframes_at() переводит сцену в состояние для одного момента таймлайна без воспроизведения анимации:

scene.apply_keyframes_at(keyframes_data, t=1.25)
scene.exportSVG("frame_at_1_25.svg")

Вызов идемпотентен, а время за пределами таймлайна ограничивается его границами. Метод подходит для серверного создания миниатюр и превью в точке каретки интерфейса.