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
Related
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.
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} });
The documentation provided an example for StyleSheet.absoluteFillObject() whose behavior is also same while using with StyleSheet.absoluteFill():
const styles = StyleSheet.create({
wrapper: {
...StyleSheet.absoluteFillObject,
top: 10,
backgroundColor: 'transparent',
},
});
What is the difference between StyleSheet.absoluteFill() and StyleSheet.absoluteFillObject()? A little example will be more appreciated. Thanks !!!
absoluteFill is an easy way to set a view to be full screen and absolute positioned. It’s a short cut for:
{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
}
Use it to extend your other styles like this:
const styles = StyleSheet.create({
container: {
backgroundColor: 'red'
}
});
<View style={[StyleSheet.absoluteFill, styles.container]} />
absoluteFillObject
Say you want to absolute position your view, but bump it down 20px to offset for the status bar (for example).
You can spread StyleSheet.absoluteFillObject into your style and then override one of it’s values.
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
top: 20,
backgroundColor: 'red'
}
});
<View style={styles.container} />
There is no difference between those two. You can see this in StyleSheet.js:
/**
* A very common pattern is to create overlays with position absolute and zero positioning,
* so `absoluteFill` can be used for convenience and to reduce duplication of these repeated
* styles.
*/
absoluteFill: (absoluteFill: any), // TODO: This should be updated after we fix downstream Flow sites.
/**
* Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be
* used to create a customized entry in a `StyleSheet`, e.g.:
*
* const styles = StyleSheet.create({
* wrapper: {
* ...StyleSheet.absoluteFillObject,
* top: 10,
* backgroundColor: 'transparent',
* },
* });
*/
absoluteFillObject: absoluteFill,
I may be late for the party. But there is some difference between absoluteFill and absoluteFillObject in typescript.
Mainly in typescript, the type of:
absoluteFill is RegisteredStyle<StyleSheet.AbsoluteFillStyle>
absoluteFillObject is StyleSheet.AbsoluteFillStyle
const styles = StyleSheet.create({
container: {
// must use "absoluteFillObject" in typescript
...StyleSheet.absoluteFillObject,
}
})
For JavaScript, there is no difference.
As of version 0.62, no difference at all according to the official document
In case you are using EXPO Snack like I do, the absoluteFill preview on web seems buggy at this time. On a real device, it should be fine.
Currently, there is no difference between using absoluteFill vs. absoluteFillObject.
I've tried to print the value of absoluteFill and absoluteFillObject.
They're no difference. They're the same value.
[LOG] absoluteFill: {"bottom": 0, "left": 0, "position": "absolute", "right": 0, "top": 0}
[LOG] absoluteFillObject: {"bottom": 0, "left": 0, "position": "absolute", "right": 0, "top": 0}
Currently (React Native 0.66), the documentation states:
there is no difference between using absoluteFill vs. absoluteFillObject.
I am learning Animated. In the document, I saw translateY and translateX and I learned them. I read the whole document but didn't find any other translation. I was expecting things like translateColor and such stuff.
For instance:
style={{
opacity: this.state.fadeAnim, // Binds directly
transform: [{
translateBackgroundcolor: this.state.fadeAnim.interpolate({
inputRange: [0, 1],
outputRange: ['rgba(255, 0, 0, 1)', 'rgba(0, 255, 0, 1)']
}),
}],
}}
Buy the I already know that I need to do this to get the background changed:
var interpolatedColorAnimation = this._animatedValue.interpolate({
inputRange: [0, 100],
outputRange: ['rgba(255,255,255, 1)', 'rgba(51,156,177, 1)']
});
return (
<View style={styles.container}>
<Animated.View
style={[styles.box, {backgroundColor: interpolatedColorAnimation}]}
/>
</View>
);
But I don't know what should be changed inside that transform and what should be passed as the style property.
Where can I find a list of them? Why aren't they in the react native doc?
Is this what you are looking for?
https://facebook.github.io/react-native/docs/transforms.html
Looks as though animating colour is not supported by transform
so i am receiving text from a web socket connection, and adding it to a Text component. It starts off as grey, and then turns into black after x amount of time ( The app processes the text ). I have the code below
<Text style={styles.confirmedText}>
{this.state.confirmedText}
<Animated.Text style={{ fontFamily: "Helvetica Neue", color: "#9b9b9b" }}>
{this.state.tempText}
</Animated.Text>
</Text>
So this tempText is constantly changing, but i want there to be a fade-in animation when the text goes from an empty string -> some / any text at all. Any ideas how i could do this?
Note: i know my code hasn't attempted to implement this but I haven't been able to find any working samples using Animated.Text to follow.
Thanks in advance,
EDIT: Better yet, if temp had a value of say "some text", and a word was added to it, eg "some text plus", the added word "plus" to be animated in individually would be great. Seems difficult though
First, you'll want to set up an Animated value like this:
this.opacity = new Animated.Value(0)
Then, when you receive the text you'll want to start the animation:
Animated.timing(this.opacity, {
duration: 350, // some number in milliseconds
toValue: 1, // or whatever final opacity you'd like
}).start();
Finally, you'll need to interpolate that value on your Animated.Text component:
style={{
opacity: this.opacity.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
}),
...
}}
Hopefully, that can get you started!
You probably want to look at the componentWillReceiveProps method.
http://devdocs.io/react/react-component#componentwillreceiveprops
You can then add/remove classes to your element (or to a span for individual words), according to the props changes.
You may need to store a ref to your element too so you can apply classes or animate css properties.
See http://devdocs.io/react/refs-and-the-dom
Try this Code for Change Text Animation.
import React, { Component, PropTypes } from 'react';
import {
StyleSheet,
View,
Text,
Dimensions,Animated
} from 'react-native';
export default class YourView extends Component {
constructor(props) {
super(props);
// 1) You'll want to set up an Animated value like:
this.state = {
tempText : "Hello"
};
}
componentWillMount () {
// 2) when you receive the text you'll want to start
setInterval(() => {
this.setState({tempText: "Hello World"})
}, 1000);
};
render() {
return (
<View>
<Animated.Text style={{ fontFamily: "Helvetica Neue", color: "#9b9b9b" }}>
{this.state.tempText}
</Animated.Text>
</View>
);
}
}
Hopefully its work for you.