I don't know why but my react-spring only works when there is a Canvas from react-three-fiber.
import React from 'react';
import { Canvas } from "#react-three/fiber";
import { useSpring, animated } from "react-spring";
// These are from the react-spring website so It does work.
function LoopTrue() {
const styles = useSpring({
loop: true,
from: { rotateZ: 0 },
to: { rotateZ: 180 },
});
return (
<animated.div
style={{
width: 80,
height: 80,
backgroundColor: "#46e891",
borderRadius: 16,
...styles,
}}
/>
);
}
function Angle() {
const [flip, set] = useState(false);
const props = useSpring({
reset: true,
reverse: flip,
from: { transform: "rotateX(0deg)" },
transform: "rotateX(180deg)",
delay: 200,
onRest: () => set(!flip),
});
return <animated.h1 style={props}>angle</animated.h1>;
}
function Project() {
return (
<>
<Angle/>
<LoopTrue/>
{/* <Canvas></Canvas> */}
</>
);
}
If I uncomment that <Canvas></Canvas> it works, but if I comment Canvas, it does not work.
I don't know if that has to do with other react-router-dom page. (I am using some react-three-fiber Canvas in "home" page", this will be the "blog" page)
Help!
This is a known bug of react-spring see here
However, as the issue describes a workaround is this:
Globals.assign({
frameLoop: 'always',
})
Related
I recently started trying to use animation in an app I am trying to make in React Native.
I am not 100% familiar with React Animated, but I believe the animations that I am trying to make are very simple.
I have a screen where I would like some text to slide in from the right, pause a few seconds, slide out to the left, before repeating itself with some other text.
While I did manage to do that, the animation and the text quickly become very buggy (aka no smooth animation, no animation at all after a while, the text will very quickly change randomly, etc...).
I am not sure why this is, I tried switching the useNativeDriver to true to hopefully get a smoother animation, but then I get an error saying I can't use the style property 'left'.
Here is the code:
import React, { useState, useCallback, useEffect } from 'react';
import { Text, View, StyleSheet, Animated } from 'react-native';
import Constants from 'expo-constants';
function App() {
let [wordsAnim] = useState(new Animated.Value(60)),
runAnimation = () => {
wordsAnim.setValue(60);
Animated.sequence([
Animated.timing(wordsAnim, {
toValue: 0,
duration: 1500,
useNativeDriver: false,
}),
Animated.timing(wordsAnim, {
toValue: -60,
duration: 1500,
delay: 3000,
useNativeDriver: false,
}),
]).start(({ finished }) => {
runAnimation();
updateWord();
});
};
//An array of random words to display
const words = ['First', 'Second', 'Third', 'Fourth'];
//First word displayed is a random word from the array
const [word, changeWord] = useState(
words[Math.floor(Math.random() * words.length)]
);
//Update the word displayed with another random word from the array
const updateWord = () => {
const index = Math.floor(Math.random() * words.length);
changeWord(words[index]);
};
useEffect(() => {
runAnimation();
});
return (
<View style={styles.container}>
<Animated.Text
style={{
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
left: wordsAnim.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
}),
}}>
{word}
</Animated.Text>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
You can also find this example here.
I would also take any tips regarding animations or anything else for React Native,
Thanks!!
As pointed out by #David784 , my runAnimation was retriggered after every completed render. It should've only be run on component mounts.
Adding a few callbacks did the trick!
Working code below:
import React, { useState, useCallback, useEffect } from 'react';
import { Text, View, StyleSheet, Animated } from 'react-native';
import Constants from 'expo-constants';
function App() {
let [wordsAnim] = useState(new Animated.Value(60)),
runAnimation = useCallback(() => {
wordsAnim.setValue(60);
Animated.sequence([
Animated.timing(wordsAnim, {
toValue: 0,
duration: 1500,
useNativeDriver: false,
}),
Animated.timing(wordsAnim, {
toValue: -60,
duration: 1500,
delay: 3000,
useNativeDriver: false,
}),
]).start(({ finished }) => {
updateWord();
runAnimation();
});
}, [updateWord, wordsAnim]);
//An array of random words to display
const words = ['First', 'Second', 'Third', 'Fourth'];
//First word displayed is a random word from the array
const [word, changeWord] = useState(
words[Math.floor(Math.random() * words.length)]
);
//Update the word displayed with another random word from the array
const updateWord = useCallback(() => {
const index = Math.floor(Math.random() * words.length);
changeWord(words[index]);
}, [words]);
useEffect(() => {
runAnimation();
}, [runAnimation]);
return (
<View style={styles.container}>
<Animated.Text
style={{
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
left: wordsAnim.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
}),
}}>
{word}
</Animated.Text>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
I'm trying to use the Tooltip component of the React Material UI Library somewhere in my project. The code for the same is like this:
<WhiteOnDarkGreyTooltipWithControls
disableTouchListener
disableFocusListener
title={
<Text selectable={false} style={[styles.ratedIndicatorTextColor]}>
{"Game Completion Rate"}
</Text>
}
placement={"bottom"}
disablePortal={true}
>
<Text selectable={false}>GCR</Text>
</WhiteOnDarkGreyTooltipWithControls>;
Where WhiteOnDarkGreyTooltipWithControls looks like this:
import withStyles from "#material-ui/core/styles/withStyles";
import Tooltip from "#material-ui/core/Tooltip";
import React from "react";
export const WhiteOnDarkGreyTooltip = withStyles({
tooltip: {
color: "E0E0E0",
backgroundColor: "#404040",
borderRadius: 2,
maxWidth: 200,
textAlign: "center",
fontWeight: 900,
padding: 8,
marginTop: 5,
opacity: 1
},
popper: {
opacity: 1
}
})(Tooltip);
export class WhiteOnDarkGreyTooltipWithControls extends React.Component {
state = { open: this.props.open };
render = () => (
<WhiteOnDarkGreyTooltip
TransitionComponent={({ children }) => children}
{...this.props}
enterDelay={0}
open={this.state.open}
PopperProps={{
placement: "bottom",
disablePortal: !!this.props.disablePortal,
modifiers: [
{
preventOverflow: {
enabled: false,
boundariesElement: "scrollParent"
}
}
]
}}
/>
);
open = () => this.setState({ open: true });
close = () => this.setState({ open: false });
}
I want my tooltips to have an opaque, black background with white text on top. Elsewhere in the project the above configuration works fine, but particularly in the above usage, some transparency is being added:
How can I disable any opacity being set by default in the Component in the Material UI library?
I am creating a component that animates a group of buttons in an elastic staggered way using react-native-pose. The buttons them selves use pose to animate the pressed state. I have almost got it looking how I want it although I'm not sure I'm doing things correctly.
This is what I want to achhieve...
... However the text looks awful like its got jpg artefacts on it :
App.js
import React, { Component } from 'react';
import styled from 'styled-components';
import posed from 'react-native-pose';
import Button from './src/components/Button';
const ScreenContainer = styled.View({
flex: 1,
padding: 20,
marginTop: 100
});
const Buttons = posed.View({
visible: {
staggerChildren: 100
},
hidden: {
staggerChildren: 100
}
});
export default class App extends Component {
state = {
buttonPose: 'hidden'
};
items = [
{ id: 0, label: 'One' },
{ id: 1, label: 'Two' },
{ id: 2, label: 'Three' }
];
componentDidMount = () => {
this.setState({
buttonPose: 'visible'
});
};
render() {
return (
<ScreenContainer>
<Buttons pose={this.state.buttonPose}>
{this.items.map(item => (
<Button label={item.label} key={item.id} />
))}
</Buttons>
</ScreenContainer>
);
}
}
Button.js
import React, { PureComponent } from 'react';
import { TouchableWithoutFeedback } from 'react-native';
import styled from 'styled-components';
import posed from 'react-native-pose';
const Container = styled(
posed.View({
visible: {
opacity: 1,
x: 0
},
hidden: {
opacity: 0,
x: -100
}
})
)({
marginBottom: 20
});
const Background = styled(
posed.View({
// If I comment out these poses the problem goes away
pressIn: {
scale: 1.1
},
pressOut: {
scale: 1
}
})
)({
padding: 20,
backgroundColor: '#f9415d',
borderRadius: 10
});
const Label = styled.Text({
fontSize: 18,
color: 'white',
textAlign: 'center'
});
export default class Button extends PureComponent {
state = {
buttonPose: 'pressOut'
};
onPressIn = () => {
this.setState({
buttonPose: 'pressIn'
});
};
onPressOut = () => {
this.setState({
buttonPose: 'pressOut'
});
};
componentDidMount = () => {};
render() {
const { onPressIn, onPressOut } = this;
const { buttonPose } = this.state;
const { label } = this.props;
return (
<Container>
<TouchableWithoutFeedback onPressIn={onPressIn} onPressOut={onPressOut}>
<Background pose={buttonPose} withParent={false}>
<Label>{label}</Label>
</Background>
</TouchableWithoutFeedback>
</Container>
);
}
}
Can anyone offer any insight into why the text and also the rounded corners look so artifacted and low res?
Right now, I return a true in my onStartShouldSetPanResponder, and as a result the PanResponder wants to handle taps AND pans. Is there any way to restrict it to just pans, as I want a TouchableHighlight to handle that? (I get that the Gesture Responder should handle both, but it seems weird that the "Pan" Responder handles taps)
Since the gesture is just starting, the dx/dy are 0 in onStartShouldSetPanResponder. Is there any way to detect if it's the start of a tap and return false, if so?
Or should I just detect whether it was a tap or pan in the OnPanResponderRelease?
I was able to accomplish this through the onMoveShouldSetPanResponder method as follows:
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dx) >= 1 || Math.abs(gestureState.dy) >= 1
}
If the x or y movement is greater than 1, return true. In order to then detect a tap, I had to wrap everything within my view containing the panHandlers with a touchable element. Here is a full working example:
import React, { Component } from 'react';
import { TouchableOpacity, Animated, PanResponder, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
return (
<View>
<CircleTapExample/>
</View>
);
}
class CircleTapExample extends Component {
constructor(props) {
super(props)
this.position = new Animated.ValueXY({ x: 0, y: 0 });
this.panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dx) >= 1 || Math.abs(gestureState.dy) >= 1
},
onPanResponderMove: (evt, gestureState) => {
console.log("I was moved")
this.position.setValue({ x: gestureState.moveX, y: gestureState.moveY })
},
});
}
circleTapped() {
// Do something here when tapped
console.log("I was tapped")
}
render() {
return (
<Animated.View style={[styles.container, { ...this.position.getLayout() }]} {...this.panResponder.panHandlers}>
<TouchableOpacity onPress={() => this.circleTapped()} style={{ flex: 1 }}>
<View style={styles.circle} />
</TouchableOpacity>
</Animated.View>
)
}
}
const styles = StyleSheet.create({
container: {
width: 75,
height: 75,
bottom: 5,
left: 5,
position: 'absolute'
},
circle: {
width: 75,
height: 75,
borderRadius: 40,
backgroundColor: 'red'
}
});
<div data-snack-id="YskU-lxRe" data-snack-platform="web" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#F9F9F9;border:1px solid var(--color-border);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.dev/embed.js"></script>
The panResponder has two events :
onStartShouldSetPanResponder(Capture)
onMoveShouldSetPanResponder(Capture)
I've been able to solve this problem only by removing onStartShouldSetPanResponderCapture.
I am trying to spin an Image it is basically to show that a coin is flipped ( coin Tossing animation ) I have applied this basic animation to the image but it is not getting animated,
The image is stationary while I tested it on emulator
this is my index.android.js file :
import React, { Component } from 'react';
import {
AppRegistry,
View,
Animated,
Easing
} from 'react-native';
export default class animateTest extends Component {
constructor(props) {
super(props);
this.spinValue = new Animated.Value(0);
}
spin() {
this.spinValue.setValue(0);
Animated.timing(
this.spinValue, {
toValue: 1,
duration: 1500,
useNativeDriver: true,
easing: Easing.linear
}
).start();
}
render() {
const spin = this.spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
return (
<View style={styles.ViewStyle}>
<Animated.Image
style={[
styles.coinStyle,
{
transform: [
{ rotate: spin }
]
}
]}
source={require('./Images/Coin_Tail.png')}
style={styles.coinStyle} />
</View>
);
}
}
const styles = {
coinStyle: {
width: 150,
height: 150,
},
ViewStyle: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black'
}
};
AppRegistry.registerComponent('animateTest', () => animateTest);
You code have 2 issues:
1) In your render function, you have a duplicated style prop for your image that override the first style with transform styling. To fix it, remove the second style prop
2) Your code did not trigger the spin animation, you can add a touchable with on press event to call your spin method. For quick test, you can add
componentDidMount() {
this.spin();
}