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

Обзор

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

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

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

  • Основная последовательность воспроизводится при продвижении слайда.
  • Интерактивная последовательность начинается, когда её триггерная фигура нажата.

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

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

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

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

using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var slide = presentation.Slides[0];

var targetShape = slide.Shapes.AddAutoShape(ShapeType.RoundCornerRectangle, 120, 100, 320, 80);
targetShape.TextFrame.Text = "Click to animate this shape";

var mainSequence = slide.Timeline.MainSequence;
var entranceEffect = mainSequence.AddEffect(targetShape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
entranceEffect.Timing.Duration = 1.5f;

var triggerShape = slide.Shapes.AddAutoShape(ShapeType.Bevel, 20, 20, 100, 40);
triggerShape.TextFrame.Text = "Move";

var interactiveSequence = slide.Timeline.InteractiveSequences.Add(triggerShape);
interactiveSequence.AddEffect(targetShape, EffectType.PathFootball, EffectSubtype.None, EffectTriggerType.OnClick);

presentation.Save("shape-animations.pptx", SaveFormat.Pptx);

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

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

Чтобы анимировать изображение, диаграмму или другую фигуру, передайте этот объект в ISequence.AddEffect вместо targetShape. Для параметров группировки, специфичных для диаграмм, см. Анимированные диаграммы.

Чтение анимаций фигур

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

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

using System;
using Aspose.Slides;
using Aspose.Slides.Animation;

using var presentation = new Presentation();
var slide = presentation.Slides[0];
var targetShape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 120, 100, 320, 80);
targetShape.TextFrame.Text = "Animated shape";

var mainSequence = slide.Timeline.MainSequence;
mainSequence.AddEffect(targetShape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);

var triggerShape = slide.Shapes.AddAutoShape(ShapeType.Bevel, 20, 20, 100, 40);
triggerShape.TextFrame.Text = "Move";

var interactiveSequence = slide.Timeline.InteractiveSequences.Add(triggerShape);
interactiveSequence.AddEffect(targetShape, EffectType.PathFootball, EffectSubtype.None, EffectTriggerType.OnClick);

var targetEffects = mainSequence.GetEffectsByShape(targetShape);
Console.WriteLine($"The main sequence contains {targetEffects.Length} effect(s) for {targetShape.Name}.");

PrintSequence("Main sequence", mainSequence);

var interactiveIndex = 1;
foreach (var sequence in slide.Timeline.InteractiveSequences)
{
    var triggerName = sequence.TriggerShape == null ? "unknown" : sequence.TriggerShape.Name;
    var sequenceLabel = $"Interactive sequence {interactiveIndex}, trigger: {triggerName}";
    PrintSequence(sequenceLabel, sequence);
    interactiveIndex++;
}

static void PrintSequence(string label, ISequence sequence)
{
    Console.WriteLine($"  {label}: {sequence.Count} effect(s)");

    foreach (var effect in sequence)
    {
        var targetName = effect.TargetShape == null ? "unknown" : effect.TargetShape.Name;
        var effectDescription = $"{effect.Type} {effect.Subtype}; target: {targetName}; trigger: {effect.Timing.TriggerType}";
        Console.WriteLine($"    {effectDescription}");
    }
}

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

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

Заполнитель на обычном слайде может наследовать поведение анимации от соответствующего заполнителя на слайде‑разметке и мастер‑слайде. IShape.GetBasePlaceholder возвращает этот родительский заполнитель или null, если родитель не существует.

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

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

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

Эффект анимации нижнего колонтитула на мастер‑слайде

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

using System;
using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var layoutSlide = presentation.LayoutSlides.GetByType(SlideLayoutType.Blank);
var layoutPlaceholder = layoutSlide.PlaceholderManager.AddTextPlaceholder(100, 100, 400, 80);
layoutSlide.Timeline.MainSequence.AddEffect(layoutPlaceholder, EffectType.Split, EffectSubtype.VerticalIn, EffectTriggerType.OnClick);

var masterPlaceholder = layoutPlaceholder.GetBasePlaceholder();
if (masterPlaceholder != null)
{
    var masterSequence = layoutSlide.MasterSlide.Timeline.MainSequence;
    masterSequence.AddEffect(masterPlaceholder, EffectType.Fly, EffectSubtype.Bottom, EffectTriggerType.OnClick);
}

var slide = presentation.Slides.AddEmptySlide(layoutSlide);
var slidePlaceholder = FindPlaceholderWithBase(slide);

if (slidePlaceholder == null)
{
    throw new InvalidOperationException("The slide does not contain a placeholder linked to its layout slide.");
}

slide.Timeline.MainSequence.AddEffect(slidePlaceholder, EffectType.RandomBars, EffectSubtype.Horizontal, EffectTriggerType.OnClick);
PrintEffects("Normal slide", slide.Timeline.MainSequence.GetEffectsByShape(slidePlaceholder));

var baseLayoutPlaceholder = slidePlaceholder.GetBasePlaceholder();
if (baseLayoutPlaceholder != null)
{
    PrintEffects("Layout slide", layoutSlide.Timeline.MainSequence.GetEffectsByShape(baseLayoutPlaceholder));

    var baseMasterPlaceholder = baseLayoutPlaceholder.GetBasePlaceholder();
    if (baseMasterPlaceholder != null)
    {
        PrintEffects("Master slide", layoutSlide.MasterSlide.Timeline.MainSequence.GetEffectsByShape(baseMasterPlaceholder));
    }
}

presentation.Save("placeholder-animations.pptx", SaveFormat.Pptx);

static IShape FindPlaceholderWithBase(ISlide slide)
{
    foreach (var shape in slide.Shapes)
    {
        if (shape.GetBasePlaceholder() != null)
        {
            return shape;
        }
    }

    return null;
}

static void PrintEffects(string source, IEffect[] effects)
{
    Console.WriteLine($"{source}: {effects.Length} effect(s)");

    foreach (var effect in effects)
    {
        Console.WriteLine($"  {effect.Type} {effect.Subtype}");
    }
}

Изменение тайминга анимации

Диалог Timing PowerPoint отображает свойства ITiming.

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

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

using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var slide = presentation.Slides[0];
var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 120, 100, 320, 80);
shape.TextFrame.Text = "Timed animation";

var effect = slide.Timeline.MainSequence.AddEffect(shape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
effect.Timing.TriggerType = EffectTriggerType.OnClick;
effect.Timing.Duration = 2.0f;
effect.Timing.TriggerDelayTime = 0.5f;
effect.Timing.RepeatUntilNextClick = false;
effect.Timing.RepeatUntilEndSlide = false;
effect.Timing.RepeatCount = 2.0f;
effect.Timing.Rewind = true;

presentation.Save("shape-animation-timing.pptx", SaveFormat.Pptx);

Используйте один режим повторения сознательно. Сочетание количества повторов с флагом «until» может давать непредсказуемые результаты в разных проигрывателях. При изменении режима повторения сначала задайте ITiming.RepeatUntilNextClick и ITiming.RepeatUntilEndSlide, а затем ITiming.RepeatCount, поскольку установка любого из флагов также меняет активный режим повторения.

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

Анимационный эффект может ссылаться на встроенный аудио‑файл через IEffect.Sound. IEffect.StopPreviousSound указывает эффекту остановить звук, запущенный предыдущим эффектом.

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

Следующий пример ожидает локальный аудио‑файл с именем animation-sound.wav. Он создаёт два эффекта, встраивает этот файл как звук для первого эффекта и настраивает второй эффект для остановки звука. Он использует объекты, возвращённые ISequence.AddEffect, поэтому индекс последовательности не требуется.

using System.IO;
using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var slide = presentation.Slides[0];
var firstShape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 80, 100, 240, 80);
var secondShape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 400, 100, 240, 80);
firstShape.TextFrame.Text = "Starts sound";
secondShape.TextFrame.Text = "Stops sound";

var sequence = slide.Timeline.MainSequence;
var firstEffect = sequence.AddEffect(firstShape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
var secondEffect = sequence.AddEffect(secondShape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);

var audioData = File.ReadAllBytes("animation-sound.wav");
var effectSound = presentation.Audios.AddAudio(audioData);
firstEffect.Sound = effectSound;
secondEffect.StopPreviousSound = true;

presentation.Save("shape-animation-sound.pptx", SaveFormat.Pptx);

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

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

using System;
using System.IO;
using Aspose.Slides;
using Aspose.Slides.Animation;

var inputPath = "presentation-with-animation-sounds.pptx";
var outputDirectory = "extracted-animation-sounds";

Directory.CreateDirectory(outputDirectory);

using var presentation = new Presentation(inputPath);
var soundIndex = 1;

foreach (var slide in presentation.Slides)
{
    SaveSounds(slide.Timeline.MainSequence, outputDirectory, ref soundIndex);

    foreach (var sequence in slide.Timeline.InteractiveSequences)
    {
        SaveSounds(sequence, outputDirectory, ref soundIndex);
    }
}

Console.WriteLine($"Extracted {soundIndex - 1} sound file(s) to {Path.GetFullPath(outputDirectory)}.");

static void SaveSounds(ISequence sequence, string outputDirectory, ref int soundIndex)
{
    foreach (var effect in sequence)
    {
        if (effect.Sound == null)
            continue;

        var extension = GetAudioExtension(effect.Sound.ContentType);
        var outputPath = Path.Combine(outputDirectory, $"effect-sound-{soundIndex}{extension}");
        File.WriteAllBytes(outputPath, effect.Sound.BinaryData);
        soundIndex++;
    }
}

static string GetAudioExtension(string contentType)
{
    var normalizedType = contentType == null ? string.Empty : contentType.ToLowerInvariant();

    if (normalizedType == "audio/mpeg")
        return ".mp3";

    if (normalizedType == "audio/mp4")
        return ".m4a";

    if (normalizedType == "audio/ogg")
        return ".ogg";

    if (normalizedType == "audio/wav" || normalizedType == "audio/x-wav")
        return ".wav";

    return ".bin";
}

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

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

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

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

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

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

using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var slide = presentation.Slides[0];
var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 120, 100, 320, 80);
shape.TextFrame.Text = "Dim after animation";

var effect = slide.Timeline.MainSequence.AddEffect(shape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
effect.AfterAnimationType = AfterAnimationType.Color;
effect.AfterAnimationColor.Color = Color.LightGray;

presentation.Save("shape-animation-after-effect.pptx", SaveFormat.Pptx);

Изменение типа с AfterAnimationType.Color снимает настройку цвета после анимации.

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

Анимация текста управляется двумя связанными параметрами:

  • ITextAnimation.BuildType определяет, появляются ли абзацы вместе или по уровням абзацев.
  • IEffect.AnimateTextType задаёт, появляется ли текст сразу целиком, по слову или по букве. IEffect.DelayBetweenTextParts задаёт задержку между словами или буквами. Положительное значение — процент от длительности эффекта; отрицательное значение — задержка в секундах.

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

using Aspose.Slides;
using Aspose.Slides.Animation;
using Aspose.Slides.Export;

using var presentation = new Presentation();
var slide = presentation.Slides[0];
var textBox = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 80, 80, 560, 100);
textBox.TextFrame.Text = "Aspose.Slides animates this sentence word by word.";

var effect = slide.Timeline.MainSequence.AddEffect(textBox, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
effect.TextAnimation.BuildType = BuildType.AsOneObject;
effect.AnimateTextType = AnimateTextType.ByWord;
effect.DelayBetweenTextParts = 20.0f;

presentation.Save("animated-text.pptx", SaveFormat.Pptx);

Чтобы строить текстовое поле по абзацам, задайте BuildType.ByLevelParagraphs1 (или другой уровень абзаца). Чтобы применить отдельный эффект к отдельному абзацу, используйте перегрузку ISequence.AddEffect, принимающую IParagraph. См. Анимированный текст для примеров на уровне абзацев.

Примечания по экспорту и совместимости

  • Сохранение в PPT или PPTX сохраняет модель анимации, но окончательное воспроизведение управляется средством просмотра презентации.
  • PDF и статические изображения не воспроизводят анимацию. Используйте Экспорт в HTML5, анимированный GIF или конвертацию в видео, когда необходимо показать движение.
  • Для HTML5 включите Html5Options.AnimateShapes и при необходимости Html5Options.AnimateTransitions.
  • Видеорендеринг поддерживает многие распространённые эффекты входа, акцента, выхода и траекторий, но не каждый эффект PowerPoint поддерживается. Проверьте текущий список поддерживаемых анимаций и эффектов и протестируйте критические презентации с вашей целевой версией Aspose.Slides.
  • Сложные пользовательские эффекты и эффекты, импортированные из других форматов, могут сохраняться в файле, но отображаться иначе в PowerPoint, HTML5 или видео. Проверяйте экспортированный результат, а не только имя эффекта.

Часто задаваемые вопросы

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

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

Почему эффект воспроизводится иначе в видео?

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

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

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