Применение анимаций фигур в презентациях с Python

Обзор

Чтобы работать с отдельными поведениями внутри эффекта или редактировать сегменты траектории движения, см. Custom Animation.

Aspose.Slides for Python via .NET представляет анимацию слайдов как эффекты во временной шкале слайда. Эффект имеет целевую форму, тип и подтип анимации, триггер, настройки времени и необязательные свойства, такие как звук или поведение после анимации.

Во временной шкале есть два типа последовательностей:

  • main sequence воспроизводится при переходе к следующему слайду.
  • interactive sequence начинается, когда по её триггерной форме выполняется щелчок.

Поскольку текстовые поля, изображения, диаграммы, таблицы и другие объекты слайда реализуют IShape, вы используете один и тот же метод Sequence.add_effect для большинства содержимого слайда. Доступные эффекты перечислены в перечислении EffectType.

Добавление анимаций форм

Чтобы добавить анимацию, получите основную последовательность слайда и вызовите Sequence.add_effect с целевой формой, типом эффекта, подтипом и триггером. Для эффекта, который начинается при щелчке по другой форме, создайте интерактивную последовательность, триггером которой будет эта другая форма.

В следующем примере создаются оба типа анимации и результат сохраняется в файл shape-animations.pptx.

import aspose.slides as slides


with slides.Presentation() as presentation:
    slide = presentation.slides[0]

    target_shape = slide.shapes.add_auto_shape(slides.ShapeType.ROUND_CORNER_RECTANGLE, 120, 100, 320, 80)
    target_shape.text_frame.text = "Click to animate this shape"

    main_sequence = slide.timeline.main_sequence
    entrance_effect = main_sequence.add_effect(target_shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)
    entrance_effect.timing.duration = 1.5

    trigger_shape = slide.shapes.add_auto_shape(slides.ShapeType.BEVEL, 20, 20, 100, 40)
    trigger_shape.text_frame.text = "Move"

    interactive_sequence = slide.timeline.interactive_sequences.add(trigger_shape)
    interactive_sequence.add_effect(target_shape, slides.animation.EffectType.PATH_FOOTBALL, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)

    presentation.save("shape-animations.pptx", slides.export.SaveFormat.PPTX)

Триггер определяет, когда эффект начинается:

  • EffectTriggerType.ON_CLICK ждёт щелчка в основной последовательности или щелчка по триггерной форме в интерактивной последовательности.
  • EffectTriggerType.WITH_PREVIOUS начинается одновременно с предыдущим эффектом.
  • EffectTriggerType.AFTER_PREVIOUS начинается после завершения предыдущего эффекта.

Чтобы анимировать изображение, диаграмму или другую форму, передайте соответствующий объект в Sequence.add_effect вместо target_shape. Для параметров группировки, специфичных для диаграмм, см. Animated Charts.

Чтение анимаций форм

Используйте Sequence.get_effects_by_shape, когда известна целевая форма. Чтобы просмотреть каждый эффект, пройдитесь по основной последовательности и по всем интерактивным последовательностям. Итерация избавляет от предположения, что в последовательности есть эффект под индексом 0.

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

import aspose.slides as slides


def print_sequence(label, sequence):
    print(f"  {label}: {sequence.count} effect(s)")

    for effect in sequence:
        target_name = "unknown" if effect.target_shape is None else effect.target_shape.name
        effect_description = f"{effect.type.name} {effect.subtype.name}; target: {target_name}; trigger: {effect.timing.trigger_type.name}"
        print(f"    {effect_description}")


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    target_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 120, 100, 320, 80)
    target_shape.text_frame.text = "Animated shape"

    main_sequence = slide.timeline.main_sequence
    main_sequence.add_effect(target_shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)

    trigger_shape = slide.shapes.add_auto_shape(slides.ShapeType.BEVEL, 20, 20, 100, 40)
    trigger_shape.text_frame.text = "Move"

    interactive_sequence = slide.timeline.interactive_sequences.add(trigger_shape)
    interactive_sequence.add_effect(target_shape, slides.animation.EffectType.PATH_FOOTBALL, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)

    target_effects = main_sequence.get_effects_by_shape(target_shape)
    print(f"The main sequence contains {len(target_effects)} effect(s) for {target_shape.name}.")

    print_sequence("Main sequence", main_sequence)

    for interactive_index, sequence in enumerate(slide.timeline.interactive_sequences, start=1):
        trigger_name = "unknown" if sequence.trigger_shape is None else sequence.trigger_shape.name
        sequence_label = f"Interactive sequence {interactive_index}, trigger: {trigger_name}"
        print_sequence(sequence_label, sequence)

Если вам нужны эффекты только для одной формы, сначала определите форму по имени, типу заполнителя или другому стабильному свойству; затем вызовите Sequence.get_effects_by_shape. Не рассчитывайте, что форма под индексом 0 всегда будет нужным объектом.

Работа с унаследованными эффектами заполнителей

Заполнитель на обычном слайде может наследовать поведение анимации от соответствующего заполнителя на слайде макета и главном слайде. Shape.get_base_placeholder возвращает родительский заполнитель, или None, если родитель отсутствует.

В представлении примера ниже нижний колонтитул имеет Random Bars на обычном слайде, Split на слайде макета и Fly In на главном слайде.

Эффект анимации нижнего колонтитула на обычном слайде

Эффект анимации заполнителя нижнего колонтитула на слайде макета

Эффект анимации заполнителя нижнего колонтитула на главном слайде

Следующий пример самостоятельно строит иерархию заполнителей. Он добавляет эффекты к заполнителю в мастер‑шаблоне, заполнителю в макете и соответствующему заполнителю на обычном слайде. Каждый вызов Shape.get_base_placeholder проверяется перед использованием возвращённой формы.

import aspose.slides as slides


def find_placeholder_with_base(slide):
    for shape in slide.shapes:
        if shape.get_base_placeholder() is not None:
            return shape

    return None


def print_effects(source, effects):
    print(f"{source}: {len(effects)} effect(s)")

    for effect in effects:
        print(f"  {effect.type.name} {effect.subtype.name}")


with slides.Presentation() as presentation:
    layout_slide = presentation.layout_slides.get_by_type(slides.SlideLayoutType.BLANK)
    layout_placeholder = layout_slide.placeholder_manager.add_text_placeholder(100, 100, 400, 80)
    layout_slide.timeline.main_sequence.add_effect(layout_placeholder, slides.animation.EffectType.SPLIT, slides.animation.EffectSubtype.VERTICAL_IN, slides.animation.EffectTriggerType.ON_CLICK)

    master_placeholder = layout_placeholder.get_base_placeholder()
    if master_placeholder is not None:
        master_sequence = layout_slide.master_slide.timeline.main_sequence
        master_sequence.add_effect(master_placeholder, slides.animation.EffectType.FLY, slides.animation.EffectSubtype.BOTTOM, slides.animation.EffectTriggerType.ON_CLICK)

    slide = presentation.slides.add_empty_slide(layout_slide)
    slide_placeholder = find_placeholder_with_base(slide)

    if slide_placeholder is None:
        raise RuntimeError("The slide does not contain a placeholder linked to its layout slide.")

    slide.timeline.main_sequence.add_effect(slide_placeholder, slides.animation.EffectType.RANDOM_BARS, slides.animation.EffectSubtype.HORIZONTAL, slides.animation.EffectTriggerType.ON_CLICK)
    print_effects("Normal slide", slide.timeline.main_sequence.get_effects_by_shape(slide_placeholder))

    base_layout_placeholder = slide_placeholder.get_base_placeholder()
    if base_layout_placeholder is not None:
        print_effects("Layout slide", layout_slide.timeline.main_sequence.get_effects_by_shape(base_layout_placeholder))

        base_master_placeholder = base_layout_placeholder.get_base_placeholder()
        if base_master_placeholder is not None:
            print_effects("Master slide", layout_slide.master_slide.timeline.main_sequence.get_effects_by_shape(base_master_placeholder))

    presentation.save("placeholder-animations.pptx", slides.export.SaveFormat.PPTX)

Изменение времени анимации

Диалог Timing в PowerPoint соответствует свойствам Timing.

Диалог Timing в PowerPoint для анимационного эффекта

Этот независимый пример добавляет эффект, меняет его время через объект, возвращаемый Sequence.add_effect, и сохраняет результат. Сохранение ссылки на возвращённый Effect избавляет от необходимости использовать индекс коллекции.

import aspose.slides as slides


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 120, 100, 320, 80)
    shape.text_frame.text = "Timed animation"

    effect = slide.timeline.main_sequence.add_effect(shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)
    effect.timing.trigger_type = slides.animation.EffectTriggerType.ON_CLICK
    effect.timing.duration = 2.0
    effect.timing.trigger_delay_time = 0.5
    effect.timing.repeat_until_next_click = False
    effect.timing.repeat_until_end_slide = False
    effect.timing.repeat_count = 2.0
    effect.timing.rewind = True

    presentation.save("shape-animation-timing.pptx", slides.export.SaveFormat.PPTX)

Используйте один режим повторения целенаправленно. Сочетание количества повторений с флагом «until» может давать неоднозначные результаты в разных средствах просмотра. При изменении режимов повторения сначала задайте Timing.repeat_until_next_click и Timing.repeat_until_end_slide, а затем Timing.repeat_count, так как установка любого из флагов также меняет активный режим повторения.

Добавление и извлечение звуков анимации

Эффект анимации может ссылаться на встроенный аудиофайл через Effect.sound. Effect.stop_previous_sound указывает эффекту остановить звук, запущенный предыдущим эффектом.

Добавление звука к эффекту

В следующем примере ожидается локальный аудиофайл animation-sound.wav. Создаются два эффекта, первый получает этот файл в качестве звука, второй настроен на остановку звука. Используются объекты, возвращаемые Sequence.add_effect, поэтому индекс последовательности не требуется.

import aspose.slides as slides


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    first_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 80, 100, 240, 80)
    second_shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 400, 100, 240, 80)
    first_shape.text_frame.text = "Starts sound"
    second_shape.text_frame.text = "Stops sound"

    sequence = slide.timeline.main_sequence
    first_effect = sequence.add_effect(first_shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)
    second_effect = sequence.add_effect(second_shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)

    with open("animation-sound.wav", "rb") as audio_file:
        effect_sound = presentation.audios.add_audio(audio_file.read())

    first_effect.sound = effect_sound
    second_effect.stop_previous_sound = True

    presentation.save("shape-animation-sound.pptx", slides.export.SaveFormat.PPTX)

Извлечение встроенных звуков эффектов

В следующем примере ожидается локальная презентация presentation-with-animation-sounds.pptx. Он сканирует основные и интерактивные последовательности и записывает каждый встроенный звук эффекта в каталог extracted-animation-sounds. Расширение выбирается исходя из MIME‑типа аудио, получаемого через Audio.content_type.

import os

import aspose.slides as slides


def get_audio_extension(content_type):
    normalized_type = "" if content_type is None else content_type.lower()

    if normalized_type == "audio/mpeg":
        return ".mp3"
    if normalized_type == "audio/mp4":
        return ".m4a"
    if normalized_type == "audio/ogg":
        return ".ogg"
    if normalized_type in ("audio/wav", "audio/x-wav"):
        return ".wav"

    return ".bin"


def save_sounds(sequence, output_directory, sound_index):
    for effect in sequence:
        if effect.sound is None:
            continue

        extension = get_audio_extension(effect.sound.content_type)
        output_path = os.path.join(output_directory, f"effect-sound-{sound_index}{extension}")
        with open(output_path, "wb") as output_file:
            output_file.write(bytes(effect.sound.binary_data))
        sound_index += 1

    return sound_index


input_path = "presentation-with-animation-sounds.pptx"
output_directory = "extracted-animation-sounds"

os.makedirs(output_directory, exist_ok=True)

with slides.Presentation(input_path) as presentation:
    sound_index = 1

    for slide in presentation.slides:
        sound_index = save_sounds(slide.timeline.main_sequence, output_directory, sound_index)

        for sequence in slide.timeline.interactive_sequences:
            sound_index = save_sounds(sequence, output_directory, sound_index)

print(f"Extracted {sound_index - 1} sound file(s) to {os.path.abspath(output_directory)}.")

Для больших аудиофайлов используйте Audio.get_stream и копируйте поток в файл вместо загрузки всего объекта в массив байтов.

Установка поведения после анимации

Параметр After animation определяет, что произойдёт с формой после завершения её эффекта.

Диалог параметров эффекта PowerPoint, показывающий настройки After animation

Перечисление AfterAnimationType поддерживает оставление формы без изменений, изменение её цвета, скрытие после анимации или скрытие при следующем щелчке. Когда тип установлен в AfterAnimationType.COLOR, также задайте Effect.after_animation_color.

Этот независимый пример создаёт эффект, задаёт ему поведение после анимации через полученный объект эффекта и сохраняет результат.

import aspose.pydrawing as draw
import aspose.slides as slides


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    shape = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 120, 100, 320, 80)
    shape.text_frame.text = "Dim after animation"

    effect = slide.timeline.main_sequence.add_effect(shape, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)
    effect.after_animation_type = slides.animation.AfterAnimationType.COLOR
    effect.after_animation_color.color = draw.Color.light_gray

    presentation.save("shape-animation-after-effect.pptx", slides.export.SaveFormat.PPTX)

Изменение типа с AfterAnimationType.COLOR удаляет установленный цвет после анимации.

Анимация текста

У анимации текста два связанных параметра:

  • TextAnimation.build_type определяет, появляются ли абзацы одновременно или последовательно.
  • Effect.animate_text_type определяет, появляется ли текст целиком, по словам или по буквам. Effect.delay_between_text_parts задаёт задержку между словами или буквами. Положительное значение — процент от длительности эффекта; отрицательное — задержка в секундах.

В следующем независимом примере анимируются слова в текстовом поле. BuildType.AS_ONE_OBJECT отключает построение абзац за абзацем, так что настройка для слова применяется ко всему текстовому фрейму.

import aspose.slides as slides


with slides.Presentation() as presentation:
    slide = presentation.slides[0]
    text_box = slide.shapes.add_auto_shape(slides.ShapeType.RECTANGLE, 80, 80, 560, 100)
    text_box.text_frame.text = "Aspose.Slides animates this sentence word by word."

    effect = slide.timeline.main_sequence.add_effect(text_box, slides.animation.EffectType.FADE, slides.animation.EffectSubtype.NONE, slides.animation.EffectTriggerType.ON_CLICK)
    effect.text_animation.build_type = slides.animation.BuildType.AS_ONE_OBJECT
    effect.animate_text_type = slides.animation.AnimateTextType.BY_WORD
    effect.delay_between_text_parts = 20.0

    presentation.save("animated-text.pptx", slides.export.SaveFormat.PPTX)

Чтобы построить текстовое поле по абзацам, задайте BuildType.BY_LEVEL_PARAGRAPHS1 (или другой уровень абзаца). Чтобы применить отдельный эффект к отдельному абзацу, используйте перегрузку Sequence.add_effect, принимающую IParagraph. См. Animated Text для примеров уровня абзаца.

Экспорт и замечания о совместимости

  • Сохранение в PPT или PPTX сохраняет модель анимации, но окончательное воспроизведение контролируется средой просмотра презентации.
  • PDF и статические изображения не воспроизводят анимацию. Используйте HTML5 export, анимированный GIF или video conversion, если требуется показать движение.
  • Для HTML5 включите Html5Options.animate_shapes и, при необходимости, Html5Options.animate_transitions.
  • При рендеринге видео поддерживается множество типичных эффектов входа, акцента, выхода и траекторий движения, но не каждый эффект PowerPoint поддерживается. Проверьте текущий список supported animations and effects и протестируйте критические презентации с вашей целевой версией Aspose.Slides.
  • Сложные пользовательские эффекты и эффекты, импортированные из других форматов презентаций, могут сохраняться в файле, но отображаться иначе в PowerPoint, HTML5 или видео. Проводите проверку экспортированного результата, а не полагайтесь только на название эффекта.

FAQ

Почему анимация отображается в PowerPoint, но не в PDF?

PDF — статический формат, поэтому анимация и переходы слайдов не воспроизводятся. Экспортируйте в HTML5, анимированный GIF или видео, если требуется сохранить движение.

Почему эффект выглядит иначе в видео?

При экспорте в видео анимация рендерится, а не сохраняется оригинальное поведение PowerPoint. Некоторые сложные эффекты не поддерживаются или приближенно имитируются. Ознакомьтесь с таблицей поддерживаемых эффектов и протестируйте презентацию перед производством.

Изменяет ли перемещение формы вперёд или назад порядок её анимации?

Нет. Порядок наложения (z‑order) управляет перекрытием, а порядок последовательностей и триггеры — воспроизведением анимации. Изменяйте временную шкалу, если нужен иной порядок воспроизведения.