Framer motion slows down at keyframes - reactjs

I am creating a react application and using framer motion for animation, I am trying to circulate a box around a central axis so it the spinning motion may look 3d. I am defining key frames so for example if I want to move the box on axis I use
{ x: [0, -100, 0, 100, 0] }
The problem is that circular motion needs to be smooth and consistent, but framer motion slows down animation at every keyframe, which doesn't create the effect I want.
So is there any way I can disable this slow down ?

In the animate prop add a transition: {ease: "linear"}
It will look like this
<motion.div
animate={{
x: [0, -100, 0, 100, 0],
transition: {
ease: "linear",
}
}}
></motion.div>

Related

AnimatePresence not working with clipPath inset

I'm trying to make an animation for when the user closes a modal, but for some reason it doesn't work when the modal is closed. It should close the modal from bottom to top, but the animation doesn't run. Can anyone shed some light on this?
here is the codesandbox
In order for AnimatePresence to work properly, you need to assign a unique key attribute / prop to AnimatePresence's direct child element. This is because AnimatePresence uses that key to keep track of when the element is removed from React tree.
See an example here.
After a few hours testing animation settings using polygon instead of inset, I got exactly the result I wanted. Below is the configuration I used for the animation:
export const variants = {
initial: {
clipPath: 'polygon(0 0, 100% 0, 100% 0, 0 0)',
transition: { duration: 0.4 },
},
animate: {
clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
transition: { duration: 0.4, staggerChildren: 0.1 },
},
exit: {
clipPath: 'polygon(0 100%, 100% 100%, 100% 100%, 0 100%)',
transition: { duration: 0.4 },
},
};

Framer Motion - Making a div randomly move across the page

I am new to using Framer Motion but I do love the library a lot. Fantastic so far. However, I am stuck right now when trying to move a background div across the whole page.
https://codesandbox.io/s/svg-background-60ht8?file=/src/App.js
This is my codesandbox
I tried everything, read all the docs and I did get one tip to use Motionvalue and calculate the boundaries so the ball doesn't go off-screen. But I can't figure it out yet...
Does anyone know how to make the SVG/ball/div infinitely animate across the whole page? Thanks in advance!
EDIT:
It is moving randomly now, but on local machine the animation breaks and repeats when going outside the screen.
Not entirely sure if this kind of effect was what you were looking for, but I tried to add the random movement plus screen constraints in an example more akin to the DVD bouncing logo. However, I guess the motionValue should be used in all instances, since using state incurs rendering costs as well as a loss of precision.
I was not able to do the same demo using motion values, so that might be something worth exploring further.
I did something similar using MathRandom, with two state variables that represented both x and y positions on the screen, along with a setInterval() so it would change after a couple of seconds. Something like:
const Test = () => {
const { height, width } = getDimensions();
const [Position, setPosition] = useState({ x: 150, y: 150 })
useEffect(() => {
let timer = setInterval(() => {
setPosition({
...Position,
x: width * (1 - Math.random()) - 250,
y: height * (1 - Math.random()) - 250,
}, 1500)
return () => {
clearInterval(timer)
}
}, [Position])
return (
<div className="screen">
<div className='Animate' style={{
top: Position.y,
left: Position.x,
borderRadius: 100,
height: 100,
width: 100,
...
}}
/>
</div>
)
}

Framer Motion different Animations for different properties

I am trying to animate an animation in framer motion using Chakra-ui and Gatsby whereby there is rotation and movement and opacity change.
At the moment the animation works as I intend on the movement on the x axis and rotation using type:spring however the 'bounce effect' also affects the opacity.
I have tried to explicitly define the type:tween for the opacity property, but this has no effect with the opacity of the object still 'bouncing' too. Here is my code:
const Rocketship = ({ top, right, bottom, left, opacity }) => {
const RocketAnim = motion(Box)
const transition = {
default: {
type: 'spring',
damping: 5,
},
opacity: { type: 'tween' },
}
return (
<RocketAnim
layoutId="rocketship"
initial={{ rotate: 25, x: -100, opacity: 0 }}
animate={{ rotate: 45, x: 0, opacity }}
whileHover={{ width: '170px', cursor: 'pointer' }}
transition={transition}
pos="fixed"
width={150}
top={top}
right={right}
bottom={bottom}
left={left}
...
I would be grateful for any advice
Don't have a complete answer because there isn't enough context, but
Your RocketAnim should be defined outside of the component since you don't need it recreated ever render
You don't have to make it a tween, I would keep it as a spring (the default value) and just set bounce to 0
opacity: { bounce: 0}
If you keep it as a tween, I would try adding a duration to trouble shoot the bouncing.

How to make a div shake from side to side with react spring

I have started playing around with react-spring and I am loving the concept but I am struggling to work out how to build the animation that I want.
I would like to make a div move to the right, then back to start, then to the right, but not as far, back to start, to the right, but not as far again and finally back to start. Just like a spring, that is pulled and when released goes boingoingoing back to it's resting place.
I can see from the documentation how to adjust the feel of the spring and how to trigger the animation, but I have never made an animation before so knowing which properties to change and how to make it loop properly are what I am looking for help on.
Edit: I have this animation so far, and it works, but it feels very disjointed.
const shake = useSpring({
from: { "margin-left": 0 },
to: [
{ "margin-left": 30 },
{ "margin-left": 0 },
{ "margin-left": 20 },
{ "margin-left": 0 },
{ "margin-left": 10 },
{ "margin-left": 0 }
],
config: {
mass: 1,
tension: 500,
friction: 10
}
});
Currently it is clearly three movements, can I decrease the delay between the movements so that it looks like one movement?
Is margin left the best CSS property to use?
UPDATE START
I just came across a simple solution for this problem. It I way better than the one I tried to achieve with configuration. It uses interpolation with range. It is using transform now but can easily adopted to margin.
export default function App() {
const { x } = useSpring({
from: { x: 0 },
to: { x: 1 }
});
return (
<div className="App">
<animated.div
style={{
transform: x
.interpolate({
range: [0, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 1],
output: [180, 220, 180, 220, 180, 220, 180, 200]
})
.interpolate(x => `translate3d(${x}px, 0px, 0px)`)
}}
>
Shake
</animated.div>
</div>
);
}
https://codesandbox.io/s/awesome-framework-go5oh?file=/src/App.js:103-609
UPDATE END*
You can achieve the bouncy effect. There are 3 variable controlling the spring based animation:
mass
friction
tension
You can play with the pink square here: https://www.react-spring.io/docs/hooks/api
I recommend low mass and friction and high tension. You can set these variable in every animation type. For example:
useSpring({ ..., config: {mass: 1, tension: 500, friction: 10} });

Problems with parallax header in react native

Trying to write a parallax scroll view in react native. First off, this is what I have so far:
The only problem, as you can see in the GIF above, is that, children in scroll view disappear at the red line, which is the ScrollView's original top border position. I've tried to change the top border position but it doesn't work, continue to read. The height of the parallax header is 170px, after 100px scrolled, the image stops going up, therefore, the sticky header height is 70px
Here is the code for the GIF above:
const parallaxHeaderHeight = 170;
const headerHeight = 70;
const headerDiff = parallaxHeaderHeight - headerHeight; // 100px
class ParallaxScrollView extends Component {
constructor(props) {
super(props);
this.scrollY = new Animated.Value(0); // How many pixels scrolled
}
<View style={{ flex: 1 }}>
<Animated.Image
source={{ uri: '...' }}
style={{
width: ..., height: ...,
transform: [
{
translateY: this.scrollY.interpolate({
inputRange: [-1, 0, headerDiff, headerDiff + 1],
outputRange: [0, 0, -headerDiff, -headerDiff]
})
},
{
scale: this.scrollY.interpolate({
inputRange: [-1, 0, 1],
outputRange: [1.005, 1, 1]
})
}
]
}}
/>
<Animated.ScrollView
scrollEventThrottle={1}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.scrollY } } }],
{ useNativeDriver: true }
)}
>
// Then, render children here
</Animated.ScrollView>
</View>
}
Then, I've tried to transform the top border of scroll view, but this happens:
Look at the first child of the scroll view, 0, it disappears when I've scrolled 100px, but what I want is for it to stay viewable when scrolling the first 100px. I know why this is happening, but I can't find a solution. How should I modify my code?
Answering my own question: This problem can be solved with a 'hacky' solution, but is not recommended, for reasons listed below.
First of all, the solution is - Add an initial padding to the scroll view's children (Looking at the code snippet in the question and adding this part to it):
...
<Animated.Image
...
style={{
...
position: 'absolute', zIndex: 1,
top: 0, left: 0, right: 0,
height: parallaxHeaderHeight // which is 170px in my case
...
}}
...
/>
<Animated.ScrollView
...
contentContainerStyle={{ paddingTop: parallaxHeaderHeight }}
...
>
...
</Animated.ScrollView>
...
This gives me:
The flaw is that, part of the scroll bar is hidden behind the image header due to the fact that the header has position = absolute and zIndex = 1. But if the scroll bar is not important, then never mind, this 'hacky' solution is just fine and doesn't cause any performance issue

Resources