Related
import React from 'react'
import { BarChart, Grid } from 'react-native-svg-charts'
import { Defs, LinearGradient, Stop } from "react-native-svg";
class ColorBarExample extends React.PureComponent {
render() {
const data = [
{
value: 50,
},
{
value: 10,
svg: {
fill: 'rgba(134, 65, 244, 0.5)',
},
},
{
value: 40,
svg: {
stroke: 'purple',
strokeWidth: 2,
fill: 'white',
strokeDasharray: [ 4, 2 ],
},
},
{
value: 95,
svg: {
fill: 'url(#gradient)',
},
},
{
value: 85,
svg: {
fill: 'green',
},
},
]
const Gradient = () => (
<Defs key={'gradient'}>
<LinearGradient id={'gradient'} x1={'0'} y={'0%'} x2={'100%'} y2={'0%'}>
<Stop offset={'0%'} stopColor={'rgb(134, 65, 244)'}/>
<Stop offset={'100%'} stopColor={'rgb(66, 194, 244)'}/>
</LinearGradient>
</Defs>
)
return (
<BarChart
style={{ height: 200 }}
data={data}
gridMin={0}
svg={{ fill: 'rgba(134, 65, 244, 0.8)' }}
yAccessor={({ item }) => item.value}
contentInset={{ top: 20, bottom: 20 }}
>
<Grid/>
<Gradient/>
</BarChart>
)
}
}
export default ColorBarExample
As this is giving me a simple gradient but i am need a gradient like this. How can i get it in this gradient .
Let me know how can i draw it like this image so that each gradient have some borderradius at the top and and have a custom gradient colors as per image and also can be of small width instead of long.
You can give vertical gradient by this x2={'0%'} y2={'100%'} in Gradient function and for rounded corners you can try this
To change width of bar you may try spacingInner property.
To give gradient to all bars, we should manage svg fill property of data array.
here is the link of your barchart code demo. Please check if it helps.
I am really new to React-native-reanimated. I am trying to create one custom bottom-sheet like this app. I am using PanGestureHandler from react-native-gesture-handler for move the Animated View to go up and down. For gestureHandler I am using useAnimatedGestureHandler props from react-native-reanimated. I want to move the Animated View from start point to middle screen and bottom of screen. This is My Bottom sheet start point image, when scroll the card down it should come middle of the screen like this image, again scroll down the card it will come bottom like this image.
I am having difficulties with the conditional useAnimatedGestureHandler onEnd movement. Currently I am tracking onEnd's event.translationY and make a condition out of it.
This is how it works currently:
When the App start, the Animated View is top of the screen, if I move the card scroll to bottom it goes middle of the screen and it does not go down from middle of the screen, I can move it to up from middle of the screen or if I scroll hard to bottom it goes all the way to bottom and if I try scroll the View up it does not go middle, it just goes up to start View.
I am trying to make the condition based screen size but I don't know how to make it.
I shared my code in expo-snacks
This is my all code
import React, { useState, useEffect } from "react";
import { StyleSheet, useWindowDimensions, RefreshControl } from "react-native";
import MapView from "react-native-maps";
import styled from "styled-components";
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
FlatList,
} from "react-native-gesture-handler";
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
Easing,
withSpring,
} from "react-native-reanimated";
const initialRegion = {
latitudeDelta: 15,
longitudeDelta: 15,
latitude: 60.1098678,
longitude: 24.7385084,
};
const api =
"http://open-api.myhelsinki.fi/v1/events/?distance_filter=60.1699%2C24.9384%2C10&language_filter=en&limit=50";
export default function App() {
const { height } = useWindowDimensions();
const top = useSharedValue(height);
const [event, setEvent] = useState([]);
const [loading, setLoading] = useState(false);
const prevTop = useSharedValue(height * 0.5);
// This is Fetch Data
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(api);
const data = await response.json();
setEvent(data.data);
setLoading(false);
} catch (error) {
console.log("erro", error);
}
};
useEffect(() => {
fetchData();
}, []);
const animatedStyle = useAnimatedStyle(() => {
return {
top: top.value * 0.2,
bottom: 0,
};
});
const gestureHandler = useAnimatedGestureHandler(
{
onStart(_, context) {
context.translateY = top.value;
},
onActive(event, context) {
top.value = context.translateY + event.translationY;
},
onEnd(event, _) {
// THIS IS MY CONDITION OF ANIMATED VIEW
if (event.translationY > 0 && event.translationY < 400) {
console.log("middle-top", top.value);
console.log("middle-height", height);
top.value = withSpring(height * 2.5, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
} else if (event.translationY > 450 && event.translationY < 800) {
console.log("bottom-top", top.value);
console.log("bottom-height", height);
top.value = withSpring(height * 4, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
} else if (event.translationY < 0) {
console.log("start-top", top.value);
console.log("start-height", height);
top.value = withSpring(height, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
}
},
},
[top]
);
return (
<>
<MapView style={styles.mapStyle} initialRegion={initialRegion} />
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.container, animatedStyle]}>
<Title>I am scroll sheet</Title>
<HeroFlatList
data={event}
refreshControl={
<RefreshControl
enabled={true}
refreshing={loading}
onRefresh={fetchData}
/>
}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
const image = item?.description.images.map((img) => img.url);
const startDate = item?.event_dates?.starting_day;
return (
<EventContainer key={index}>
<EventImage
source={{
uri:
image[0] ||
"https://res.cloudinary.com/drewzxzgc/image/upload/v1631085536/zma1beozwbdc8zqwfhdu.jpg",
}}
/>
<DescriptionContainer>
<Title ellipsizeMode="tail" numberOfLines={1}>
{item?.name?.en}
</Title>
<DescriptionText>
{item?.description?.intro || "No description available"}
</DescriptionText>
<DateText>{startDate}</DateText>
</DescriptionContainer>
</EventContainer>
);
}}
/>
</Animated.View>
</PanGestureHandler>
</>
);
}
const styles = StyleSheet.create({
container: {
position: "absolute",
left: 0,
right: 0,
top: 0,
backgroundColor: "white",
shadowOffset: {
height: -6,
width: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,
borderTopEndRadius: 15,
borderTopLeftRadius: 15,
},
mapStyle: {
flex: 1,
},
});
const HeroFlatList = styled(FlatList).attrs({
contentContainerStyle: {
flexGrow: 1,
},
})`
padding: 12px;
`;
const Title = styled.Text`
font-size: 16px;
font-weight: 700;
margin-bottom: 10px;
align-self: center;
padding: 10px;
`;
const DescriptionText = styled.Text`
font-size: 14px;
opacity: 0.7;
`;
const DateText = styled.Text`
font-size: 14px;
opacity: 0.8;
color: #0099cc;
`;
const EventImage = styled.Image`
width: 70px;
height: 70px;
border-radius: 70px;
margin-right: 20px;
`;
const DescriptionContainer = styled.View`
width: 200px;
`;
const EventContainer = styled(Animated.View)`
flex-direction: row;
padding: 20px;
margin-bottom: 10px;
border-radius: 20px;
background-color: #fff;
shadow-color: #000;
shadow-opacity: 0.3;
shadow-radius: 20px;
shadow-offset: 0 10px;
`;
Tech information
Tech
Version
react-native-gesture-handler
^1.10.3
react-native-reanimated
^2.2.0
Not the perfect solution...
added a new sharedValue to track if its moving up or down.
const prevTop = useSharedValue(height * 0.5);
and respective code on gesture end.
onEnd() {
if (top.value > prevTop.value) {
top.value = withTiming(height * 0.98);
} else {
top.value = withTiming(Math.min(200, top.value));
}
prevTop.value = top.value;
},
Still there is a scope of improvement.
I'm making a simple app that requires the user to select an image, and that image needs to be passed on to a Webview window.
Now I have multiple problems:
1.The image needs to be passed on to a css "background-image" element
I can't seem to rename the file of the image that the user inputs
I can't relocate the image to the app's resources folder.
So here is the code for the image input:
state = {
ImageSource: null,
};
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 500,
maxHeight: 500,
title: 'Image Picker',
storageOptions: {
skipBackup: true,
path:'data/data/fiftyoff/pictures',
}
};
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled photo picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
var ImagePicked= {path:response.path}
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
module.exports=ImagePicked
this.setState({
ImageSource: source
});
}
});
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.selectPhotoTapped.bind(this)}>
<View style={styles.ImageContainer}>
{ this.state.ImageSource === null ? <Text>Select a Photo</Text> :
<Image style={styles.ImageContainer} source={this.state.ImageSource} />
}
</View>
</TouchableOpacity>
</View>
And here's the CSS where the image needs to be passed to (where image1 is):
.app-picture {
border: 2px solid #893579;
background-position: center;
background-size: cover;
border-radius: 80px;
width: 80px;
height: 80px;
margin: 0 0 1em 0;
top: 68.4714px;
display: block;
position: absolute;
right: 5%;
display: none;
background-image: url("image1.jpg")
I see this behavior on both iOS and Android. Do you know why the content within the card flashes (hides then reappears instantly) after the transform animation starts (and also right before the animation ends).
This only happens if I do rotate, rotateY, or rotateX (untested rotateZ). I have no idea why.
Here is screencast:
High quality webm - https://gfycat.com/SplendidCompetentEnglishpointer
Low quality gif:
My render code is this:
<View style={style}>
<Animated.View style={[styleCommon, styleFace]}><Text>front</Text></Animated.View>
<Animated.View style={[styleCommon, styleBack]}><Text>front</Text></Animated.View>
</View>
And my styles are:
const styleCommon = {
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
elevation: 2,
shadowRadius: 2,
shadowOpacity: 0.4,
shadowOffset: { height:1 },
overflow: 'hidden',
width: '100%',
height: '100%',
position: 'absolute',
backgroundColor: '#FFFFFF'
}
const styleFace = {
opacity: anim.interpolate({ inputRange:[0,.5,.5,1], outputRange:[1,1,0,0] }),
transform: [
{ rotateY:anim.interpolate({ inputRange:[0,1], outputRange:['0deg', '180deg'] }) }
]
};
const styleBack = {
opacity: anim.interpolate({ inputRange:[0,.5,.5,1], outputRange:[0,0,1,1] }),
transform: [
{ rotateY:anim.interpolate({ inputRange:[0,1], outputRange:['-180deg', '0deg'] }) }
]
};
I found it should be a bug of transform in react-native, showing view from 0.1 deg to 0.4 deg, and from -0.1 deg to -0.4 deg. Everything disappears within these degree.
That can be easily proven, by set start degree to 0.1 ~ 0.4.
transform: [
{ rotateY:anim.interpolate({ inputRange:[0,1], outputRange:['0.1deg', '180deg'] }) }
]
A quick workaround for this could be bypassing those Bermuda Degree:
const styleFace = {
transform: [
{ rotateY:this.anim.interpolate({ inputRange:[0,0.01,0.01,1], outputRange:['0deg', '0deg', '0.4deg', '180deg'] }) }
]
};
const styleBack = {
transform: [
{ rotateY:this.anim.interpolate({ inputRange:[0,0.99,0.99,1], outputRange:['-180deg', '-0.4deg', '0deg', '0deg'] }) }
]
};
Result:
I am using Animated.Image inside a scrollView. I apply a pan responder to the Animated.Image.
The problem: when I move the image for big distance, it disappears.
The question: how can I adjust the Animated.Image to stay within specific boundaries when i move it?
My code:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
Animated,
PanResponder,
ScrollView
} from 'react-native';
var xPosition, yPosition;
var position;
export default class AvatarEditor extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(), // inits to zero
image: this.props.image,
border: this.props.border,
width: this.props.width,
height: this.props.height,
viewWidth: this.props.width + 2 * this.props.border,
viewHeight: this.props.height + 2 * this.props.border,
first: true,
left: 0,
top: 0,
xPosition: 0,
yPosition: 0,
translateX: 0,
translateY: 0
};
}
componentWillMount() {
this._animatedValueX = 0;
this._animatedValueY = 0;
this.state.pan.x.addListener((value) => this._animatedValueX = value.value);
this.state.pan.y.addListener((value) => this._animatedValueY = value.value);
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true, //Tell iOS that we are allowing the movement
onMoveShouldSetPanResponderCapture: () => true, // Same here, tell iOS that we allow dragging
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({ x: this._animatedValueX, y: this._animatedValueY });
this.state.pan.setValue({ x: 0, y: 0 }); //Initial value
},
onPanResponderMove: (evt, gestureState) => {
// Animated.event([
// null, { dx: this.state.pan.x, dy: this.state.pan.y }
// ]) // Creates a function to handle the movement and set offsets
newdx = gestureState.dx;
newdy = gestureState.dy;
Animated.event([
null, { dx: this.state.pan.x, dy: this.state.pan.y },
])(evt, { dx: newdx, dy: newdy });
},
onPanResponderRelease: () => {
this.state.pan.flattenOffset(); // Flatten the offset so it resets the default positioning
}
});
}
componentDidMount() {
}
componentWillUnmount() {
this.state.pan.x.removeAllListeners();
this.state.pan.y.removeAllListeners();
}
render() {
var imageStyle = {
width: this.state.width,
height: this.state.height,
resizeMode: 'stretch',
top: this.state.top,
left: this.state.left,
transform: [
{ translateX: this.state.pan.x },//this.state.pan.x
{ translateY: this.state.pan.y },
{ scale: this.props.scale }
]
};
return (
<View
style={[this.props.style, { backgroundColor: 'gray' }]}
>
<ScrollView
style={{
width: this.state.viewWidth,
height: this.state.viewHeight,
borderWidth: this.state.border,
borderColor: 'rgba(100, 100, 100, 0.5)',
overflow: 'hidden',
}}
scrollEnabled={false}
>
<Animated.Image
style={imageStyle}
source={{ uri: this.state.image }}
{...this._panResponder.panHandlers}
>
</Animated.Image>
</ScrollView>
</View>
);
}
}
AvatarEditor.propTypes = {
scale: React.PropTypes.number,
image: React.PropTypes.string,
border: React.PropTypes.number,
width: React.PropTypes.number,
height: React.PropTypes.number,
style: React.PropTypes.object
};
AvatarEditor.defaultProps = {
scale: 1,
border: 25,
width: 200,
height: 200,
style: {
top: 50,
left: 25,
position: 'absolute',
},
image: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQesv5ucRQ1KUNtDipnrhS6Gn9yMn7GOqFdQGTeLMG1fCKGvudEUji_Aw',
};