react-native repeat animation
I searched how to implement a repeated animation ,and I found this.
//this.state.animatedStartValue = 0;
function cycleAnimation() {
Animated.sequence([
Animated.timing(this.state.animatedStartValue, {
toValue: 1,
duration: 500,
delay: 1000
}),
Animated.timing(this.state.animatedStartValue, {
toValue: 0,
duration: 500
})
]).start(event => {
if (event.finished) {
cycleAnimation();
}
});
}
It do works ,but when I use it in my project, I found that this solution will conflict with InteractionManager.runAfterInteractions, which is usually used to do something after animation.
I can use setTimeOut and so on to avoid this, but I want to ask ,is there any better solution to repeat animation , or to avoid this conflict?
use param {isInteraction: false }can avoid this problem.
//this.state.animatedStartValue = 0;
function cycleAnimation() {
Animated.sequence([
Animated.timing(this.state.animatedStartValue, {
toValue: 1,
duration: 500,
delay: 1000,
isInteraction: false,
}),
Animated.timing(this.state.animatedStartValue, {
toValue: 0,
duration: 500
})
]).start(event => {
if (event.finished) {
cycleAnimation();
}
});
}
Related
I have been working on this issue for some time and cannot find a correct solution.
Here a pen https://codepen.io/jean2607/pen/bXKPXz?editors=0010
with some code
Plotly.newPlot('myDiv', [trace1, trace2, trace3, trace4], layout).then(function() {
Plotly.animate('myDiv', frames, {
transition: {
duration: 0
},
frame: {
duration: 1,
redraw: false
}
});
});
I would like to animate only the green "dashdot line" corresponding to trace 4 but impossible so far...
I must be doing something wrong, but I can't see what it is !
Thank you in advance for your help
Finally, I found a solution that meets my needs
There was still a small error in the preparation of the data for the target trace but it is especially with this piece of code that everything works well now.
If someone finds something more appropriate, even better!
Plotly.newPlot('myDiv', [trace6, trace2], layout, options)
.then(function() {
Plotly.addTraces('myDiv', trace1);
})
.then(function() {
Plotly.addTraces('myDiv', trace4);
})
.then(function() {
Plotly.addTraces('myDiv', trace5);
})
.then(function() {
console.log('top!');
Plotly.animate('myDiv', frames, {
transition: {
duration: 0
},
frame: {
duration: 1,
redraw: false
}
});
})
No collisions between map layer and player sprite. But collisions between world bounds work. What is wrong?
I tried different workarounds that could find online and none of them worked.
Game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: "#f",
physics: {
default: 'arcade',
arcade: {
gravity: { y: gameState.gravity },
debug: true
}
},
scene: {
preload,
create,
update
}
};
Code in create() regarding the tilemap and its layers and the character
gameState.map = this.add.tilemap('map');
gameState.dungeonTileset = gameState.map.addTilesetImage('dungeon', 'dungeonTiles');
gameState.backgroundLayer = gameState.map.createStaticLayer('Background', gameState.dungeonTileset);
gameState.mapLayer = gameState.map.createStaticLayer('Map', gameState.dungeonTileset);
gameState.miscLayer = gameState.map.createStaticLayer('Misc', gameState.dungeonTileset);
gameState.mapLayer.setCollisionByExclusion([-1]);
this.physics.world.bounds.width = gameState.mapLayer.width;
this.physics.world.bounds.height = gameState.mapLayer.height;
gameState.player = this.physics.add.sprite(73, 398, 'player', 0);
gameState.player.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player, gameState.mapLayer);
No warning and no errors are coming up in the console. I don't know what to do anymore.
Thanks in advance!
I have addited a little bit the original example so you can see how it looks, I think the problem in your code is the line concerning gameState.mapLayer.setCollisionByExclusion([-1]); but am not sure because I can't see how the tilemap is created
var config = {
type: Phaser.WEBGL,
width: 800,
height: 576,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
loader: {
baseURL: 'https://labs.phaser.io',
crossOrigin: 'anonymous'
},
pixelArt: true,
physics: {
default: 'arcade',
arcade: { gravity: { y: 300 } }
},
scene: {
preload: preload,
create: create,
update: update
}
};
var game = new Phaser.Game(config);
var map;
var cursors;
var player;
var groundLayer;
function preload ()
{
this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/tile-collision-test.json');
this.load.image('player', 'assets/sprites/phaser-dude.png');
}
function create ()
{
map = this.make.tilemap({ key: 'map' });
var groundTiles = map.addTilesetImage('ground_1x1');
map.createDynamicLayer('Background Layer', groundTiles, 0, 0);
groundLayer = map.createDynamicLayer('Ground Layer', groundTiles, 0, 0);
groundLayer.setCollisionBetween(1, 25);//here
player = this.physics.add.sprite(80, 70, 'player')
.setBounce(0.1);
this.physics.add.collider(player, groundLayer);
cursors = this.input.keyboard.createCursorKeys();
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(player);
}
function update ()
{
player.body.setVelocityX(0);
if (cursors.left.isDown)
{
player.body.setVelocityX(-200);
}
else if (cursors.right.isDown)
{
player.body.setVelocityX(200);
}
if (cursors.up.isDown && player.body.onFloor())
{
player.body.setVelocityY(-300);
}
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>
I'm trying to animate fontWeight of an Animated.Text but can't get it to work.
1. Interpolating
const titleFontWeight = this.positionTitle1.x.interpolate({
inputRange: [-0.3 * SCREEN_WIDTH, 0],
outputRange: ['400', '600']
});
render() {
return (
<Animated.Text style={{ fontWeight: titleFontWeight }}>
Title
</Animated.Text>
);
}
With this solution the effect of the change won't happen until the whole animation (i.e. the animation of this.positionTitle1) is done and I get a
Warning: Failed prop type: Invalid prop 'fontWeight' of value '377.333' supplied to text [...].
2. Animated.timing
constructor() {
super();
this.fontWeight = new Animated.Value(400);
}
animateFontWeight() {
Animated.timing(this.fontWeight, {
toValue: 600,
duration: 500
}).start();
}
render() {
return (
<Animated.Text style={{ fontWeight: `${this.fontWeight._value}` }}>
Title
</Animated.Text>
);
}
For this solution the effect also doesn't show until the animation is done.
Is there any solution to this?
Try this
Setup Text
<Animated.Text
style={{fontWeight: this.fontWeightAnimation.interpolate({
inputRange: [300, 900],
outputRange: ['300', '900'],
easing: value => {
const thousandRounded = value * 1000;
if (thousandRounded < 300) {
return 0;
}
if (thousandRounded < 600) {
return 0.5;
}
return 1;
}
})}}> Sample Text </Animated.Text>
Init Animation : this.fontWeightAnimation = new Animated.Value(300);
Start Animation :
Animated.timing(this.fontWeightAnimation, {
toValue: 900,
duration: 3000
}).start();
As it has been mentioned above, fontWeight only accept fixed string value, ex.: '400', '800' or 'normal', 'bold'. To achieve your goal, you can simply using setState at the same time as your animation start.
startAnimation() {
Animated.parallel([
Animated.timing(this.state.translate, {
toValue: -27,
duration: 200,
}),
Animated.timing(this.state.color, {
toValue: 1,
duration: 300,
}),
]).start();
this.setState({
fontWeight: '800'
});};
stopAnimation() {
this.state.translate.setValue(0);
this.state.color.setValue(0);
this.setState({
fontWeight: '400'
});};
I would like when you press a button, the animation runs with the setState of btnLoaded that goes to true, for that's good but I would like that once the animation was done, when click on the buttons, the animation is not executed anymore because it has already been launched and finished.
I do not see how to do that.
Here is the function of animation:
startButtonAnimation = () => {
this.animatedValue.setValue(0);
if (this.state.btnLoaded) {
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 500,
useNativeDriver: true,
easing: Easing.bezier(0.15, 0.73, 0.37, 1.2)
}
).start();
}}
And the code of button:
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
this.setState({
btnLoaded: true
});
setTimeout(() => this.startButtonAnimation(), 50);
}}>
Code in action :
Thank you for your help !
From the docs:
Animations are started by calling start() on your animation. start() takes a completion callback that will be called when the animation is done.
This:
startButtonAnimation = () => {
this.animatedValue.setValue(0);
if (!this.state.btnLoaded) {
this.setState({
btnLoaded: true,
});
Animated.timing(
this.animatedValue,
{
toValue: 1,
duration: 500,
useNativeDriver: true,
easing: Easing.bezier(0.15, 0.73, 0.37, 1.2)
}
).start(() => {
}};
...
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
setTimeout(() => this.startButtonAnimation(), 50);
}}
>
might do it. You should add more of your code to better understand what's going on.
By using this.animatedValue.setValue(0);, you restart the animation to 0. If you put this code outside of your button's callback, then the animation, even if restarted, will be already to its toValue (1 here), so it won't run again.
React-native introduce new Animated API, I want to make a loop animation such as a bubble scale up then scale down and repeat that progress.
However I can not figure it out. I've tried write some code like below
class TestProject extends React.Component {
constructor(): void {
super();
this.state = {
bounceValue: new Animated.Value(0),
v: 1,
};
}
componentDidMount() {
this.state.bounceValue.setValue(1.5);
let animation = Animated.timing(this.state.bounceValue, {
toValue: this.state.v,
});
setInterval(() => {
animation.stop();
if (this.state.flag) {
this.state.v = 0.5;
this.state.bounceValue.setValue(0.5);
}
else {
this.state.v = 1.5;
this.state.bounceValue.setValue(1.5);
}
animation.start();
}, 5000);
}
render(): ReactElement {
return (
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={{uri: 'http://image142-c.poco.cn/best_pocoers/20130517/91062013051716553599334223.jpg'}}
/>
<Animated.Text
style={[
styles.test,
{transform: [
{scale: this.state.bounceValue},
],}
]
}>
haha
</Animated.Text>
</View>
);
}
}
but not works very well.
Any suggestion will be appreciate.
There's now loop animation available:
Animated.loop(
Animated.sequence([
Animated.timing(this.state.animatedStartValue, {
toValue: 1,
duration: 500,
delay: 1000
}),
Animated.timing(this.state.animatedStartValue, {
toValue: 0,
duration: 500
})
]),
{
iterations: 4
}
).start()
I use the sequence method to pass an array of animations to cycle and then repeat the function.
//this.state.animatedStartValue = 0;
function cycleAnimation() {
Animated.sequence([
Animated.timing(this.state.animatedStartValue, {
toValue: 1,
duration: 500,
delay: 1000
}),
Animated.timing(this.state.animatedStartValue, {
toValue: 0,
duration: 500
})
]).start(() => {
cycleAnimation();
});
}
If I'm toggling that animation on it's own it will fade in/out, however I layer it on top of a base to mimic an active state or hotspot-style button
<TouchableOpacity>
<Animated.Image
source={activeImageSource}
style={this.state.animatedStartValue}}
/>
<Image source={nonActiveImageSource}
/>
</TouchableOpacity>
React Native Sequence Documentation
improved version of #bcomerford answer
//this.state.animatedStartValue = 0;
function cycleAnimation() {
Animated.sequence([
Animated.timing(this.state.animatedStartValue, {
toValue: 1,
duration: 500,
delay: 1000
}),
Animated.timing(this.state.animatedStartValue, {
toValue: 0,
duration: 500
})
]).start(event => {
if (event.finished) {
cycleAnimation();
}
});
}
Try something like this:
componentDidMount() {
this.bootAnimation();
}
bootAnimation() {
this.animation = Animated.loop(
Animated.timing(this.state.progress, {
toValue: 1,
duration: 5000
})
).start();
}
It seems that 'looping' is not supported by the Animated api for now.
I managed to do that by start the animation again when it finished.
startAnimation() {
Animated.timing(this._animatedValue, {
toValue: 100,
duration: 1000,
}).start(() => {
this.startAnimation();
});
}
Looking forward to a better solution...
You can set another animation then call the animation again:
An example I did to fade text in and out:
textAnimate: function() {
Animated.timing(
this.state.textOpacity,
{
toValue: 0.3,
duration: 500,
}
).start(() => {
Animated.timing(
this.state.textOpacity,
{
toValue: 1,
duration: 500,
}
).start(() => {
this.textAnimate();
});
});
},
componentDidMount: function() {
this.state.textOpacity.setValue(1)
this.textAnimate();
},
Not sure if it's hacky, but I use this:
Animated.spring(this.state.rotation, {
toValue: 5,
stiffness: 220, // the higher value, the faster the animation
damping: 0.000001, // never stop wiggle wiggle wiggle
}).start();
It's creating spring animation that will never (technically, for a very very very long time) stop waving.
For most of my cases it was enough. Also it has great performance as it does not require any JS tread action ever during animation.
If eventually you'd like to stop it gracefully:
Animated.spring(this.state.rotation, {
toValue: 0,
stiffness: 220, // the higher value, the faster the animation
damping: 10, // never stop wiggle wiggle wiggle
}).start();
And it'll nicely 'slow down' until it stops.
Here's another example for an infinite animation using hooks and iterations set to "infinity". Avoids the use of the recursion in previous answers which sometimes led to funky behaviour during e2e testing for us.
const rotation = React.useRef(new Animated.Value(0)).current;
function runAnimation() {
return Animated.loop(
Animated.timing(rotation, {
toValue: 1,
duration: 1200,
easing: Easing.linear,
useNativeDriver: true,
}),
{resetBeforeIteration: true, iterations: Number.MAX_SAFE_INTEGER},
);
}
React.useEffect(() => {
const animation = runAnimation();
return () => animation.stop();
}, []);