From 1422aefdde9c99198e9c599a92dcfbc9f36e1e77 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Fri, 13 Dec 2024 16:19:55 +0100 Subject: [PATCH] Ensuring easing is repeated correctly --- CHANGELOG.md | 6 ++++ .../sequence/__tests__/index.test.ts | 33 +++++++++++++++++-- .../src/animation/sequence/create.ts | 13 +++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ced1f393b..c3b57c6e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Motion adheres to [Semantic Versioning](http://semver.org/). Undocumented APIs should be considered internal and may change without warning. +## [11.15.0] 2024-12-13 + +### Added + +- Add support for `repeat` in animation sequences. + ## [11.14.4] 2024-12-13 ### Fixed diff --git a/packages/framer-motion/src/animation/sequence/__tests__/index.test.ts b/packages/framer-motion/src/animation/sequence/__tests__/index.test.ts index eb01557e75..03560e45cf 100644 --- a/packages/framer-motion/src/animation/sequence/__tests__/index.test.ts +++ b/packages/framer-motion/src/animation/sequence/__tests__/index.test.ts @@ -618,16 +618,45 @@ describe("createAnimationsFromSequence", () => { test("It correctly repeats keyframes once", () => { const animations = createAnimationsFromSequence( - [[a, { x: [0, 100] }, { duration: 1, repeat: 1 }]], + [[a, { x: [0, 100] }, { duration: 1, repeat: 1, ease: "linear" }]], undefined, undefined, { spring } ) expect(animations.get(a)!.keyframes.x).toEqual([0, 100, 0, 100]) - const { duration, times } = animations.get(a)!.transition.x + const { duration, times, ease } = animations.get(a)!.transition.x expect(duration).toEqual(2) expect(times).toEqual([0, 0.5, 0.5, 1]) + expect(ease).toEqual(["linear", "linear", "linear", "linear"]) + }) + + test("It correctly repeats easing", () => { + const animations = createAnimationsFromSequence( + [ + [ + a, + { x: [0, 50, 100] }, + { duration: 1, repeat: 1, ease: ["easeIn", "easeOut"] }, + ], + ], + undefined, + undefined, + { spring } + ) + + expect(animations.get(a)!.keyframes.x).toEqual([0, 50, 100, 0, 50, 100]) + const { duration, times, ease } = animations.get(a)!.transition.x + expect(duration).toEqual(2) + expect(times).toEqual([0, 0.25, 0.5, 0.5, 0.75, 1]) + expect(ease).toEqual([ + "easeIn", + "easeOut", + "linear", + "easeIn", + "easeOut", + "easeIn", + ]) }) test("Repeating a segment correctly places the next segment at the end", () => { diff --git a/packages/framer-motion/src/animation/sequence/create.ts b/packages/framer-motion/src/animation/sequence/create.ts index 2bdcd7a9a8..d672174262 100644 --- a/packages/framer-motion/src/animation/sequence/create.ts +++ b/packages/framer-motion/src/animation/sequence/create.ts @@ -29,6 +29,7 @@ import { compareByTime } from "./utils/sort" import { invariant } from "motion-utils" import { normalizeTimes } from "./utils/normalize-times" import { calculateRepeatDuration } from "./utils/calc-repeat-duration" +import { getEasingForSegment } from "../../easing/utils/get-easing-for-segment" const defaultSegmentEasing = "easeInOut" @@ -203,18 +204,28 @@ export function createAnimationsFromSequence( const originalKeyframes = [...valueKeyframesAsList] const originalTimes = [...times] + ease = Array.isArray(ease) ? [...ease] : [ease] + const originalEase = [...ease] for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) { valueKeyframesAsList.push(...originalKeyframes) for ( let keyframeIndex = 0; - keyframeIndex < originalTimes.length; + keyframeIndex < originalKeyframes.length; keyframeIndex++ ) { times.push( originalTimes[keyframeIndex] + (repeatIndex + 1) ) + ease.push( + keyframeIndex === 0 + ? "linear" + : getEasingForSegment( + originalEase, + keyframeIndex - 1 + ) + ) } }