Appliquer des animations de formes dans les présentations en utilisant JavaScript

Vue d’ensemble

Pour travailler avec les comportements individuels à l’intérieur d’un effet ou modifier les segments de trajectoire de mouvement, voir Animation personnalisée.

Aspose.Slides for Node.js via Java représente les animations de diapositives comme des effets dans une chronologie de diapositive. Un effet possède une forme cible, un type et sous‑type d’animation, un déclencheur, des paramètres de synchronisation, ainsi que des propriétés facultatives telles que le son ou le comportement après l’animation.

La chronologie contient deux types de séquences :

  • La séquence principale se lit au fur et à mesure que la diapositive avance.
  • Une séquence interactive démarre lorsque sa forme déclencheur est cliquée.

Comme les zones de texte, images, graphiques, tableaux et autres objets de diapositive sont des objets Shape , vous utilisez la même méthode Sequence.addEffect pour la plupart du contenu des diapositives. Les effets disponibles sont répertoriés dans l’énumération EffectType .

Ajouter des animations de forme

Pour ajouter une animation, récupérez la séquence principale de la diapositive et appelez Sequence.addEffect avec la forme cible, le type d’effet, le sous‑type et le déclencheur. Pour un effet qui démarre lorsqu’une autre forme est cliquée, créez une séquence interactive dont le déclencheur est cette autre forme.

L’exemple suivant crée les deux types d’animation et enregistre le résultat dans shape-animations.pptx.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);

    const targetShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.RoundCornerRectangle, 120, 100, 320, 80);
    targetShape.addTextFrame("Click to animate this shape");

    const mainSequence = slide.getTimeline().getMainSequence();
    const entranceEffect = mainSequence.addEffect(targetShape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);
    entranceEffect.getTiming().setDuration(java.newFloat(1.5));

    const triggerShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Bevel, 20, 20, 100, 40);
    triggerShape.addTextFrame("Move");

    const interactiveSequence = slide.getTimeline().getInteractiveSequences().add(triggerShape);
    interactiveSequence.addEffect(targetShape, aspose.slides.EffectType.PathFootball, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);

    presentation.save("shape-animations.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Le déclencheur contrôle le moment où un effet démarre :

Pour animer une image, un graphique ou un autre type de forme, transmettez cet objet à Sequence.addEffect au lieu de targetShape. Pour les options de groupement spécifiques aux graphiques, voir Graphiques animés .

Lire les animations de forme

Utilisez Sequence.getEffectsByShape lorsque vous connaissez la forme cible. Pour examiner chaque effet, énumérez la séquence principale et chaque séquence interactive. L’énumération évite de supposer qu’une séquence contient un effet à l’index 0.

L’exemple suivant crée une forme avec des effets de séquence principale et interactive, récupère les effets qui ciblent la forme, puis énumère chaque séquence de la diapositive.

const aspose = { slides: require("aspose.slides.via.java") };

function getEnumName(enumType, value) {
    for (const [name, enumValue] of Object.entries(enumType)) {
        if (enumValue === value) {
            return name;
        }
    }

    return String(value);
}

function printSequence(label, sequence) {
    console.log(`  ${label}: ${sequence.getCount()} effect(s)`);

    for (let i = 0; i < sequence.getCount(); i++) {
        const effect = sequence.get_Item(i);
        const targetName = effect.getTargetShape() == null ? "unknown" : effect.getTargetShape().getName();
        const typeName = getEnumName(aspose.slides.EffectType, effect.getType());
        const subtypeName = getEnumName(aspose.slides.EffectSubtype, effect.getSubtype());
        const triggerName = getEnumName(aspose.slides.EffectTriggerType, effect.getTiming().getTriggerType());
        console.log(`    ${typeName} ${subtypeName}; target: ${targetName}; trigger: ${triggerName}`);
    }
}

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const targetShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 120, 100, 320, 80);
    targetShape.addTextFrame("Animated shape");

    const mainSequence = slide.getTimeline().getMainSequence();
    mainSequence.addEffect(targetShape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);

    const triggerShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Bevel, 20, 20, 100, 40);
    triggerShape.addTextFrame("Move");

    const interactiveSequence = slide.getTimeline().getInteractiveSequences().add(triggerShape);
    interactiveSequence.addEffect(targetShape, aspose.slides.EffectType.PathFootball, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);

    const targetEffects = mainSequence.getEffectsByShape(targetShape);
    console.log(`The main sequence contains ${targetEffects.length} effect(s) for ${targetShape.getName()}.`);

    printSequence("Main sequence", mainSequence);

    const interactiveSequences = slide.getTimeline().getInteractiveSequences();
    for (let i = 0; i < interactiveSequences.getCount(); i++) {
        const sequence = interactiveSequences.get_Item(i);
        const triggerName = sequence.getTriggerShape() == null ? "unknown" : sequence.getTriggerShape().getName();
        printSequence(`Interactive sequence ${i + 1}, trigger: ${triggerName}`, sequence);
    }
} finally {
    presentation.dispose();
}

Si vous n’avez besoin des effets que pour une seule forme, identifiez d’abord la forme par son nom, son type d’espace réservé, ou une autre propriété stable ; puis appelez Sequence.getEffectsByShape. Ne supposez pas que ShapeCollection.get_Item à l’index 0 soit toujours l’objet souhaité.

Travailler avec les effets d’espace réservé hérités

Un espace réservé sur une diapositive normale peut hériter du comportement d’animation de l’espace réservé correspondant sur la diapositive de mise en page et la diapositive maîtresse. Shape.getBasePlaceholder renvoie cet espace réservé parent, ou null lorsqu’aucun parent n’existe.

Dans la présentation d’exemple suivante, le pied de page possède Random Bars sur la diapositive normale, Split sur la diapositive de mise en page, et Fly In sur la diapositive maîtresse.

Effet d’animation du pied de page sur la diapositive normale

Effet d’animation de l’espace réservé du pied de page sur la diapositive de mise en page

Effet d’animation de l’espace réservé du pied de page sur la diapositive maîtresse

L’exemple suivant utilise une hiérarchie d’espaces réservés provenant d’une nouvelle présentation. Il ajoute des effets à un espace réservé maître, un espace réservé de mise en page, et l’espace réservé correspondant sur une diapositive normale. Chaque appel à Shape.getBasePlaceholder est vérifié avant d’utiliser la forme retournée.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

function findPlaceholderWithBase(baseSlide, expectedBase) {
    const shapes = baseSlide.getShapes();

    for (let i = 0; i < shapes.size(); i++) {
        const shape = shapes.get_Item(i);
        const basePlaceholder = shape.getBasePlaceholder();

        if (basePlaceholder == null) {
            continue;
        }

        if (expectedBase == null || basePlaceholder.getPlaceholder().getType() === expectedBase.getPlaceholder().getType()) {
            return shape;
        }
    }

    return null;
}

function getEnumName(enumType, value) {
    for (const [name, enumValue] of Object.entries(enumType)) {
        if (enumValue === value) {
            return name;
        }
    }

    return String(value);
}

function printEffects(source, effects) {
    console.log(`${source}: ${effects.length} effect(s)`);

    for (const effect of effects) {
        const typeName = getEnumName(aspose.slides.EffectType, effect.getType());
        const subtypeName = getEnumName(aspose.slides.EffectSubtype, effect.getSubtype());
        console.log(`  ${typeName} ${subtypeName}`);
    }
}

const presentation = new aspose.slides.Presentation();
try {
    const layoutSlide = presentation.getLayoutSlides().getByType(java.newByte(aspose.slides.SlideLayoutType.TitleAndObject));
    const layoutPlaceholder = findPlaceholderWithBase(layoutSlide, null);

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

    const masterPlaceholder = layoutPlaceholder.getBasePlaceholder();
    layoutSlide.getMasterSlide().getTimeline().getMainSequence().addEffect(masterPlaceholder, aspose.slides.EffectType.Fly, aspose.slides.EffectSubtype.Bottom, aspose.slides.EffectTriggerType.OnClick);
    layoutSlide.getTimeline().getMainSequence().addEffect(layoutPlaceholder, aspose.slides.EffectType.Split, aspose.slides.EffectSubtype.VerticalIn, aspose.slides.EffectTriggerType.OnClick);

    const slide = presentation.getSlides().addEmptySlide(layoutSlide);
    const slidePlaceholder = findPlaceholderWithBase(slide, layoutPlaceholder);

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

    slide.getTimeline().getMainSequence().addEffect(slidePlaceholder, aspose.slides.EffectType.RandomBars, aspose.slides.EffectSubtype.Horizontal, aspose.slides.EffectTriggerType.OnClick);
    printEffects("Normal slide", slide.getTimeline().getMainSequence().getEffectsByShape(slidePlaceholder));

    const baseLayoutPlaceholder = slidePlaceholder.getBasePlaceholder();
    if (baseLayoutPlaceholder != null) {
        printEffects("Layout slide", layoutSlide.getTimeline().getMainSequence().getEffectsByShape(baseLayoutPlaceholder));

        const baseMasterPlaceholder = baseLayoutPlaceholder.getBasePlaceholder();
        if (baseMasterPlaceholder != null) {
            printEffects("Master slide", layoutSlide.getMasterSlide().getTimeline().getMainSequence().getEffectsByShape(baseMasterPlaceholder));
        }
    }

    presentation.save("placeholder-animations.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Modifier le minutage de l’animation

La boîte de dialogue Timing de PowerPoint correspond aux propriétés de Timing .

Boîte de dialogue Timing de PowerPoint pour un effet d’animation

Cet exemple autonome ajoute un effet, modifie son minutage via l’objet retourné par Sequence.addEffect, et enregistre le résultat. Conserver la référence de l'Effect retourné évite un index de collection inutile.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 120, 100, 320, 80);
    shape.addTextFrame("Timed animation");

    const effect = slide.getTimeline().getMainSequence().addEffect(shape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);
    effect.getTiming().setTriggerType(aspose.slides.EffectTriggerType.OnClick);
    effect.getTiming().setDuration(java.newFloat(2.0));
    effect.getTiming().setTriggerDelayTime(java.newFloat(0.5));
    effect.getTiming().setRepeatUntilNextClick(false);
    effect.getTiming().setRepeatUntilEndSlide(false);
    effect.getTiming().setRepeatCount(java.newFloat(2.0));
    effect.getTiming().setRewind(true);

    presentation.save("shape-animation-timing.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Utilisez intentionnellement un seul mode de répétition. Combiner un nombre de répétitions avec un indicateur “until” peut produire des résultats confus selon les visionneuses. Lors du changement de modes de répétition, définissez Timing.setRepeatUntilNextClick et Timing.setRepeatUntilEndSlide avant Timing.setRepeatCount , car la définition de l’un de ces indicateurs modifie également le mode de répétition actif.

Ajouter et extraire les sons d’animation

Un effet d’animation peut référencer un audio embarqué via Effect.getSound . Effect.setStopPreviousSound indique à un effet d’arrêter le son démarré par un effet précédent.

Ajouter un son à un effet

L’exemple suivant suppose un fichier audio local nommé animation-sound.wav. Il crée deux effets, intègre ce fichier comme son du premier effet, et configure le deuxième effet pour arrêter le son. Il utilise les objets retournés par Sequence.addEffect , de sorte qu’aucun index de séquence n’est requis.

const fs = require("fs");
const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const firstShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 80, 100, 240, 80);
    const secondShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 400, 100, 240, 80);
    firstShape.addTextFrame("Starts sound");
    secondShape.addTextFrame("Stops sound");

    const sequence = slide.getTimeline().getMainSequence();
    const firstEffect = sequence.addEffect(firstShape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);
    const secondEffect = sequence.addEffect(secondShape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);

    const audioData = java.newArray("byte", Array.from(fs.readFileSync("animation-sound.wav")));
    const effectSound = presentation.getAudios().addAudio(audioData);
    firstEffect.setSound(effectSound);
    secondEffect.setStopPreviousSound(true);

    presentation.save("shape-animation-sound.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Extraire les sons intégrés aux effets

L’exemple suivant suppose une présentation locale nommée presentation-with-animation-sounds.pptx. Il parcourt les séquences principale et interactive et écrit chaque son d’effet intégré dans le répertoire extracted-animation-sounds . L’extension est choisie à partir du type MIME audio fourni par Audio.getContentType .

const fs = require("fs");
const path = require("path");
const aspose = { slides: require("aspose.slides.via.java") };

function getAudioExtension(contentType) {
    const normalizedType = contentType == null ? "" : contentType.toLowerCase();

    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";
}

function saveSounds(sequence, outputDirectory, soundIndex) {
    for (let i = 0; i < sequence.getCount(); i++) {
        const effect = sequence.get_Item(i);

        if (effect.getSound() == null) {
            continue;
        }

        const extension = getAudioExtension(effect.getSound().getContentType());
        const outputPath = path.join(outputDirectory, `effect-sound-${soundIndex}${extension}`);
        fs.writeFileSync(outputPath, Buffer.from(effect.getSound().getBinaryData()));
        soundIndex++;
    }

    return soundIndex;
}

const outputDirectory = "extracted-animation-sounds";
fs.mkdirSync(outputDirectory, { recursive: true });

const presentation = new aspose.slides.Presentation("presentation-with-animation-sounds.pptx");
try {
    let soundIndex = 1;

    for (let slideIndex = 0; slideIndex < presentation.getSlides().size(); slideIndex++) {
        const slide = presentation.getSlides().get_Item(slideIndex);
        soundIndex = saveSounds(slide.getTimeline().getMainSequence(), outputDirectory, soundIndex);

        const interactiveSequences = slide.getTimeline().getInteractiveSequences();
        for (let sequenceIndex = 0; sequenceIndex < interactiveSequences.getCount(); sequenceIndex++) {
            soundIndex = saveSounds(interactiveSequences.get_Item(sequenceIndex), outputDirectory, soundIndex);
        }
    }

    console.log(`Extracted ${soundIndex - 1} sound file(s) to ${path.resolve(outputDirectory)}.`);
} finally {
    presentation.dispose();
}

Pour les gros objets audio, utilisez Audio.getStream et copiez le flux dans un fichier au lieu de charger l’objet entier dans un tableau d’octets.

Définir le comportement après l’animation

L’option After animation contrôle ce qui arrive à une forme après la fin de son effet.

Boîte de dialogue Options d’effet de PowerPoint affichant les paramètres After animation

L’énumération AfterAnimationType permet de laisser la forme inchangée, de changer sa couleur, de la masquer après l’animation, ou de la masquer au clic suivant. Lorsque le type est AfterAnimationType.Color , définissez également Effect.getAfterAnimationColor .

Cet exemple autonome crée un effet, définit son comportement après l’animation via l’objet effet retourné, et enregistre le résultat.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 120, 100, 320, 80);
    shape.addTextFrame("Dim after animation");

    const effect = slide.getTimeline().getMainSequence().addEffect(shape, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);
    effect.setAfterAnimationType(aspose.slides.AfterAnimationType.Color);
    effect.getAfterAnimationColor().setColor(java.getStaticFieldValue("java.awt.Color", "LIGHT_GRAY"));

    presentation.save("shape-animation-after-effect.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Changer le type depuis AfterAnimationType.Color efface le paramètre de couleur après l’animation.

Animer le texte

L’animation du texte comporte deux contrôles associés :

  • TextAnimation.getBuildType contrôle si les paragraphes apparaissent ensemble ou par niveau de paragraphe.
  • Effect.getAnimateTextType contrôle si le texte apparaît en une fois, par mot ou par lettre. Effect.getDelayBetweenTextParts définit le délai entre les mots ou les lettres. Une valeur positive représente un pourcentage de la durée de l’effet ; une valeur négative représente un délai en secondes.

L’exemple autonome suivant anime les mots d’une zone de texte. BuildType.AsOneObject désactive la construction paragraphe par paragraphe afin que le paramètre de mot s’applique à l’ensemble du cadre de texte.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const textBox = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 80, 80, 560, 100);
    textBox.addTextFrame("Aspose.Slides animates this sentence word by word.");

    const effect = slide.getTimeline().getMainSequence().addEffect(textBox, aspose.slides.EffectType.Fade, aspose.slides.EffectSubtype.None, aspose.slides.EffectTriggerType.OnClick);
    effect.getTextAnimation().setBuildType(aspose.slides.BuildType.AsOneObject);
    effect.setAnimateTextType(aspose.slides.AnimateTextType.ByWord);
    effect.setDelayBetweenTextParts(java.newFloat(20.0));

    presentation.save("animated-text.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Pour construire une zone de texte paragraphe par paragraphe, définissez BuildType.ByLevelParagraphs1 (ou un autre niveau de paragraphe). Pour cibler un paragraphe unique avec son propre effet, utilisez la surcharge de Sequence.addEffect qui accepte un Paragraph . Consultez Texte animé pour des exemples au niveau du paragraphe.

Notes d’exportation et de compatibilité

  • Enregistrer au format PPT ou PPTX préserve le modèle d’animation, mais la lecture finale est contrôlée par le visualiseur de présentation.
  • Les PDF et les images statiques ne jouent pas d’animations. Utilisez l’exportation HTML5 , GIF animé, ou la conversion vidéo lorsque la sortie doit montrer du mouvement.
  • Pour HTML5, activez Html5Options.setAnimateShapes et, si nécessaire, Html5Options.setAnimateTransitions .
  • Le rendu vidéo prend en charge de nombreux effets d’entrée, d’emphase, de sortie et de trajectoire de mouvement courants, mais tous les effets PowerPoint ne sont pas pris en charge. Vérifiez les animations et effets pris en charge actuels et testez les présentations critiques avec votre version cible d’Aspose.Slides.
  • Les effets personnalisés avancés et les effets importés d’autres formats de présentation peuvent être conservés dans le fichier mais s’afficher différemment dans PowerPoint, HTML5 ou la vidéo. Validez le résultat exporté plutôt que de vous fier uniquement au nom de l’effet.

FAQ

Pourquoi une animation apparaît-elle dans PowerPoint mais pas dans un PDF ?

Le PDF est un format statique, donc les animations et les transitions de diapositive ne sont pas jouées. Exportez vers HTML5, GIF animé ou vidéo lorsque le mouvement doit être conservé.

Pourquoi un effet se lit-il différemment dans une vidéo ?

L’exportation vidéo rend les animations au lieu de stocker le comportement original de PowerPoint. Certains effets avancés ne sont pas pris en charge ou sont approximés. Consultez le tableau des effets pris en charge et testez la présentation réelle avant de l’utiliser en production.

Le déplacement d’une forme vers l’avant ou l’arrière modifie-t-il son ordre d’animation ?

Non. L’ordre Z d’une forme contrôle le chevauchement, tandis que l’ordre des séquences et les déclencheurs contrôlent la lecture de l’animation. Modifiez la chronologie si vous avez besoin d’un ordre de lecture différent.