Python via Java를 사용한 프레젠테이션에서 모양 애니메이션 적용

개요

효과 내부의 개별 동작을 다루거나 모션 경로 세그먼트를 편집하려면 맞춤 애니메이션을 참조하십시오.

Aspose.Slides for Python via Java는 슬라이드 애니메이션을 슬라이드 타임라인의 효과로 표현합니다. 효과는 대상 모양, 애니메이션 유형 및 서브 유형, 트리거, 타이밍 설정 및 사운드나 애니메이션 후 동작과 같은 선택적 속성을 가집니다.

타임라인에는 두 가지 종류의 시퀀스가 있습니다:

  • 메인 시퀀스는 슬라이드가 진행될 때 재생됩니다.
  • 대화형 시퀀스는 트리거 모양을 클릭할 때 시작됩니다.

텍스트 상자, 그림, 차트, 표 및 기타 슬라이드 개체는 모두 Shape에서 파생되므로 대부분의 슬라이드 콘텐츠에 대해 동일한 Sequence.addEffect 메서드를 사용합니다. 사용 가능한 효과는 EffectType 클래스에 나열되어 있습니다.

모양 애니메이션 추가

애니메이션을 추가하려면 슬라이드의 메인 시퀀스를 가져와서 대상 모양, 효과 유형, 서브 유형 및 트리거와 함께 Sequence.addEffect를 호출합니다. 다른 모양을 클릭했을 때 시작되는 효과의 경우 해당 다른 모양을 트리거로 하는 대화형 시퀀스를 생성합니다.

다음 예제는 두 종류의 애니메이션을 모두 생성하고 결과를 shape-animations.pptx에 저장합니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)

    target_shape = slide.getShapes().addAutoShape(ShapeType.RoundCornerRectangle, 120, 100, 320, 80)
    target_shape.addTextFrame("Click to animate this shape")

    main_sequence = slide.getTimeline().getMainSequence()
    entrance_effect = main_sequence.addEffect(target_shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)
    entrance_effect.getTiming().setDuration(1.5)

    trigger_shape = slide.getShapes().addAutoShape(ShapeType.Bevel, 20, 20, 100, 40)
    trigger_shape.addTextFrame("Move")

    interactive_sequence = slide.getTimeline().getInteractiveSequences().add(trigger_shape)
    interactive_sequence.addEffect(target_shape, EffectType.PathFootball, EffectSubtype.None_, EffectTriggerType.OnClick)

    presentation.save("shape-animations.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

트리거는 효과가 언제 시작되는지를 제어합니다:

그림, 차트 또는 기타 모양 유형을 애니메이션하려면 target_shape 대신 해당 개체를 Sequence.addEffect에 전달합니다. 차트 전용 그룹화 옵션은 애니메이션 차트를 참조하십시오.

모양 애니메이션 읽기

대상 모양을 알고 있을 때는 Sequence.getEffectsByShape를 사용하십시오. 모든 효과를 검사하려면 메인 시퀀스와 모든 대화형 시퀀스를 열거합니다. 열거는 시퀀스가 인덱스 0에 효과가 있다고 가정하는 것을 피하게 해줍니다.

다음 예제는 메인 시퀀스와 대화형 효과가 있는 모양을 만들고, 해당 모양을 대상으로 하는 효과를 가져온 다음 슬라이드의 모든 시퀀스를 열거합니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Presentation, ShapeType

def print_sequence(label, sequence):
    print(f"  {label}: {sequence.getCount()} effect(s)")
    for effect in sequence:
        target_shape = effect.getTargetShape()
        target_name = "unknown" if target_shape is None else target_shape.getName()
        type_name = EffectType.getName(EffectType.class_, effect.getType())
        subtype_name = EffectSubtype.getName(EffectSubtype.class_, effect.getSubtype())
        trigger_name = EffectTriggerType.getName(EffectTriggerType.class_, effect.getTiming().getTriggerType())
        effect_description = f"{type_name} {subtype_name}; target: {target_name}; trigger: {trigger_name}"
        print(f"    {effect_description}")


presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    target_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 120, 100, 320, 80)
    target_shape.addTextFrame("Animated shape")

    main_sequence = slide.getTimeline().getMainSequence()
    main_sequence.addEffect(target_shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)

    trigger_shape = slide.getShapes().addAutoShape(ShapeType.Bevel, 20, 20, 100, 40)
    trigger_shape.addTextFrame("Move")

    interactive_sequence = slide.getTimeline().getInteractiveSequences().add(trigger_shape)
    interactive_sequence.addEffect(target_shape, EffectType.PathFootball, EffectSubtype.None_, EffectTriggerType.OnClick)

    target_effects = main_sequence.getEffectsByShape(target_shape)
    print(f"The main sequence contains {len(target_effects)} effect(s) for {target_shape.getName()}.")
    print_sequence("Main sequence", main_sequence)

    for interactive_index, sequence in enumerate(slide.getTimeline().getInteractiveSequences(), start=1):
        trigger_shape = sequence.getTriggerShape()
        trigger_name = "unknown" if trigger_shape is None else trigger_shape.getName()
        sequence_label = f"Interactive sequence {interactive_index}, trigger: {trigger_name}"
        print_sequence(sequence_label, sequence)
finally:
    presentation.dispose()

하나의 모양에 대한 효과만 필요하면 이름, 자리표시자 유형 또는 다른 안정적인 속성으로 모양을 식별한 후 Sequence.getEffectsByShape를 호출하십시오. 인덱스 0에 있는 ShapeCollection.get_Item이 항상 원하는 객체라고 가정하지 마십시오.

상속된 자리표시자 효과 작업

일반 슬라이드의 자리표시자는 레이아웃 슬라이드와 마스터 슬라이드의 해당 자리표시자에서 애니메이션 동작을 상속할 수 있습니다. Shape.getBasePlaceholder는 부모 자리표시자를 반환하며, 부모가 없으면 None을 반환합니다.

다음 예제 프레젠테이션에서 바닥글은 일반 슬라이드에서는 Random Bars, 레이아웃 슬라이드에서는 Split, 마스터 슬라이드에서는 Fly In 효과를 갖습니다.

일반 슬라이드의 바닥글 애니메이션 효과

레이아웃 슬라이드의 바닥글 자리표시자 애니메이션 효과

마스터 슬라이드의 바닥글 자리표시자 애니메이션 효과

다음 예제는 새 프레젠테이션의 자리표시자 계층 구조를 사용합니다. 마스터 자리표시자, 레이아웃 자리표시자 및 일반 슬라이드의 해당 자리표시자에 효과를 추가합니다. Shape.getBasePlaceholder를 호출할 때마다 반환된 모양이 사용되기 전에 확인합니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, SlideLayoutType

def find_placeholder_with_base(slide, expected_base=None):
    for shape in slide.getShapes():
        base_placeholder = shape.getBasePlaceholder()
        if base_placeholder is not None and (expected_base is None or base_placeholder == expected_base):
            return shape
    return None


def print_effects(source, effects):
    print(f"{source}: {len(effects)} effect(s)")
    for effect in effects:
        type_name = EffectType.getName(EffectType.class_, effect.getType())
        subtype_name = EffectSubtype.getName(EffectSubtype.class_, effect.getSubtype())
        print(f"  {type_name} {subtype_name}")


presentation = Presentation()
try:
    layout_slide = presentation.getLayoutSlides().getByType(SlideLayoutType.TitleAndObject)
    layout_placeholder = find_placeholder_with_base(layout_slide) if layout_slide is not None else None
    if layout_placeholder is None:
        print("The layout slide does not contain a placeholder linked to its master slide.")
    else:
        master_placeholder = layout_placeholder.getBasePlaceholder()
        layout_slide.getMasterSlide().getTimeline().getMainSequence().addEffect(master_placeholder, EffectType.Fly, EffectSubtype.Bottom, EffectTriggerType.OnClick)
        layout_slide.getTimeline().getMainSequence().addEffect(layout_placeholder, EffectType.Split, EffectSubtype.VerticalIn, EffectTriggerType.OnClick)

        slide = presentation.getSlides().addEmptySlide(layout_slide)
        slide_placeholder = find_placeholder_with_base(slide, layout_placeholder)
        if slide_placeholder is None:
            print("The slide does not contain a placeholder linked to its layout slide.")
        else:
            slide.getTimeline().getMainSequence().addEffect(slide_placeholder, EffectType.RandomBars, EffectSubtype.Horizontal, EffectTriggerType.OnClick)
            slide_effects = slide.getTimeline().getMainSequence().getEffectsByShape(slide_placeholder)
            print_effects("Normal slide", slide_effects)

            base_layout_placeholder = slide_placeholder.getBasePlaceholder()
            if base_layout_placeholder is not None:
                layout_effects = layout_slide.getTimeline().getMainSequence().getEffectsByShape(base_layout_placeholder)
                print_effects("Layout slide", layout_effects)

                base_master_placeholder = base_layout_placeholder.getBasePlaceholder()
                if base_master_placeholder is not None:
                    master_effects = layout_slide.getMasterSlide().getTimeline().getMainSequence().getEffectsByShape(base_master_placeholder)
                    print_effects("Master slide", master_effects)

            presentation.save("placeholder-animations.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

애니메이션 타이밍 변경

PowerPoint 타이밍 대화상자는 Timing 속성에 매핑됩니다.

애니메이션 효과에 대한 PowerPoint 타이밍 대화상자

이 독립 예제는 효과를 추가하고, Sequence.addEffect에서 반환된 객체를 통해 타이밍을 변경한 뒤 결과를 저장합니다. 반환된 Effect 참조를 유지하면 불필요한 컬렉션 인덱스를 피할 수 있습니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 120, 100, 320, 80)
    shape.addTextFrame("Timed animation")

    effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)
    effect.getTiming().setTriggerType(EffectTriggerType.OnClick)
    effect.getTiming().setDuration(2.0)
    effect.getTiming().setTriggerDelayTime(0.5)
    effect.getTiming().setRepeatUntilNextClick(False)
    effect.getTiming().setRepeatUntilEndSlide(False)
    effect.getTiming().setRepeatCount(2.0)
    effect.getTiming().setRewind(True)

    presentation.save("shape-animation-timing.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

반복 모드를 하나만 사용하십시오. 반복 횟수와 “until” 플래그를 동시에 설정하면 뷰어마다 혼란스러운 결과가 나타날 수 있습니다. 반복 모드를 변경할 때는 Timing.setRepeatUntilNextClick와 Timing.setRepeatUntilEndSlide를 먼저 설정하고, 그 다음에 Timing.setRepeatCount를 호출하십시오. 두 플래그 중 하나를 설정하면 활성 반복 모드도 변경됩니다.

애니메이션 사운드 추가 및 추출

애니메이션 효과는 Effect.getSound를 통해 포함된 오디오를 참조할 수 있습니다. Effect.setStopPreviousSound는 이전 효과가 시작한 오디오를 중지하도록 지시합니다.

효과에 사운드 추가

다음 예제는 animation-sound.wav라는 로컬 오디오 파일을 기대합니다. 두 효과를 만들고 첫 번째 효과에 해당 파일을 사운드로 포함하며, 두 번째 효과가 사운드를 중지하도록 구성합니다. Sequence.addEffect에서 반환된 객체를 사용하므로 시퀀스 인덱스가 필요하지 않습니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, ShapeType
from pathlib import Path

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    first_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 80, 100, 240, 80)
    second_shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 400, 100, 240, 80)
    first_shape.addTextFrame("Starts sound")
    second_shape.addTextFrame("Stops sound")

    sequence = slide.getTimeline().getMainSequence()
    first_effect = sequence.addEffect(first_shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)
    second_effect = sequence.addEffect(second_shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)

    audio_data = Path("animation-sound.wav").read_bytes()
    effect_sound = presentation.getAudios().addAudio(jpype.JArray(jpype.JByte)(audio_data))
    first_effect.setSound(effect_sound)
    second_effect.setStopPreviousSound(True)

    presentation.save("shape-animation-sound.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

포함된 효과 사운드 추출

다음 예제는 presentation-with-animation-sounds.pptx라는 로컬 프레젠테이션을 기대합니다. 메인 및 대화형 시퀀스를 모두 스캔하고 모든 포함된 효과 사운드를 extracted-animation-sounds 디렉터리에 씁니다. 확장자는 Audio.getContentType에서 제공되는 오디오 MIME 유형에 따라 선택됩니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import Presentation
from pathlib import Path

def get_audio_extension(content_type):
    normalized_type = "" if content_type is None else str(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:
        sound = effect.getSound()
        if sound is None:
            continue
        extension = get_audio_extension(sound.getContentType())
        output_path = output_directory / f"effect-sound-{sound_index}{extension}"
        audio_data = bytes(sound.getBinaryData())
        output_path.write_bytes(audio_data)
        sound_index += 1
    return sound_index


input_path = Path("presentation-with-animation-sounds.pptx")
output_directory = Path("extracted-animation-sounds")
output_directory.mkdir(parents=True, exist_ok=True)

presentation = Presentation(str(input_path))
try:
    sound_index = 1
    for slide in presentation.getSlides():
        sound_index = save_sounds(slide.getTimeline().getMainSequence(), output_directory, sound_index)
        for sequence in slide.getTimeline().getInteractiveSequences():
            sound_index = save_sounds(sequence, output_directory, sound_index)
    print(f"Extracted {sound_index - 1} sound file(s) to {output_directory.resolve()}.")
finally:
    presentation.dispose()

대용량 오디오 객체의 경우 Audio.getStream를 사용하고 스트림을 파일로 복사하여 전체 객체를 바이트 배열로 로드하는 것을 피하십시오.

애니메이션 후 동작 설정

After animation 옵션은 효과가 끝난 후 모양에 대해 수행할 작업을 제어합니다.

After animation 설정을 표시하는 PowerPoint 효과 옵션 대화상자

AfterAnimationType 클래스는 모양을 그대로 두거나 색상을 변경하거나 애니메이션 후 숨기거나 다음 클릭 시 숨기는 옵션을 지원합니다. 유형이 AfterAnimationType.Color인 경우 Effect.getAfterAnimationColor도 설정하십시오.

이 독립 예제는 효과를 만들고 반환된 효과 객체를 통해 after‑animation 동작을 설정한 뒤 결과를 저장합니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import AfterAnimationType, EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, ShapeType
from java.awt import Color

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 120, 100, 320, 80)
    shape.addTextFrame("Dim after animation")

    effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)
    effect.setAfterAnimationType(AfterAnimationType.Color)
    effect.getAfterAnimationColor().setColor(Color.LIGHT_GRAY)

    presentation.save("shape-animation-after-effect.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

AfterAnimationType.Color 유형을 다른 것으로 변경하면 after‑animation 색상 설정이 지워집니다.

텍스트 애니메이션

텍스트 애니메이션에는 두 가지 관련 제어가 있습니다:

  • TextAnimation.getBuildType은 문단이 함께 나타날지 문단 수준별로 나타날지를 제어합니다.
  • Effect.getAnimateTextType은 텍스트가 한 번에, 단어별로 또는 글자별로 나타날지를 제어합니다. Effect.getDelayBetweenTextParts는 단어 또는 글자 사이의 지연을 설정합니다. 양수 값은 효과 지속 시간의 백분율이며, 음수 값은 초 단위 지연입니다.

다음 독립 예제는 텍스트 상자의 단어를 애니메이션합니다. BuildType.AsOneObject은 문단별 빌드를 비활성화하여 단어 설정이 전체 텍스트 프레임에 적용되도록 합니다.

import jpype
import asposeslides

if not jpype.isJVMStarted():
    jpype.startJVM()

from asposeslides.api import AnimateTextType, BuildType, EffectSubtype, EffectTriggerType, EffectType, Presentation, SaveFormat, ShapeType

presentation = Presentation()
try:
    slide = presentation.getSlides().get_Item(0)
    text_box = slide.getShapes().addAutoShape(ShapeType.Rectangle, 80, 80, 560, 100)
    text_box.addTextFrame("Aspose.Slides animates this sentence word by word.")

    effect = slide.getTimeline().getMainSequence().addEffect(text_box, EffectType.Fade, EffectSubtype.None_, EffectTriggerType.OnClick)
    effect.getTextAnimation().setBuildType(BuildType.AsOneObject)
    effect.setAnimateTextType(AnimateTextType.ByWord)
    effect.setDelayBetweenTextParts(20.0)

    presentation.save("animated-text.pptx", SaveFormat.Pptx)
finally:
    presentation.dispose()

문단별로 텍스트 상자를 빌드하려면 BuildType.ByLevelParagraphs1 (또는 다른 문단 수준)를 설정하십시오. 개별 문단에 자체 효과를 적용하려면 Paragraph를 받아들이는 Sequence.addEffect 오버로드를 사용하십시오. 문단 수준 예제는 애니메이션 텍스트를 참조하십시오.

내보내기 및 호환성 참고 사항

  • PPT 또는 PPTX로 저장하면 애니메이션 모델이 보존되지만 최종 재생은 프레젠테이션 뷰어에 의해 제어됩니다.
  • PDF와 정적 이미지는 애니메이션을 재생하지 않습니다. 모션을 표시해야 할 경우 HTML5 내보내기, 애니메이션 GIF 또는 비디오 변환을 사용하십시오.
  • HTML5에서는 Html5Options.setAnimateShapes를 활성화하고 필요에 따라 Html5Options.setAnimateTransitions도 설정하십시오.
  • 비디오 렌더링은 일반적인 입장, 강조, 퇴장 및 모션 경로 효과를 많이 지원하지만 모든 PowerPoint 효과를 지원하지는 않습니다. 현재 지원되는 애니메이션 및 효과를 확인하고 대상 Aspose.Slides 버전으로 중요한 프레젠테이션을 테스트하십시오.
  • 고급 맞춤 효과 및 다른 프레젠테이션 형식에서 가져온 효과는 파일에 보존될 수 있지만 PowerPoint, HTML5 또는 비디오에서 다르게 렌더링될 수 있습니다. 효과 이름에만 의존하지 말고 내보낸 결과를 검증하십시오.

FAQ

왜 애니메이션은 PowerPoint에서는 보이지만 PDF에서는 보이지 않나요?

PDF는 정적 포맷이므로 애니메이션과 슬라이드 전환이 재생되지 않습니다. 모션을 유지해야 할 경우 HTML5, 애니메이션 GIF 또는 비디오로 내보내십시오.

왜 효과가 비디오에서 다르게 재생되나요?

비디오 내보내기는 애니메이션을 렌더링하여 원본 PowerPoint 동작을 저장하지 않습니다. 일부 고급 효과는 지원되지 않거나 근사화됩니다. 지원되는 효과 표를 검토하고 실제 프레젠테이션을 테스트한 후에 사용하십시오.

모양을 앞으로 또는 뒤로 이동하면 애니메이션 순서가 변경되나요?

아니요. 모양의 z‑order는 겹침을 제어하고, 시퀀스 순서와 트리거는 애니메이션 재생을 제어합니다. 다른 재생 순서가 필요하면 타임라인을 변경하십시오.