Android에서 사용자 지정 애니메이션 동작 만들기 및 수정

개요

맞춤 애니메이션 동작을 사용하면 색상 변경, 도형 회전 또는 편집 가능한 모션 경로 따위와 같은 애니메이션 효과의 개별 작업을 제어할 수 있습니다. 이 가이드는 동작을 만들고 결합하는 방법, 타이밍을 구성하는 방법, 기존 애니메이션을 검사·수정하는 방법, 그리고 프레젠테이션을 저장하고 다시 열었을 때 속성이 유지되는지 확인하는 방법을 보여줍니다.

미리 정의된 효과와 클릭 트리거에 대해서는 도형 애니메이션을 참고하십시오.

애니메이션 모델 이해

애니메이션은 Timeline → Sequence → Effect → Behaviors 로 구성됩니다:

  • getTimeline 메서드는 슬라이드 타임라인을 반환하며, 여기에는 기본 시퀀스와 인터랙티브 시퀀스가 포함됩니다.
  • ISequence 은 다양한 도형을 대상으로 할 수 있는 효과들을 포함합니다.
  • IEffect 은 대상 도형, 프리셋, 서브타입 및 효과 타이밍을 식별합니다.
  • IEffect.getBehaviors 가 반환하는 컬렉션에는 색상 변경, 이동, 회전, 속성 설정 등 효과를 구현하는 작업이 들어 있습니다.

개별 동작 만들기

ISequence.addEffect 를 호출하여 효과를 만들고 getBehaviors 컬렉션에 접근합니다. 프리셋을 사용하면 이 컬렉션이 자동으로 채워집니다. 프리셋을 확장할 때는 해당 작업을 유지하거나, 의도적으로 교체할 경우 clear 를 사용하십시오.

IBehaviorFactory 는 아래에 나타낸 여덟 가지 동작 유형을 생성합니다. 모션에 대해서는 모션 경로 만들기 를 참고하십시오. 각 스니펫에는 필요한 import 문이 포함되어 있으며, 실행 가능한 구문은 메서드 내부에 배치합니다. 이후 편집 예제에서는 사용한 출력 파일명을 명시합니다. Android에서는 샘플 파일명을 앱이 접근 가능한 디렉터리(예: 앱의 files 디렉터리)의 전체 경로로 교체하십시오.

회전

createRotationEffect 로 회전 효과를 생성합니다. getBy 은 상대 각도를 도(°) 단위로 지정하고, getFromgetTo 은 시작점과 끝점을 지정합니다.

예제는 Spin 효과를 시작점으로 하여 프리셋 작업을 하나의 회전 동작으로 교체하고, 해당 동작에 2초 지속 시간을 부여합니다. 90도라는 상대 각도는 도형의 시작 방향에서 1/4회전임을 의미하므로 명시적인 시작 각도를 지정할 필요가 없습니다.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Spin, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IRotationEffect rotation = factory.createRotationEffect();
    rotation.setBy(90f);
    rotation.getTiming().setDuration(2f);

    effect.getBehaviors().add(rotation);

    presentation.save("rotation.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

rotation.pptx에는 하나의 도형과 하나의 회전 동작이 들어 있습니다. 아래의 컬렉션, 타이밍 및 회전 편집 예제는 이 파일을 사용합니다.

크기 조절

[X/Y 백분율]을 사용하려면 createScaleEffect 를 이용합니다. getFromgetTo 은 시작 및 종료 크기를 설명하고, getBy 은 상대 변화를 나타냅니다. 여기서 100은 원래 크기를 의미합니다.

예제는 두 차원을 100%에서 125%까지 2초에 걸쳐 확대합니다. 가로·세로 비율을 동일하게 지정하면 도형의 비율이 유지되며, 비율을 다르게 지정하면 한 차원이 더 늘어나게 됩니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.GrowShrink, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IScaleEffect scale = factory.createScaleEffect();
    scale.setFrom(new PointF(100, 100));
    scale.setTo(new PointF(125, 125));
    scale.getTiming().setDuration(2f);

    effect.getBehaviors().add(scale);

    presentation.save("scale.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

색상

createColorEffect 를 사용하여 채우기 색을 파랑에서 주황색으로 변경합니다. getFromgetTo 은 색상이며, getBy 은 색상 오프셋입니다. IBehavior.getProperties 은 애니메이션되는 속성을 식별합니다.

도형의 단색 채우기는 파랑으로 초기화되며, 이는 애니메이션 시작 색과 일치합니다. 채우기 색 속성을 선택하면 동작이 어떤 부분을 변경해야 하는지 지정할 수 있습니다; 색상 끝점만으로는 해당 속성을 알 수 없습니다. 저장된 효과는 2초에 걸쳐 주황색으로 전환하는 것을 설명합니다.

import com.aspose.slides.*;
import android.graphics.Color;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);
    shape.getFillFormat().setFillType(FillType.Solid);
    shape.getFillFormat().getSolidFillColor().setColor(Color.BLUE);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.ChangeFillColor, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IColorEffect color = factory.createColorEffect();
    color.getProperties().add(BehaviorProperty.getFillColor().getValue());
    color.getFrom().setColor(Color.BLUE);
    int orange = Color.rgb(255, 165, 0);
    color.getTo().setColor(orange);
    color.getTiming().setDuration(2f);

    effect.getBehaviors().add(color);

    presentation.save("color.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

필터

createFilterEffect 로 와이프 필터를 선택합니다. getType, getSubtype, getReveal 은 각각 필터 종류, 방향, 도형을 나타낼지 숨길지를 지정합니다.

이 예제는 오른쪽 방향 서브타입을 사용하여 2초 동안 도형을 나타내는 와이프를 구성합니다. 필터 설정은 효과 내부 동작에 속하므로 프리셋의 원래 작업을 제거한 뒤에 구성합니다.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Wipe, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IFilterEffect filter = factory.createFilterEffect();
    filter.setType(FilterEffectType.Wipe);
    filter.setSubtype(FilterEffectSubtype.Right);
    filter.setReveal(FilterEffectRevealType.In);
    filter.getTiming().setDuration(2f);

    effect.getBehaviors().add(filter);

    presentation.save("filter.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

속성

createPropertyEffect 로 불투명도를 애니메이션합니다. getFrom, getTo, getBy 은 문자열이며, getValueTypegetCalcMode 로 해석됩니다. 세 값을 모두 무분별하게 설정하기보다는 끝점이나 상대 오프셋을 선택하십시오.

여기서는 대상 속성이 불투명도이며, 문자열은 25% 불투명도에서 완전 불투명도로 변경됨을 나타냅니다. 선형 보간은 해당 값들 사이의 점진적 변화를 의미합니다. 다른 속성에 적용할 경우 해당 속성에 맞는 값 타입과 끝점 값을 선택하십시오.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Fade, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IPropertyEffect property = factory.createPropertyEffect();
    property.getProperties().add(BehaviorProperty.getStyleOpacity().getValue());
    property.setValueType(PropertyValueType.Number);
    property.setCalcMode(PropertyCalcModeType.Linear);
    property.setFrom("0.25");
    property.setTo("1");
    property.getTiming().setDuration(2f);

    effect.getBehaviors().add(property);

    presentation.save("property.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

설정

createSetEffectgetTo 를 통해 가시성을 지정합니다. 설정 동작은 끝점 사이를 보간하지 않습니다.

예제는 가시성 속성을 선택하고 동작이 실행될 때 문자열 visible 을 할당합니다. 최소 예제이므로 사각형은 이미 보이므로 자체적으로 눈에 띄는 변화가 없을 수 있습니다. 이러한 동작은 도형이 숨겨지거나 표시되는 시점을 제어하는 더 큰 효과의 일부로 유용합니다.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.Appear, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    ISetEffect set = factory.createSetEffect();
    set.getProperties().add(BehaviorProperty.getStyleVisibility().getValue());
    set.setTo("visible");

    effect.getBehaviors().add(set);

    presentation.save("set.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

명령

createCommandEffect 를 사용하고 getType, getCommandString, getShapeTarget 를 구성합니다. 작업 디렉터리에 sample.wav 라는 WAV 녹음을 두십시오. 이 예제는 addAudioFrameEmbedded 로 파일을 삽입하고 재생 명령을 오디오 프레임에 연결합니다.

오디오 프레임은 효과의 대상이자 명령의 대상입니다. 이는 재생 요청을 삽입된 녹음에 연결합니다; 명령 문자열만으로는 어떤 미디어 객체를 제어할지 알 수 없습니다. 이 효과는 슬라이드쇼 중 클릭으로 시작하도록 구성됩니다.

import com.aspose.slides.*;
import java.io.FileInputStream;
import java.io.IOException;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    try (FileInputStream audioStream = new FileInputStream("sample.wav")) {
        IAudioFrame audioFrame = slide.getShapes().addAudioFrameEmbedded(100, 100, 40, 40, audioStream);

        IEffect effect = slide.getTimeline().getMainSequence().addEffect(audioFrame, EffectType.MediaPlay, EffectSubtype.None, EffectTriggerType.OnClick);
        effect.getBehaviors().clear();

        IBehaviorFactory factory = new BehaviorFactory();
        ICommandEffect command = factory.createCommandEffect();
        command.setType(CommandEffectType.Call);
        command.setCommandString("play");
        command.setShapeTarget(audioFrame);

        effect.getBehaviors().add(command);

        presentation.save("command.pptx", SaveFormat.Pptx);
    } catch (IOException exception) {
        System.out.println("Unable to read sample.wav: " + exception.getMessage());
    }
} finally {
    presentation.dispose();
}

저장은 명령을 command.pptx 에 저장하지만 녹음을 재생하지는 않습니다. 재생하려면 명령과 미디어 대상을 지원하는 슬라이드쇼 플레이어가 필요합니다.

동작 컬렉션 관리

IBehaviorCollectionadd, insert, remove, removeAt 를 지원합니다. 이 예제는 rotation.pptx 를 열어 크기 조절을 추가하고, 회전 앞에 삽입한 뒤 회전을 제거합니다. 동일 객체를 제거하고 다시 삽입하면 복사본을 만들지 않고 저장된 위치만 변경됩니다.

편집 순서는 컬렉션을 회전‑크기조절 → 크기조절‑회전 → 크기조절만 남도록 변경합니다. 인덱스는 현재 컬렉션을 기준으로 하므로 재정렬 후 회전의 새로운 인덱스를 사용합니다. 최종 열거는 저장될 동작을 확인합니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("rotation.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);
    IBehaviorCollection behaviors = effect.getBehaviors();

    IBehaviorFactory factory = new BehaviorFactory();
    IScaleEffect scale = factory.createScaleEffect();
    scale.setTo(new PointF(125, 125));
    scale.getTiming().setDuration(2f);

    behaviors.add(scale);

    behaviors.remove(scale);
    behaviors.insert(0, scale);
    behaviors.removeAt(1);

    for (IBehavior behavior : behaviors)
        System.out.println(behavior.getClass().getSimpleName());

    presentation.save("collection-edited.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

출력은 ScaleEffect 로, 크기조절만 남습니다. 컬렉션 순서는 자체적으로 동작을 순차적으로 실행하도록 예약하지 않습니다. 모든 작업을 교체할 때만 컬렉션을 비우십시오.

동작 타이밍 구성

IBehavior.getTimingIEffect.getTiming 와 독립적으로 ITiming 을 노출합니다. 효과 타이밍은 외부 효과를 예약하고, 동작 타이밍은 그 내부 작업을 설명합니다.

지속 시간, 지연, 반복, 가속 설정

rotation.pptx 를 열어 지속 시간(getDuration) 과 트리거 지연(getTriggerDelayTime) 을 초 단위로 설정하고, setRepeatCount 로 반복 횟수를 지정합니다. getAccelerategetDecelerate 은 지속 시간의 비율이며, 두 값의 합은 1 이하로 유지하십시오.

입력 파일은 회전 예제에서 만든 파일이며, 첫 번째 동작이 회전임이 알려져 있습니다. 이 예제는 해당 동작의 타이밍만 변경하고, 90도 각도는 그대로 유지합니다. 각도와 타이밍을 분리하면 애니메이션을 재구성하지 않고도 속도를 조정하기가 쉽습니다.

import com.aspose.slides.*;

Presentation presentation = new Presentation("rotation.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);

    IRotationEffect rotation = (IRotationEffect)effect.getBehaviors().get_Item(0);
    rotation.getTiming().setDuration(2f);
    rotation.getTiming().setTriggerDelayTime(0.5f);
    rotation.getTiming().setRepeatCount(3f);
    rotation.getTiming().setAccelerate(0.2f);
    rotation.getTiming().setDecelerate(0.2f);

    presentation.save("timing.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

이 동작은 2초 지속, 0.5초 지연, 반복 횟수 3을 사용합니다. 지속 시간의 처음과 마지막 20%는 가속 및 감속에 사용됩니다.

다른 반복 정책으로는 getRepeatDuration, getRepeatUntilEndSlide, getRepeatUntilNextClick 가 있으며, 모든 옵션을 동시에 켜는 대신 하나를 선택하십시오. getAutoReverse 은 앞쪽 재생이 끝난 뒤 역방향으로 애니메이션을 재생합니다. 가속·감속은 연속적인 변화에만 적용되며, 이산 할당이나 명령에는 적용되지 않습니다.

모션 경로 만들기

createMotionEffect 로 모션을 생성합니다. getFrom, getTo, getBy 은 백분율 기반 좌표 또는 오프셋을 설명합니다. 편집 가능한 경로를 만들려면 MotionPath 를 생성하고 IMotionEffect.setPath 로 할당합니다. IMotionPath 은 경로 명령을 저장합니다.

MotionCommandPathType 은 작업을 선택합니다:

Command Points Meaning
MoveTo One 시작 위치를 설정합니다.
LineTo One 직선 구간을 따라 끝점까지 이동합니다.
CurveTo Three 두 제어점과 끝점으로 정의된 3차 곡선을 따릅니다.
CloseLoop None 시작 위치로 되돌아갑니다.
End None 경로를 종료합니다.

MotionPathPointsType 은 코너 포인트 또는 부드러운 포인트와 같은 점 편집 특성을 설명합니다. 이는 명령 유형을 대체하지 않으며, 아래 곡선 예제에서는 곡선 포인트 유형을, 직선 구간에서는 코너 포인트 유형을 사용하십시오.

경로 좌표는 슬라이드 크기에 정규화됩니다. X 변위 0.25는 슬라이드 너비의 1/4을 의미하고, 0.25 포인트가 아닙니다. Y 좌표는 아래쪽이 양수입니다. 절대 명령은 경로 좌표계의 위치를 지정하고, 상대 명령은 현재 위치에서의 오프셋을 지정합니다. 이는 getOrigin 이 선택하는 경로 기준 프레임 및 getPathEditMode 이 도형 이동 시 경로가 어떻게 움직이는지를 제어하는 것과 별개입니다.

직선 경로 만들기

시작점, 한 개의 직선 구간, 종료 명령으로 구성된 모션 동작을 생성합니다. IMotionPath.add 은 명령 유형, 해당 점들, 점 유형, 상대 좌표 플래그를 받습니다.

시작 명령은 (0, 0) 을 설정하고, 선 명령은 (0.25, 0) 으로 끝나 슬라이드 너비의 1/4 만큼 수평 이동합니다. 종료 명령은 좌표 점이 없습니다. 경로를 할당한 뒤 효과에 모션 동작을 추가하면 해당 경로가 사각형에 연결됩니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 160, 80);

    IEffect effect = slide.getTimeline().getMainSequence().addEffect(shape, EffectType.PathRight, EffectSubtype.None, EffectTriggerType.OnClick);
    effect.getBehaviors().clear();

    IBehaviorFactory factory = new BehaviorFactory();
    IMotionEffect motion = factory.createMotionEffect();
    motion.setOrigin(MotionOriginType.Layout);
    motion.getTiming().setDuration(2f);

    IMotionPath path = new MotionPath();
    path.add(MotionCommandPathType.MoveTo, new PointF[] { new PointF(0, 0) }, MotionPathPointsType.Auto, false);
    path.add(MotionCommandPathType.LineTo, new PointF[] { new PointF(0.25f, 0) }, MotionPathPointsType.Corner, false);
    path.add(MotionCommandPathType.End, new PointF[0], MotionPathPointsType.None, false);

    motion.setPath(path);
    effect.getBehaviors().add(motion);

    presentation.save("motion.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

motion.pptx에는 세 개의 경로 명령을 가진 하나의 모션 동작이 들어 있습니다. 아래 파일 편집 예제는 이 구조를 전제로 합니다.

절대 좌표와 상대 좌표 비교

두 경로 객체는 동일한 경로를 설명합니다. 절대 명령은 (0.3, 0.1) 에 끝나고, 상대 명령은 현재 위치에 (0.1, 0.1) 을 추가하여 (0.2, 0) 로 이동합니다.

두 경로 모두 동일한 시작 위치에서 시작합니다. 상대 선의 경우 현재 위치에 X·Y 오프셋을 더해 끝점을 구하고, 절대 선은 끝점을 그대로 읽습니다. 좌표 변환 없이 플래그만 전환하면 다른 경로가 됩니다.

import com.aspose.slides.*;
import android.graphics.PointF;

MotionPath absolutePath = new MotionPath();
absolutePath.add(MotionCommandPathType.MoveTo, new PointF[] { new PointF(0.2f, 0) }, MotionPathPointsType.Auto, false);
absolutePath.add(MotionCommandPathType.LineTo, new PointF[] { new PointF(0.3f, 0.1f) }, MotionPathPointsType.Corner, false);

MotionPath relativePath = new MotionPath();
relativePath.add(MotionCommandPathType.MoveTo, new PointF[] { new PointF(0.2f, 0) }, MotionPathPointsType.Auto, false);
relativePath.add(MotionCommandPathType.LineTo, new PointF[] { new PointF(0.1f, 0.1f) }, MotionPathPointsType.Corner, true);

어느 경로든 모션 동작에 할당하면 프레젠테이션에서 사용할 수 있습니다. 마지막 Boolean 인자는 해당 명령에 상대 좌표를 사용할지 여부를 선택합니다.

선을 곡선으로 교체

motion.pptx 를 열어 선 명령을 3차 곡선으로 교체합니다. 먼저 두 제어점을 제공하고, 이어서 끝점을 지정합니다.

시작 위치는 앞선 명령이 제공합니다. 첫 두 점이 곡선을 형성하고, 세 번째 점이 목적지입니다; 이는 연속적인 목적지가 아니라 하나의 곡선 정의입니다. 명령 유형, 점 편집 유형, 점 배열을 함께 업데이트하면 새 기하학에 맞게 구간이 일관됩니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("motion.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);
    IMotionEffect motion = (IMotionEffect)effect.getBehaviors().get_Item(0);

    IMotionPath path = motion.getPath();
    path.get_Item(1).setCommandType(MotionCommandPathType.CurveTo);
    path.get_Item(1).setPointsType(MotionPathPointsType.CurveSmooth);
    path.get_Item(1).setPoints(new PointF[] { new PointF(0.1f, 0), new PointF(0.2f, 0.1f), new PointF(0.3f, 0.1f) });

    presentation.save("curve.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

curve.pptx의 경로는 여전히 세 개의 명령을 가지고 있으며, 가운데 명령이 이제 곡선을 정의합니다.

저장된 경로 검사 및 편집

IMotionCmdPathgetPoints, getCommandType, getPointsType, isRelative 를 노출합니다. 아래 예제는 motion.pptx 의 알려진 세 명령 경로를 사용합니다. 임의 입력의 경우, 편집하기 전에 대상 효과를 찾고 명령 유형과 점 개수를 확인하십시오.

명령 및 좌표 읽기

경로를 변경 없이 읽습니다. 종료 및 닫기 루프 명령은 점이 필요 없으므로 null 점 배열을 허용합니다.

출력은 각 숫자 명령 유형과 상대 좌표 플래그를 쌍으로 표시한 뒤 점들을 나열합니다. 이를 통해 경로를 수정하기 전에 끝점과 오프셋을 구분할 수 있습니다. 곡선은 세 점을, 여기 파일의 직선은 한 점만 나열합니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("motion.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);
    IMotionEffect motion = (IMotionEffect)effect.getBehaviors().get_Item(0);

    IMotionPath path = motion.getPath();
    for (IMotionCmdPath segment : path)
    {
        System.out.println(segment.getCommandType() + ", relative: " + segment.isRelative());
        if (segment.getPoints() != null)
            for (PointF point : segment.getPoints())
                System.out.println("X=" + point.x + ", Y=" + point.y);
    }
} finally {
    presentation.dispose();
}

목록에는 시작점, (0.25, 0) 에 끝나는 절대 선, 그리고 종료 명령이 포함됩니다.

끝점 변경

motion.pptx 를 열고 선의 점 배열을 교체하여 끝점을 이동합니다.

입력 파일에서 인덱스 0은 시작 명령, 인덱스 1은 선입니다. 선의 단일 점을 교체하면 명령 유형, 타이밍, 컬렉션 내 위치는 바뀌지 않고 목적지만 변경됩니다. 명령이 절대 좌표를 사용하므로 새 쌍은 오프셋이 아니라 위치를 지정합니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("motion.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);

    IMotionEffect motion = (IMotionEffect)effect.getBehaviors().get_Item(0);
    motion.getPath().get_Item(1).setPoints(new PointF[] { new PointF(0.4f, 0.1f) });

    presentation.save("motion-endpoint.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

motion-endpoint.pptx 의 선은 (0.4, 0.1) 에 끝나며, 원본 파일은 그대로 유지됩니다.

구간 교체

insertremoveAt 를 사용해 motion.pptx 의 선을 교체합니다. 삽입 시 기존 선이 인덱스 2 로 이동합니다.

이는 기존 좌표를 편집하는 대신 명령 객체 자체를 교체하는 방법을 보여줍니다. 삽입 후 컬렉션은 일시적으로 시작 명령, 새 선, 기존 선, 종료 명령을 포함합니다. 인덱스 2 를 제거하면 기존 선이 사라지고 새 경로가 남습니다.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("motion.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);
    IMotionEffect motion = (IMotionEffect)effect.getBehaviors().get_Item(0);

    IMotionPath path = motion.getPath();
    path.insert(1, MotionCommandPathType.LineTo, new PointF[] { new PointF(0.2f, 0.1f) }, MotionPathPointsType.Corner, false);
    path.removeAt(2);

    presentation.save("motion-edited.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

저장된 경로는 여전히 세 개의 명령을 가지고 있으며, 새 선은 (0.2, 0.1) 에 끝나고 종료 명령이 마지막에 위치합니다.

기존 동작 수정 및 확인

동작 인덱스를 모를 경우 타입으로 선택합니다. 이 예제는 rotation.pptx 를 열어 IRotationEffect 를 찾고 각도를 변경한 뒤 다시 열어 저장된 값을 확인합니다.

타입 검사를 통해 회전이 아닌 동작을 건너뛸 수 있습니다. 두 번째 로드에서는 저장된 파일을 별도 프레젠테이션 객체에 읽어 들여 메모리에 남아 있는 값이 아니라 지속된 데이터를 비교합니다. 이 예제는 알려진 효과가 기본 시퀀스의 첫 번째에 있다고 가정합니다; 타입으로 동작을 선택한다고 해서 임의 프레젠테이션에서 올바른 효과를 찾는 것은 아닙니다.

import com.aspose.slides.*;

Presentation presentation = new Presentation("rotation.pptx");
try {
    IEffect effect = presentation.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);

    for (IBehavior behavior : effect.getBehaviors())
    {
        if (behavior instanceof IRotationEffect) {
            IRotationEffect rotation = (IRotationEffect) behavior;
            rotation.setBy(180f);
        }
    }

    presentation.save("rotation-edited.pptx", SaveFormat.Pptx);

    Presentation reopened = new Presentation("rotation-edited.pptx");
    try {
        IEffect savedEffect = reopened.getSlides().get_Item(0).getTimeline().getMainSequence().get_Item(0);

        for (IBehavior behavior : savedEffect.getBehaviors())
        {
            if (behavior instanceof IRotationEffect) {
                IRotationEffect rotation = (IRotationEffect) behavior;
                System.out.println("Rotation preserved: " + (Math.abs(rotation.getBy() - 180f) < 0.001f));
            }
        }
    } finally {
        reopened.dispose();
    }
} finally {
    presentation.dispose();
}

출력은 Rotation preserved: true 입니다. 동일한 타입 검사 패턴을 다른 동작에도 적용하십시오. 전체 보존 검사를 위해서는 대상 도형, 효과, 동작 타입 및 순서, 타이밍, 경로 명령을 비교하고 부동소수점 값은 수치 허용오차를 사용하십시오. 애니메이션 레이아웃을 알 수 없는 프레젠테이션에 대해서는 도형 애니메이션 읽기 를 참고해 기본 및 인터랙티브 시퀀스를 순회하십시오.

동작 순서, 프리셋 및 재생

IBehaviorCollection 의 순서는 효과 작업의 저장 순서이며, 각 동작이 자동으로 앞의 동작을 기다리는 재생 목록이 아닙니다. 타이밍과 외부 효과가 스케줄을 결정합니다. 동작은 겹칠 수 있으며, 같은 속성에 대한 작업은 getAdditivegetAccumulate 로 상호 작용할 수 있습니다. 컬렉션 순서만으로 “이동 후 회전”을 예약하지 말고, 도형 애니메이션 에 설명된 대로 명시적인 타이밍이나 별도 효과를 사용하십시오.

효과의 getTypegetSubtype 은 프리셋을 설명합니다. 이는 편집된 동작 트리의 전체 설명이 아니므로, 동작을 사용자 정의하기 전에 프리셋과 서브타입을 선택하십시오. 프리셋을 변경하면 컬렉션이 재구성되어 사용자 정의 작업이 사라질 수 있습니다. 예를 들어, 사용자 정의 Spin 효과를 Fade 로 변경하면 회전 동작이 설정·필터 동작으로 교체됩니다. 프리셋이나 서브타입을 변경한 뒤 컬렉션을 다시 검사하십시오. 프리셋 동작을 비우면 프리셋이 필요로 하는 가시성·초기화 작업도 제거될 수 있습니다. 예제에서는 가시적인 도형을 사용하고 동작을 교체했으며, 모든 프리셋 구현을 재구성하지는 않습니다.

포맷 호환성

보존된 동작 트리가 모든 뷰어나 출력 렌더러에서 동일한 재생을 보장하지는 않습니다. 저장된 데이터와 렌더링 결과를 별도로 확인하십시오.

Format or output What to verify
PPTX 이 예제의 기본 포맷으로 사용합니다. 파일을 다시 열어 수정 가능한 동작 트리를 확인한 뒤, 원하는 PowerPoint 버전에서 재생을 체크하십시오.
PPT 레거시 바이너리 형식은 PPTX와 다를 수 있습니다. 별도 저장‑재열 사이클과 재생을 테스트하십시오; PPTX 출력이 성공했다고 해서 모든 사용자 정의 조합이 지원된다고 추론하지 마십시오.
PDF, PNG, JPEG 및 기타 정적 슬라이드 이미지 정적 슬라이드 표현이며, 재생 가능한 동작 타임라인이나 최종 애니메이션 프레임을 보장하지 않습니다.
HTML5 도형 애니메이션이 내보내기 옵션에서 활성화된 경우 지원되는 애니메이션을 재생할 수 있습니다. 브라우저에서 사용자 정의 조합을 테스트하십시오.
Animated GIF 렌더링된 프레임을 저장하며, 편집 가능한 동작이나 클릭 트리거 인터랙션은 포함하지 않습니다. 실제 렌더링된 모션을 확인하십시오.
Video 애니메이션 프레임을 렌더링하고 비디오로 인코딩합니다. 지원은 렌더러의 지원 애니메이션 및 효과 에 제한되며, 명령 및 인터랙티브 이벤트는 편집 가능한 타임라인이 되지 않습니다.

FAQ

내 효과에 동작이 없는데도 동작이 포함되어 있는 이유는?

미리 정의된 효과를 생성하면 기본 작업이 자동으로 생성될 수 있습니다. 프리셋을 확장할지 교체할지 결정하기 전에 이를 확인하십시오.

동작을 처음으로 이동하면 먼저 재생되나요?

반드시 그렇지는 않습니다. 컬렉션 순서는 타이밍을 대신하지 못합니다. 지연, 지속 시간 및 같은 속성에 대한 작업 간 상호 작용을 확인하십시오.

끝점 명령에 점이 없는 이유는?

끝점 명령은 경로의 종료를 표시하며 좌표가 필요하지 않습니다. 파일에서 경로를 읽을 때 null 점 배열을 검사하십시오.

라운드 트립이 성공했다고 해서 재생이 보장되나요?

아니요. 재열은 확인한 속성 보존만 입증합니다. 슬라이드쇼 플레이어나 애니메이션 내보내기를 별도로 테스트하여 시각적 동작을 확인하십시오.