32. Animate Presence: Stack 3D
This is quite a complicated example. A lot is going on here.
Code component
A few details:
- There are always just two cards whose
key
s count up when the first card is removed (changing the key triggers the Animate Presence animation). - The cards have
scale
androtate
Motion values that are transformed by the card’sx
position (when the card is draggable, only the first card is). - The cards are wrapped in an
<AnimatePresence>
, and the first card will have anexit
animation that moves it to the left or right (starting from the point where you release it). - The animations are passed in variants, of which there are two sets:
variantsFrontCard
andvariantsBackCard
. Theexit
variant of the front card uses acustom
property set on the card: thex
position it should animate to. - That
custom
value is saved in anexitX
state and gets set just before theexit
animation happens in thehandleDragEnd()
handler that is called ononDragEnd()
. - The parent’s
setIndex()
is passed to the first card, and when it is called (in that samehandleDragEnd()
), the cards change position.
This is the Card()
component:
function Card(props) {
const [exitX, setExitX] = useState(0)
const x = useMotionValue(0)
const scale = useTransform(x, [-150, 0, 150], [0.5, 1, 0.5])
const rotate = useTransform(x, [-150, 0, 150], [-45, 0, 45], {
clamp: false,
})
const variantsFrontCard = {
animate: { scale: 1, y: 0, opacity: 1 },
exit: (custom) => ({ x: custom, opacity: 0, scale: 0.5 }),
}
const variantsBackCard = {
initial: { scale: 0, y: 105, opacity: 0 },
animate: { scale: 0.75, y: 30, opacity: 0.5 },
}
function handleDragEnd(_, info) {
if (info.offset.x < -100) {
setExitX(-250)
props.setIndex(props.index + 1)
}
if (info.offset.x > 100) {
setExitX(250)
props.setIndex(props.index + 1)
}
}
return (
<motion.div
style={{
width: 150,
height: 150,
position: "absolute",
top: 0,
x,
rotate,
}}
drag={props.drag}
dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }}
onDragEnd={handleDragEnd}
variants={props.frontCard ? variantsFrontCard : variantsBackCard}
initial="initial"
animate="animate"
exit="exit"
custom={exitX}
transition={
props.frontCard
? { type: "spring", stiffness: 300, damping: 20 }
: { scale: { duration: 0.2 }, opacity: { duration: 0.4 } }
}
>
<motion.div
style={{
width: 150,
height: 150,
backgroundColor: "#fff",
borderRadius: 30,
scale,
}}
/>
</motion.div>
)
}
And this is the main component that contains the two cards.
export default function CC_32_Animate_Presence_Stack_3D(props) {
const [index, setIndex] = useState(0)
return (
<div>
<motion.div
style={{ width: 150, height: 150, position: "relative" }}
>
<AnimatePresence initial={false}>
<Card key={index + 1} frontCard={false} />
<Card
key={index}
frontCard={true}
index={index}
setIndex={setIndex}
drag="x"
/>
</AnimatePresence>
</motion.div>
</div>
)
}
Code override
This might be possible with three cards that get reused continuously (and no <AnimatePresence>
). But why bother? A component version will always be easier to make.
6 comments on “32. Animate Presence: Stack 3D”
Leave a Reply
You must be logged in to post a comment.
After dragging out the top card, is both cards’ key changed from 0, 1 to 1, 2? I think I get the idea of what is going when dragging the top card, but after drag, why is the bottom card animating to the same size as top card? Are you planning doing a break down for this example soon?
I didn’t make this one, and I also had to take a good look to figure out what’s happening .
Yes, the
index
(and therefore the cards’ keys) always counts up.I suppose the bottom card animates to the front because it becomes that card by virtue of its
key
.Let’s say the bottom card at some point has a
key
of6
, and the front one akey
of7
. When theindex
counts up, the bottom card becomes the one with akey
of7
and, therefore, kind of ‘assumes that position’.Hi, I’m trying to add another framer in the default function that the color could effect by the motionvalue when the card swiping (kinda like the Example ’24. Colors: Interpolation’), My code is attached below, but it doesn’t work at all, seems the children’s value can’t be transport into the parent.
It’s hard to debug standalone code, because I can’t make changes and see what happens.
Do you have a link to a project? (You can mail it if you prefer. Link in FAQ)
how would i go about adding more cards
Something like this:
Here’s that project on CodeSandbox, but you should be able to copy/paste that added code to the Framer project.
I added one card, gave it a
key
ofindex + 2
, and also tweaked theanimate
values (smallerscale
andopacity
+ lowery
position). Because there are now three cards you can see through the second one, so I would tweak thebackgroundColor
instead of usingopacity
.