What you'll learn:
useTransform
with MotionValue
useTransform
input and output rangesopacity
with MotionValue
MotionValue
Now let’s make this swipe gesture a bit more realistic. We’ll rotate the card counter-clockwise when we drag it to the left and rotate it to the other direction when we drag it to the right.
Replacing y={mv}
with rotate={mv}
,
<Frame
center
drag="x"
x={mv}
rotate={mv}
dragConstraints={{ left: -200, right: 200 }}
style={style}
/>
Perhaps you can invent some new interaction by exploring these attributes!
However, don’t get distracted yet. We want to create the tinder swipe first!
As a result, the card shouldn’t rotate this much.
To do that, we need to convert the x offset range to a smaller range of rotation degrees.
transform
We did something similar to that in our Slider. The function transform
was used to do this conversion.
Using transform
, we only need to give it a value, an input range, and an output range.
Unfortunately, the transform
function cannot be used here. If you check out its documentation, the value being transformed must be a number.
Scrolling down,
useTransform
The good news is that the Framer library gives us something similar to transform
for MotionValues. It’s another hook called useTransform
.
On the same Framer documentation page, we can click on useTransform
on the left menu to see its documentation.
Let's take a look at the second example first. We'll go over the first one later.
Although the first parameter is called parent
, we can see that the function takes an input range the gives an output range.
To use useTransform
we first have to import it.
import "./styles.css"
We can then call useTransform
inside our App
component. Since the dragging range of the card is between -200
and 200
, from our dragConstraints
, we have the input range.
function App() {
let mv = useMotionValue(0)
let rotateMv = useTransform(mv, [-200, 200], [-50,50])
...
Then instead of rotate = {mv}
, we replace that with rotate = {rotateMv}
.
<Frame
center
drag="x"
x={mv}
rotate={rotateMv}
dragConstraints={{ left: -200, right: 200 }}
style={style}
/>
That works pretty well.
opacity
Similarly, we can create another MotionValue
for the opacity
of the card. This will make the card fade away when we drag it.
function App() {
let mv = useMotionValue(0)
let rotateMv = useTransform(mv, [-200, 200], [-50,50])
let opacityMv = useTransform(mv, [-200, 200], [1, 0])
...
Then in our <Frame>
,
<Frame
center
drag="x"
x={mv}
rotate={rotateMv}
opacity={opacityMv}
dragConstraints={{ left: -200, right: 200 }}
style={style}
/>
However, this isn’t quite what we wanted. The card is supposed to be opaque when it’s sitting in the middle and disappear when we swipe it to the left or the right.
We can solve this by fixing our input and output ranges.
Since the input and output ranges in useTransform
are arrays, we can put more than just two elements inside. As long as the number of input and output ranges match, Framer will treat them as keyframes.
When the card is sitting in the middle, its x offset is 0, so let's add 0
to our input range.
function App() {
let mv = useMotionValue(0)
let rotateMv = useTransform(mv, [-200, 200], [-50,50])
let opacityMv = useTransform(mv, [-200, 0, 200], [1, 0])
...
When the x offset is 0
we want the opacity to be 1, so let's move the 1
in our output range to the middle. We can do this by adding a 0
to the output array because we also want our card to fade when we drag it to the left.
function App() {
let mv = useMotionValue(0)
let rotateMv = useTransform(mv, [-200, 200], [-50,50])
let opacityMv = useTransform(mv, [-200, 0, 200], [0, 1, 0])
...
If you think the card disappears too soon, we can insert more "keyframes" to fine-tune the animation.
function App() {
let mv = useMotionValue(0)
let rotateMv = useTransform(mv, [-200, 200], [-50,50])
let opacityMv = useTransform(mv, [-200,-150, 0, 150, 200], [0, 1, 1, 1, 0])
...
This is it! We can build this swipe gesture with the Framer library in just a few lines of code. It’s a lot of fun to play with different attributes and watch how the frames would change. I highly encourage you to try a few more things, and really, unleash your creativity!
In the next post, we'll revisit our summer parallax module and learn how to convert the animation to incorporate useMotionValue
.