30. Stack: Animation
The frames inside the stack resize. Note that the distribution
of the stack is adjusted so that one frame can push the other frame to the left or right side, respectively.
Code component
export function CC30StackAnimation() {
const [leftExpanded, setLeftExpanded] = useState(false)
const [rightExpanded, setRightExpanded] = useState(false)
return (
<Stack
// Visual & layout
size={150}
radius={30}
center
// Stack
direction="horizontal"
overflow="hidden"
// Animation
distribution={rightExpanded ? "end" : "start"}
>
<Frame
// Visual & layout
width={leftExpanded ? 150 : 70}
height={150}
radius={10}
backgroundColor="#fff"
// Animation
layout
onTap={() => {
setLeftExpanded(!leftExpanded)
}}
/>
<Frame
// Visual & layout
width={rightExpanded ? 150 : 70}
height={150}
radius={10}
backgroundColor="#fff"
// Animation
layout
onTap={() => {
setRightExpanded(!rightExpanded)
}}
/>
</Stack>
)
}
Framer Motion
The Framer Motion library doesn’t have stacks, so here I used a regular Flexbox.
export function FM30FlexboxAnimation() {
const [leftExpanded, setLeftExpanded] = useState(false)
const [rightExpanded, setRightExpanded] = useState(false)
return (
<Center>
<div
style={{
width: 150,
height: 150,
borderRadius: 30,
overflow: "hidden",
display: "flex",
justifyContent: "space-between",
}}
>
<motion.div
style={{
width: leftExpanded ? 150 : rightExpanded ? 1 : 70,
height: 150,
borderRadius: 10,
backgroundColor: "#fff",
cursor: "pointer",
}}
layout
onTap={() => {
setLeftExpanded(!leftExpanded)
}}
/>
<motion.div
style={{
width: rightExpanded ? 150 : leftExpanded ? 1 : 70,
height: 150,
borderRadius: 10,
backgroundColor: "#fff",
cursor: "pointer",
}}
layout
onTap={() => {
setRightExpanded(!rightExpanded)
}}
/>
</div>
</Center>
)
}
We can’t swap the flexDirection
here (from row
to row-reverse
) because that also changes the order, so the only option is to animate the width of both divs simultaneously.
Override
You can’t use layout animations in overrides, so here it’s solved old skool, by animating the width
with animate
.
const appState = Data({
stackDistribution: "start",
})
export function Stack(): Override {
return { distribution: appState.stackDistribution }
}
export function Left_frame(): Override {
const [width, cycleWidth] = useCycle(70, 150)
return {
animate: { width: width },
onTap() {
cycleWidth()
appState.stackDistribution = "start"
},
}
}
export function Right_frame(): Override {
const [width, cycleWidth] = useCycle(70, 150)
return {
animate: { width: width },
onTap() {
cycleWidth()
appState.stackDistribution = "end"
},
}
}
4 comments on “30. Stack: Animation”
Leave a Reply
You must be logged in to post a comment.
I’ve encountered a bit of “strange” behaviour with stacks so far, and I wonder if you might know the answer. For example, if I just put a couple of frames in a stack on the canvas and apply a hover which animates height, the other frame(s) will move and the gap between the frames stay consistent. However, if I put a couple of code components in that same stack, this does not happen. They all stay in place while the hovered component changes height.
Is this because of the ComponentContainer?
Probably, yes. Because when you add components to a stack in code they do resize.
Try this:
(I added the
whileHover
directly instead of applying it with an override, but the result is the same.)I see, thanks. What would be the best way to solve property controls for the component in your example?
You can’t use the property controls when using a component in code, of course, so you just pass those properties (here:
text
andtint
) in the code.Another option would be to add properties controls to the Stack, and then pass those properties to the components.
(BTW, the component’s properties are now unpacked, just like in Framer’s boilerplate code.)