In React Native - How to move View element in parallely while ScrollView scrolls - animation

I'm developing a TimeLine component. There are Views list inside horizontal ScrollView that represents half an hour blocks. And I have a Component called TimeRangeSelector, which I use to select a range of time in TimeLine ScrollView. So while I scroll the ScrollView I need to move the TimeRangeSelector in parallel without any lag. Below is the TimeLine component. You can see the 30mins blocks are filled inside ScrollView. The yellow color one is the TimeRangeSelector which is a Animated.View. And the left position of the TimeRangeSelector is set using the scroll position. Each time ScrollView moves im setting the state as below.
<ScrollView
horizontal={true}
onScroll={this.onScrollFunc}
>
{timelineElements}
</ScrollView>
onScrollFunc = (event, gesture) => {
this.setState({
contentOffsetX: event.nativeEvent.contentOffset.x,
scrollStopped: false
});
};
And Im passing the scrollBarPosition as props to the TimeRangeSelector and setting its left position style as shown in below code
<TimeRangeSelector
scrollBarPosition={this.state.contentOffsetX}
/>
let styles= [
{
position: "absolute",
left: this.props.scrollBarPosition,
backgroundColor: backgroundColor,
marginTop: 20,
marginLeft: 1,
width: this.state.selectionWidth,
height: 100
}
];
But the issue is when I scroll the ScrollView the TimeRangeSelector moves with it, but it has a delay.. when I scroll faster the distance between where it should be and where it is, is becoming high. Can anyone give me some ideas according to your knowledge.
My assumption: According to my understanding, I think the lag is because of it takes several instances to reach the setState in and set the ScrollBarPosition as below steps.
ScrollView moved 1 frame.
ScrollView fires the onScroll function and emmits the event with new x point.
Then in the onScroll function it sets the State.
As I understand it takes some time to happen all these steps from JavaScript thread all the way to Native Thread and come back to JS thread again.

You should use something like const onScroll = Animated.event([{ nativeEvent: { contentOffset: { x: animatedValue } } }], { useNativeDriver: true }); with const animatedValue = new Animated.Value(0). This way the animation is only done on the native level without going back and forth through the JS thread.
This animated value can only be used effectively with opacity and transform style properties, but it should help you with your problem.
You can read more about this, in this awesome article.

Related

Nativescript - how to get layout to open on a tap

I've got a StackLayout where one of the entries is a GridLayout of a fixed size. Normally this GridLayout is not visible.
On tapping a button, I'd like the GridLayout be made visible - but I'd like to animate it open - - like a menu open.
Any ideas?
Actually toggling visibility is not too bad - it seems to animate the open - any way to control the speed?
The close operation is maybe too fast for what I'm trying to achieve.
You could animate the opacity of your grid. so when you click on it you would
// View is your gridView, this would hide it completely
view.opacity = 0;
// when you want to show it.
// fade in view.
view.animate({
opacity: 1,
duration: 250
}).then(() => {
//Set the visibility to collapsed after the animation is complete
//I believe you will want to do this so that the surrounding views adjust accordingly.
view.visibility='collapse';
}, (err) => {});
// when you want to hide it.
// fade out.
view.animate({
opacity: 0,
duration: 250
}).then(() => {
view.visibility='visible';
}, (err) => {});
You also may want to look into translate for you animations so you can move view down, left, up, right any way you want.

React Native: Creating an Animated View that slides directly with finger drag

Desired Effect:
I want to create a basic animation of a smooth sliding View (left or right) in sync with the drag pace of my finger.(e.g. sliding an off- canvas menu into and out of view)
Currently I'm using Animated.spring in a parent component that handles all gestures. Then I'm using transform,translateX in a child component to slide a View left or right.
For Example:
Root.js(Parent Component that handles gestures)
_handlePanResponderMove(e: Object, gestureState: Object) {
let dx = gestureState.moveX - this.state.swipeStart;
Animated.spring(
this.state.slideValue,
{
toValue: newValue,
velocity: gestureState.vx,
tension: 2,
friction: 8,
}).start();
}
Navigation.js(Child Component that slides)
render(){
<Animated.View style={[styles.navigation,{transform: [{translateX: this.props.slideValue}]}]}>
<View >
</View>
</Animated.View>
}
The Problem:
There is sticky/lagging behavior with animation instead of a smooth movement that paces the finger sliding guesture.
My reasoning so far:
From my limited Animation experience - Animated.spring, Animated.ease and Animated.timing don't really describe well the equally paced sliding movement I'm after - but I suppose I need to be using one of them to get optimized native performance
(otherwise I'd just use .setState(slideValue) and do some math with the current location of my finger to figure the position of the View.)
Question:
What would be the preferred way to describe this type of smooth sliding animation using the optimized React-Native Animated library?
What I've tried out:
1) Using Animated.timing and setting duration low and easing to linear(my best guess at what I should do)
Animated.timing(
this.state.navSlideValue,
{
toValue: newValue,
duration: 10,
easing: Easing.linear
}).start();
2) Moving up the tension on Animated.spring
Animated.spring(
this.state.navSlideValue,
{
toValue: newValue,
velocity: (gestureState.vx),
tension: 300,
friction: 30,
}).start();
The preset functions timing, spring are useful for running animations to a specified value with a given easing. If you want to tie an Animated value to an event, you can use Animated.event instead:
PanResponder.create({
// on each move event, set slideValue to gestureState.dx -- the
// first value `null` ignores the first `event` argument
onPanResponderMove: Animated.event([
null, {dx: this.state.slideValue}
])
// ...rest of your panResponder handlers
});

Replace one component with another using animation

I'm looking to animate a text field into view and a button out of view at the same time, so that it looks like the text field is replacing the button. (They are both equal size and take up the same area of the screen).
What's the best way to do this using React Native animation?
At this point, I am rendering the button if one of my state values is false, and the text field if it is true.
You can animate any style property in react-native using the Animated API.
If you are able to represent the changes in a sequence of style changes, the Animated API can do it. For instance animating the opacity from 1 to 0 and back to 1 will give a nice fade in fade out effect. The docs explain the Animations much more clearly
Also you can you selective rendering to mount or hide the component
<View style={{/*style props that need to be animated*/}}
{ boolShowText? <Text/> : <View/> }
</View>
The fading example as found in react-native docs
class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), // init opacity 0
};
}
componentDidMount() {
Animated.timing( // Uses easing functions
this.state.fadeAnim, // The value to drive
{toValue: 1}, // Configuration
).start(); // Don't forget start!
}
render() {
return (
<Animated.View // Special animatable View
style={{opacity: this.state.fadeAnim}}> // Binds
{this.props.children}
</Animated.View>
);
}
}

how to execute a function after responsive slides initialization but before first transition

I have a div with images scrolling throw responsive slides and next to this div I have navigation buttons to change pages.
I need that buttons to be centered vertically with the images and I have a function to do it:
function CenterArrow()
{
var posV=($("#slider1").height() - $("#navegacao").height())/2;
$("#navegacao").addClass(" visible-lg visible-md");
$("#navegacao").css('top',posV+'px');
}
$(function()
{
$("#slider1").responsiveSlides({
maxwidth: 400,
speed: 800,
timeout: 4000,
after: function(){
CenterArrow();
}
});
$("#slider1").show();
});
In the responsive slides there is an adjust of some image's height, so I need to call CenterArrow after resposive slides.
I tried to put it in the after callback, as you can see in the code above, but in the first slide the navigation buttons don't show.
I've also tried in the before callbakc, and although it shows a few seconds earlier it still doesn't show in the first slide.
Is there a ready callback, or something similar?
Thanks

Appcelerator TableViewRow swipe

Does anyone know of a hack to allow for a left->right swipe on a tableviewrow. The default swipe action opens a delete button however I require additional buttons but want to maintain the same UX but the "swipe" event listener doesn't seem to fire on rows.
myTblRow.addEventListener('swipe', function(e){
Titanium.API.info("huzzah, a row was swiped");
});
The above == no dice.
It does require a bit of a hack.. remove the editable property on the tableView declaration.
The hack is to apply a view that covers the tableRow:
var row1 = Titanium.UI.createView({
width: Titanium.Platform.displayCaps.platformWidth,
height: 145,
zIndex: 100,
opacity: 0.1
});
row.add(row1);
Notice the zIndex, the opacity makes it exist but be totally transparent.
You now need to create a 'swipe' event listener:
tableView.addEventListener('swipe', function(e){
tableView.updateRow(e.index, createUpdateRow(e.source.myProperty), {
animationStyle: Titanium.UI.iPhone.RowAnimationStyle.LEFT
});
});
When the event fires, createUpdateRow() is called, which returns a tableRow. This tableRow you add all of your custom buttons to, you can change the height of the row, anything. The animation style property will mean if you swipe from the right > left, the new row will animate in from the left, which is an effect I like..
Hope this helps, anyone else.. The extra View (row1) is what got me for ages!

Resources