From 1c776fd521c49b9dbe97125258457f629dbaf203 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 11 Jan 2022 14:06:47 +0100 Subject: [PATCH 1/3] Support crossfade of px and % unit border radius --- src/projection/animation/mix-values.ts | 22 ++++++++++---------- src/projection/styles/scale-border-radius.ts | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/projection/animation/mix-values.ts b/src/projection/animation/mix-values.ts index b8890115ff..03395452ab 100644 --- a/src/projection/animation/mix-values.ts +++ b/src/projection/animation/mix-values.ts @@ -1,10 +1,14 @@ import { circOut, linear, mix, progress as calcProgress } from "popmotion" +import { percent } from "style-value-types" import { ResolvedValues } from "../../render/types" import { EasingFunction } from "../../types" const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"] const numBorders = borders.length +const asNumber = (value: string | number) => + typeof value === "string" ? parseFloat(value) : value + export function mixValues( target: ResolvedValues, follow: ResolvedValues, @@ -47,17 +51,13 @@ export function mixValues( followRadius ||= 0 leadRadius ||= 0 - /** - * Currently we're only crossfading between numerical border radius. - * It would be possible to crossfade between percentages for a little - * extra bundle size. - */ - if ( - typeof followRadius === "number" && - typeof leadRadius === "number" - ) { - const radius = Math.max(mix(followRadius, leadRadius, progress), 0) - target[borderLabel] = radius + target[borderLabel] = Math.max( + mix(asNumber(followRadius), asNumber(leadRadius), progress), + 0 + ) + + if (percent.test(leadRadius) || percent.test(followRadius)) { + target[borderLabel] += "%" } } diff --git a/src/projection/styles/scale-border-radius.ts b/src/projection/styles/scale-border-radius.ts index 2a57d84ff9..4dabee158c 100644 --- a/src/projection/styles/scale-border-radius.ts +++ b/src/projection/styles/scale-border-radius.ts @@ -17,6 +17,7 @@ export function pixelsToPercent(pixels: number, axis: Axis): number { export const correctBorderRadius: ScaleCorrectorDefinition = { correct: (latest, node) => { if (!node.target) return latest + /** * If latest is a string, if it's a percentage we can return immediately as it's * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number. From fa3aca90d7d68cf459efd06ae47c4443dcf11e67 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 11 Jan 2022 14:07:03 +0100 Subject: [PATCH 2/3] Adding tests --- .../animation/__tests__/mix-values.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/projection/animation/__tests__/mix-values.test.ts diff --git a/src/projection/animation/__tests__/mix-values.test.ts b/src/projection/animation/__tests__/mix-values.test.ts new file mode 100644 index 0000000000..b75b5aca67 --- /dev/null +++ b/src/projection/animation/__tests__/mix-values.test.ts @@ -0,0 +1,74 @@ +import { mixValues } from "../mix-values" + +describe("mixValues", () => { + test("mixes borderRadius numbers", () => { + const output = {} + + mixValues( + output, + { borderTopLeftRadius: 10 }, + { borderTopLeftRadius: 20 }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: 15 }) + }) + + test("mixes borderRadius px", () => { + const output = {} + + mixValues( + output, + { borderTopLeftRadius: "10px" }, + { borderTopLeftRadius: "20px" }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: 15 }) + }) + + test("mixes borderRadius percentage", () => { + const output = {} + + mixValues( + output, + { borderTopLeftRadius: "10%" }, + { borderTopLeftRadius: "20%" }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: "15%" }) + }) + + test("mixes borderRadius percentage with 0", () => { + const output = {} + + mixValues( + output, + { borderTopLeftRadius: 0 }, + { borderTopLeftRadius: "20%" }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: "10%" }) + + mixValues( + output, + { borderTopLeftRadius: "20%" }, + { borderTopLeftRadius: 0 }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: "10%" }) + }) +}) From 3b802f95c9cbd978441a7d267949d2fcb2362cb7 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 11 Jan 2022 15:27:50 +0100 Subject: [PATCH 3/3] Setting unmixable radius to lead --- .../animation/__tests__/mix-values.test.ts | 26 +++++++++++++++++++ src/projection/animation/mix-values.ts | 26 ++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/projection/animation/__tests__/mix-values.test.ts b/src/projection/animation/__tests__/mix-values.test.ts index b75b5aca67..7c98a2b7bd 100644 --- a/src/projection/animation/__tests__/mix-values.test.ts +++ b/src/projection/animation/__tests__/mix-values.test.ts @@ -71,4 +71,30 @@ describe("mixValues", () => { expect(output).toEqual({ borderTopLeftRadius: "10%" }) }) + + test("doesn't mix % with px", () => { + const output = {} + + mixValues( + output, + { borderTopLeftRadius: "10px" }, + { borderTopLeftRadius: "20%" }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: "20%" }) + + mixValues( + output, + { borderTopLeftRadius: "20%" }, + { borderTopLeftRadius: "10px" }, + 0.5, + false, + false + ) + + expect(output).toEqual({ borderTopLeftRadius: "10px" }) + }) }) diff --git a/src/projection/animation/mix-values.ts b/src/projection/animation/mix-values.ts index 03395452ab..09f5589ac6 100644 --- a/src/projection/animation/mix-values.ts +++ b/src/projection/animation/mix-values.ts @@ -1,5 +1,5 @@ import { circOut, linear, mix, progress as calcProgress } from "popmotion" -import { percent } from "style-value-types" +import { percent, px } from "style-value-types" import { ResolvedValues } from "../../render/types" import { EasingFunction } from "../../types" @@ -9,6 +9,9 @@ const numBorders = borders.length const asNumber = (value: string | number) => typeof value === "string" ? parseFloat(value) : value +const isPx = (value: string | number) => + typeof value === "number" || px.test(value) + export function mixValues( target: ResolvedValues, follow: ResolvedValues, @@ -51,13 +54,22 @@ export function mixValues( followRadius ||= 0 leadRadius ||= 0 - target[borderLabel] = Math.max( - mix(asNumber(followRadius), asNumber(leadRadius), progress), - 0 - ) + const canMix = + followRadius === 0 || + leadRadius === 0 || + isPx(followRadius) === isPx(leadRadius) + + if (canMix) { + target[borderLabel] = Math.max( + mix(asNumber(followRadius), asNumber(leadRadius), progress), + 0 + ) - if (percent.test(leadRadius) || percent.test(followRadius)) { - target[borderLabel] += "%" + if (percent.test(leadRadius) || percent.test(followRadius)) { + target[borderLabel] += "%" + } + } else { + target[borderLabel] = leadRadius } }