In the next code:
return connectDropTarget(
<div style={{ ...style, backgroundColor }}>
{`Works with ${allowedDropEffect} drop effect`}
<br />
<br />
{isActive ? 'Release to drop' : 'Drag a box here'}
</div>,
)
what is the use of ...style for? I can't see any variable declared as style.
It's spread syntax that is being used to copy the contents of an existing style object and then adding/overwriting a backgroundColor property.
const styles = {
position: 'relative',
top: 0,
left: 0
};
const backgroundColor = {
background: '#FFF'
};
const newStyles = {...styles, backgroundColor };
console.log(newStyles);
// newStyles
// {
// position: 'relative',
// top: 0,
// left: 0,
// background: '#FFF'
// }
It can also be used to overwrite existing properties, but keep the others untouched:
const styles = {
position: 'relative',
top: 0,
left: 0,
background: '#222'
};
const backgroundColor = {
background: '#FFF'
};
const newStyles = {...styles, backgroundColor };
console.log(newStyles);
// newStyles
// {
// position: 'relative',
// top: 0,
// left: 0,
// background: '#FFF'
// }
So it's basically an es6 shorthand for Object.assign.
It means that the props of style will be passed as they exists
The style and background might already exists in your page :
const style = {fontSize: "9px", backgroundColor: "red"};
Related
I am trying to make an animated menu components which consists out of a main menu button and a few menu items. I am trying to make the menu items to be animated in the following way (see gif below):
See below what i have tried so far but i am unsure of how to accomplish the desired effect. Since i dont know how many menu items there will be and thus cannot set predefined sizes for the container.
In my current attempt i do use predefined sizes and also have no control over each specific item's bounce and delay.
I am new to react native and definitely completely lost when it comes to animating layouts. Any help or pointers would be greatly appreciated!
(this is a part of a more exhaustive menu component hence the additional parent views, but i left most of that out to keep the question to the point).
menu component:
const Menu = function (props: any) {
const anim_def = { duration: 500, easing: Easing.out(Easing.exp) }
const animated_height = useSharedValue(60);
const animated_menu = useAnimatedStyle(() => {
return {
position: "absolute",
bottom: 20,
left: 20,
display: "flex",
flexDirection: "column",
overflow: "hidden",
height: withTiming(animated_height.value, anim_def),
}
});
const generate_menuitems = (): ReactNode[] | null => {
const items: ReactNode[] = [];
if (props.menu_items === undefined) return null;
props.menu_items.forEach((item, index) => {
const styles: ViewStyle = {
marginTop: (index === 0) ? 0 : 20,
backgroundColor: item.backgroundColor
}
items.push(<ImageButton
image={item.image ?? 0}
onPress={() => { on_press_behaviour(index, item); }}
underlayColor={item.highlightColor}
style={[style.buttons, props.style?.buttons, styles]}
></ImageButton>)
});
return items;
}
const on_open_menu = () => {
animated_height.value = 300;
}
return (
<View pointerEvents="box-none" style={{ ...style.container, ...props.style?.container }}>
<View pointerEvents="box-none" style={style.container_wrapper}>
<Animated.View style={animated_menu}>
<ImageButton onPress={on_open_menu} onLayout={(l: LayoutChangeEvent) => {console.log(l.nativeEvent.layout)}} image={0} underlayColor="orange" style={[style.buttons, props.style?.buttons, { position: "absolute", left: 0, bottom: 0, zIndex: 2 }]}></ImageButton>
{generate_menuitems()}
</Animated.View>
</View>
</View>
);
}
component styles:
const style = StyleSheet.create({
container: {
position: "absolute",
left: 0,
bottom: 0,
width: "100%",
height: "100%",
},
container_wrapper: {
position: "relative",
width: "100%",
height: "100%",
},
buttons: {
width: 60,
aspectRatio: 1,
borderRadius: 30,
backgroundColor: "green",
marginTop: 20
}
});
This is inside a .tsx file. The styles variable is applied as styles={styles}
var styles = {
bmBurgerBars: {
height: '10px',
right: '20px'
}
//nth child for bmBars
}
I have tried the below code but it is not implementing on the UI
var styles = {
bmBurgerBars: {
height: '10px',
right: '20px',
'&:nth-child(2)': {
width: '80%',
left: '7px',
background: '#111'
}
}
}
Assuming you're using a library such as Material UI (or similar/forked), you can use the following spec:
var styles = {
bmBars: {
height: '10px',
right: '20px',
'&:nth-child(n)': {
color: 'red'
}
}
}
I am trying to render multiple components inside of a parent component on a specific position (based on some calculations). The calculations that give me the vertical position look correct, but the components are not displayed in the position they should. I have tried both absolute window position and relative component position with no luck.
The parent looks like follows:
const top = 170;
const bottom = 10;
const left = 10;
const right = 10;
const styles = StyleSheet.create({
grid: {
flex: 1,
position: 'absolute',
top: top,
height: Dimensions.get('window').height - top - bottom,
width: Dimensions.get('window').width - left - right,
borderLeftColor: 'black',
borderLeftWidth: 1,
borderBottomColor: 'black',
borderBottomWidth: 1
}
});
const DrawGrid: React.FC<IDrawGrid> = ({ maxDistance, elements }) => {
const [gridSize, setGridSize] = useState<LayoutRectangle>();
return (
<View style={styles.grid} onLayout={(event) => {
setGridSize(event.nativeEvent.layout);
}}>
{elements.map((element, index) => {
return (
<DrawElement element={element} maxDistance={maxDistance} gridSize={gridSize} index={index * 2} />
)
})}
</View>
);
};
And the child component that renders all the elements looks like follows:
const top = 170;
const bottom = 20;
const left = 10;
const right = 10;
const styles = StyleSheet.create({
elementContainer: {
borderLeftColor: 'red',
borderLeftWidth: 1,
borderTopColor: 'red',
borderTopWidth: 1,
borderRightColor: 'red',
borderRightWidth: 1,
borderBottomColor: 'red',
borderBottomWidth: 1,
borderRadius: 5,
padding: 2,
position: 'relative',
alignSelf: 'flex-start'
}
});
const getVerticalPosition = (someDistance: number, maxDistance: number, height: number) => {
if (!someDistance || !maxDistance) return { top: 0 };
const topDistance = (1 - (someDistance / maxDistance)) * height;
return { top: topDistance };
};
const DrawElement: React.FC<IDrawElement> = ({ maxDistance, element, gridSize, index }) => {
const styleVertical = getVerticalPosition(someDistance, maxDistance, gridSize.height);
return (
<View key={key} style={[styles.elementContainer, styleVertical]}>
<Text>top: {styleVertical.top.toFixed(2)}</Text>
</View>
);
};
I can see how getVerticalPosition returns the right value, but the element is never located in the position expected. In the snapshot below I am printing the top value for each element, and we can see it is not respected at all. (horizontal location is out of the scope of the problem)
My first thought was that I am messing up the styles somehow, I also tried giving a different zindex to each element without luck. Any ideas what could happen? Any help would be much appreciated, thank you!
I think you don't understand how the layout type works in React Native. Here is a link: https://reactnative.dev/docs/flexbox#absolute--relative-layout. The main problem is relative position for each child element related to its own initial position.
By default, all elements in React Native have position equal to relative, so you don't need to put it in style definition.
To better understanding relative position scheme, I suggest you consider its rendering process in two steps:
rendering children without any of top, left, bottom, right properties;
shifting children according to their top, left, bottom, right properties from their current positions;
Imagine that you have such code:
<View style={styles.parent}>
<View style={styles.firstChild}/>
<View style={styles.secondChild}/>
</View>
const styles = StyleSheet.create({
parent: {
top: 100,
flex: 1,
},
firstChild: {
top: 100,
height: 50,
},
secondChild: {
top: 80,
height: 70,
},
});
If we apply two steps mentioned above to this example it will look like this:
To solve your problem you need to apply absolute position scheme to children. Only absolute position scheme place element, with for example top: 100 property, to 100 points down related to the current top of its parent.
I hope it will help you a little.
I have a background image for my component. I want 1 more image which is 48 * 48 (small image)
to move on it from left to right and top randomly. Here is the code of my background image -
const sectionStyle = {
width: "100vw",
height: "100vh",
backgroundImage: `url(${Background})`,
backgroundSize: 'cover',
backgroundPosition: 'center'
};
Then the render of the compoent has div which uses sectionStyle.
How do I get this small image moving on the background image randomly every few seconds?
One way to accomplish this would be to use use absolute positioning and define state for position of your image. Then you can change positions with setInterval.
It might not be the best way possible but it will get the work done. Here's an example implementation. (I've taken random values for the ease of understanding) -
const styles = {
mainContainerStyle: {
margin: '50px',
background: 'yellow',
padding: '20px',
height: '600px',
},
backgroundContainer: {
position: 'absolute',
background: `url('YOUR_BACKGROUND_IMAGE')`,
width: '400px',
height: '400px',
},
imageStyle: {
position: 'absolute',
top: 0,
left: 0,
width: 45,
height: 45,
borderRadius: 5,
},
}
class ExampleComponent extends Component {
constructor(props) {
super(props);
this.state = {
left: 0,
top: 0,
}
}
randomMovement = () => {
this.setState((prevState) => ({
left: prevState.left + 30 * Math.random(),
top: prevState.top + 30 * Math.random(),
}));
}
render() {
return (
<div style={styles.mainContainerStyle}>
<div style={styles.backgroundContainer}>
<img
src="YOUR_IMAGE_URL"
style={{
...styles.imageStyle,
top: this.state.top,
left: this.state.left,
}}
/>
</div>
</div>
);
}
componentDidMount() {
this.startMovement = setInterval(this.randomMovement, 3000);
}
}
Hope it helps.
I am trying to animate the scale of my react component. It seems fairly simple and this was basically right out of the React-Motion examples:
var component = () => {
let style = { scale: spring(1.1, presets.wobbly)};
return (
<Motion style={style}>
{value =>
<div
style={{
backgroundColor: 'purple',
height: '50%', width: '50%',
transform: `scale(${value.scale})`,
WebkitTransform: `scale(${value.scale})`,
}}>
</div>
}
</Motion>
);
}
but the component keeps disappearing or not displaying properly for a variety of reasons. How do I get this component's scale to animate properly?
You don't want to use value.scale but just value. Also to improve readability you can use scale instead of value as your parameter. Check this article for a simple example
var component = () => {
let defaultStyle = {scale: 0};
let style = { scale: spring(1.1, presets.wobbly)};
return (
<Motion defaultStyle={defaultStyle} style={style}>
{({scale}) =>
<div
style={{
backgroundColor: 'purple',
height: '50px', width: '50px',
transform: `scale(${scale})`,
WebkitTransform: `scale(${scale})`,
}}>
</div>
}
</Motion>
);
}