Skip to content

Commit

Permalink
Add layout="size" for size-only layout animations (#1154)
Browse files Browse the repository at this point in the history
  • Loading branch information
bhollis committed Jul 2, 2021
1 parent d253025 commit c57c724
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

Framer Motion adheres to [Semantic Versioning](http://semver.org/).

### Added

- `layout="size"` for size-only layout animations.

## [4.1.17] 2021-05-17

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion api/framer-motion.api.md
Expand Up @@ -432,7 +432,7 @@ export const LayoutGroupContext: import("react").Context<string | null>;

// @public (undocumented)
export interface LayoutProps {
layout?: boolean | "position";
layout?: boolean | "position" | "size";
layoutId?: string;
// @internal
_layoutResetTransform?: boolean;
Expand Down
72 changes: 72 additions & 0 deletions cypress/integration/layout-shared.ts
Expand Up @@ -95,6 +95,42 @@ describe("AnimateSharedLayout: A -> B transition", () => {
})
})
})

it(`It correctly fires layout="size" animations`, () => {
cy.visit("?test=layout-shared-switch-a-b&type=size")
.wait(50)
.get("#a")
.should(([$box]: any) => {
expectBbox($box, {
top: 0,
left: 0,
width: 100,
height: 200,
})
})
.trigger("click")
.wait(50)
.get("#b")
.should(([$box]: any) => {
expectBbox($box, {
top: 50,
left: 100,
width: 300,
height: 300,
})
})
.trigger("click")
.wait(50)
.get("#a")
.should(([$box]: any) => {
expectBbox($box, {
top: 25,
left: 50,
width: 100,
height: 200,
})
})
})
})

describe("AnimateSharedLayout: A -> AB -> A switch transition", () => {
Expand Down Expand Up @@ -345,6 +381,42 @@ describe("AnimateSharedLayout: A -> B crossfade transition", () => {
})
})
})

it(`It correctly fires layout="size" animations`, () => {
cy.visit("?test=layout-shared-switch-a-b&type=size")
.wait(50)
.get("#a")
.should(([$box]: any) => {
expectBbox($box, {
top: 0,
left: 0,
width: 100,
height: 200,
})
})
.trigger("click")
.wait(50)
.get("#b")
.should(([$box]: any) => {
expectBbox($box, {
top: 50,
left: 100,
width: 300,
height: 300,
})
})
.trigger("click")
.wait(50)
.get("#a")
.should(([$box]: any) => {
expectBbox($box, {
top: 25,
left: 50,
width: 100,
height: 200,
})
})
})
})

describe("AnimateSharedLayout: A -> AB -> A crossfade transition", () => {
Expand Down
24 changes: 24 additions & 0 deletions cypress/integration/layout.ts
Expand Up @@ -69,6 +69,30 @@ describe("Layout animation", () => {
})
})

it(`It correctly fires layout="size" animations`, () => {
cy.visit("?test=layout&type=size")
.wait(50)
.get("#box")
.should(([$box]: any) => {
expectBbox($box, {
top: 0,
left: 0,
width: 100,
height: 200,
})
})
.trigger("click")
.wait(50)
.should(([$box]: any) => {
expectBbox($box, {
top: 50,
left: 100,
width: 300,
height: 300,
})
})
})

it("Doesn't initiate a new animation if the viewport box hasn't updated between renders", () => {
cy.visit("?test=layout-interrupt")
.wait(50)
Expand Down
44 changes: 44 additions & 0 deletions dev/examples/Layout-Projection-scale-size.tsx
@@ -0,0 +1,44 @@
import React from "react"
import { useState } from "react"
import { motion } from "@framer"

const textA = `
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
`

const textB = `
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
`

export function App() {
const [c, setC] = useState(1)

return (
<div
id="parent"
onClick={() => setC((i) => -1 * i)}
style={{
backgroundColor: "#fff",
padding: 40,
overflow: "hidden",
maxWidth: 500,
position: "absolute",
top: c * 10 + 100,
left: c * 10 + 100,
}}
>
<motion.div
layout="size"
id="child"
transition={transition}
style={{ backgroundColor: "#ccc" }}
>
{c === 1 ? textA : textB}
</motion.div>
</div>
)
}

const transition = {
duration: 3,
}
16 changes: 12 additions & 4 deletions src/motion/features/layout/Animate.tsx
Expand Up @@ -170,13 +170,21 @@ class Animate extends React.Component<AnimateProps> {
const boxHasMoved = hasMoved(origin, target)

const animations = eachAxis((axis) => {
/**
* If layout is set to "position", we can resize the origin box based on the target
* box and only animate its position.
*/
if (layout === "position") {
/**
* If layout is set to "position", we can resize the origin box based on the target
* box and only animate its position.
*/
const targetLength = target[axis].max - target[axis].min
origin[axis].max = origin[axis].min + targetLength
} else if (layout === "size") {
/**
* If layout is set to "size", we move the origin to the target box and only animate
* its length.
*/
const originLength = origin[axis].max - origin[axis].min
origin[axis].min = target[axis].min
origin[axis].max = origin[axis].min + originLength
}

if (visualElement.projection.isTargetLocked) {
Expand Down
5 changes: 3 additions & 2 deletions src/motion/features/layout/types.ts
Expand Up @@ -20,11 +20,12 @@ export interface LayoutProps {
* animated on this component. Otherwise, set them directly via the `initial` prop.
*
* If `layout` is set to `"position"`, the size of the component will change instantly and
* only its position will animate.
* only its position will animate. If `layout` is set to `"size"`, the position of the
* component will change instantly but its size will animate.
*
* @public
*/
layout?: boolean | "position"
layout?: boolean | "position" | "size"

/**
* Enable shared layout transitions between components for children of `AnimateSharedLayout`.
Expand Down

0 comments on commit c57c724

Please sign in to comment.