I have tried formatting the X-axis to display dates in order, but some of the points are still not showing. Here's a link for viewing the chart and editing - https://snack.expo.io/#anushkas/4cfb05
The very first label is suppose to be '09-10-2020' , it shows as '10-2020'. I am using react-native-svg-charts library.
import React from 'react';
import {LineChart, XAxis, YAxis} from 'react-native-svg-charts';
import {View, Text} from 'react-native';
import {Svg, Line, Circle, G, Rect} from 'react-native-svg';
export default class CustomLineChartOne extends React.PureComponent {
handleFill = (value) => {
if (value > 100) {
return 'rgba(190, 30, 45, 0.5)';
} else if (value > 80 && value <= 100) {
return 'yellow';
} else {
return '#CCE6D0';
}
};
render() {
const xAxisLabels = [
'09-10-2020',
'10-10-2020',
'11-10-2020',
'12-10-2020',
];
const data = [50, 10, 40, 95];
const Decorator = ({x, y, data}) => {
return data.map((value, index) => (
<View>
<Rect
x={x(index) - 1.75}
y={y(value + 8)}
width="4"
height="40"
rx="2"
fill={this.handleFill(value)}
/>
<Circle
key={index}
cx={x(index)}
cy={y(value)}
r={2}
stroke={'#639123'}
fill={'#606060'}
/>
</View>
));
};
return (
<View
style={{
height: 200,
flexDirection: 'row',
}}>
<YAxis
data={data}
contentInset={{top: 20, bottom: 20}}
svg={{
fill: 'black',
fontSize: 10,
}}
/>
<View style={{flex: 1}}>
<LineChart
style={{flex: 1}}
data={data}
svg={{stroke: 'rgb(134, 65, 244)'}}
contentInset={{top: 10, bottom: 10, left: 10, right: 10}}>
<Decorator />
</LineChart>
<XAxis
data={data}
formatLabel={(value, index) => xAxisLabels[index]}
contentInset={{ left: 10, right: 10 }}
svg={{ fontSize: 8, fill: '#3A8F98' }}
/>
</View>
</View>
);
}
}
To XAxis component, give contentInset value of 15 to left and right, 10 seems bit low, if it's still hiding then increase it a bit more:
<XAxis
data={data}
formatLabel={(value, index) => xAxisLabels[index]}
contentInset={{ left:15, right: 15 }}
svg={{ fontSize: 8, fill: '#3A8F98' }}
/>
Related
Not sure how I would convert this class component to hooks form. I tried to, but the app doesn't run the same.
Here's the original code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
This code is being used to integrate tooltip in react native charts. I want to include this code with rest of the project code written in hooks form.
export default class myPureComponent extends React.PureComponent
is equal to:
export default React.memo(()=>{
return <View>//...</view>
},(prevProps, nextProps) => {
return prevProps.x === nextProps.x;
// the component will be updated only on `x` prop changes.
})
componentDidMount is equal to:
React.useEffect(()=>reorderData(),[]);
Try to format your codes properly.
Add a [] in your use effects to it only render once.
useEffect(() => {
reorderData();
},[]); //add [] as a dependency, this renders useEffect only on mount.
Full codes
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
I am attempting to convert the following code snippet to hooks. What I have converted so far, doesn't render output as the original. Below is the default code written as class components-
class Area extends React.PureComponent {
state = {
data: [],
tooltipX: null,
tooltipY: null,
tooltipIndex: null,
};
componentDidMount() {
this.reorderData();
}
reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
this.setState({
data: reorderedData,
});
};
render() {
const { data, tooltipX, tooltipY, tooltipIndex } = this.state;
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
}
Below's the code I converted. I feel the below code is not rendering the tooltip because of how the state is written out in hooks. The tooltip isn't showing up upon click.
const Area = () => {
const [ data, setData ] = useState([]);
const [ tooltipX, setTooltipX ] = useState(null);
const [ tooltipY, setTooltipY ] = useState(null);
const [ tooltipIndex, setTooltipIndex ] = useState(null);
useEffect(() => reorderData(),[]);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() =>
this.setState({
tooltipX: moment(item.date),
tooltipY: item.score,
tooltipIndex: index,
})
}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: '70%' }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: '#003F5A' }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid svg={{ stroke: 'rgba(151, 151, 151, 0.09)' }} belowChart={false} />
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: '50%',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 18,
color: '#ccc',
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
}
export default Area = () => {
const [data, setData] = useState([]);
const [tooltipX, setTooltipX] = useState(null);
const [tooltipY, setTooltipY] = useState(null);
const [tooltipIndex, setTooltipIndex] = useState(null);
useEffect(() => reorderData(), []);
const reorderData = () => {
const reorderedData = DATA.sort((a, b) => {
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
return new Date(a.date) - new Date(b.date);
});
setData(reorderedData);
};
const contentInset = { left: 10, right: 10, top: 10, bottom: 7 };
const ChartPoints = ({ x, y, color }) =>
data.map((item, index) => (
<Circle
key={index}
cx={x(moment(item.date))}
cy={y(item.score)}
r={6}
stroke={color}
fill="white"
onPress={() => {
// try this 👇
setTooltipX(moment(item.date));
setTooltipY(item.score);
setTooltipIndex(index);
// try this ☝
}}
/>
));
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
{data.length !== 0 ? (
<AreaChart
style={{ height: "70%" }}
data={data}
yAccessor={({ item }) => item.score}
xAccessor={({ item }) => moment(item.date)}
contentInset={contentInset}
svg={{ fill: "#003F5A" }}
numberOfTicks={10}
yMin={0}
yMax={10}
>
<Grid
svg={{ stroke: "rgba(151, 151, 151, 0.09)" }}
belowChart={false}
/>
<ChartPoints color="#003F5A" />
<Tooltip
tooltipX={tooltipX}
tooltipY={tooltipY}
color="#003F5A"
index={tooltipIndex}
dataLength={data.length}
/>
</AreaChart>
) : (
<View
style={{
height: "50%",
justifyContent: "center",
alignItems: "center",
}}
>
<Text
style={{
fontSize: 18,
color: "#ccc",
}}
>
There are no responses for this month.
</Text>
</View>
)}
<Text style={styles.heading}>Tooltip Area Chart</Text>
</View>
</SafeAreaView>
);
};
I have a Button element here at the bottom. onPress somehow is not rendering my function - 'barCharts' - I wonder why it's not showing up the component upon click. I am using buttons from the react-native paper library.
import * as React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { Button, Menu, Divider, Provider, TextInput } from 'react-native-paper';
import { BarChart, LineChart, Grid } from 'react-native-svg-charts';
import { Colors } from 'react-native/Libraries/NewAppScreen';
const App = () => {
const barCharts = () => {
const fill = 'rgb(134, 65, 244)'
const data = [50, 10, 40, 95, -4, -24, null, 85, undefined, 0, 35, 53, -53, 24, 50, -20, -80]
return (
<View style={styles.sectionContainer}>
<BarChart style={{ height: 200 }} data={data} svg={{ fill }} contentInset={{ top: 30, bottom: 30 }}>
<Grid />
</BarChart>
</View>
);
};
const [visible, setVisible] = React.useState(false);
const openMenu = () => setVisible(true);
const closeMenu = () => setVisible(false);
return (
<Provider>
<View
style={{
paddingTop: 50,
flexDirection: 'row',
justifyContent: 'center',
}}>
<Menu
visible={visible}
onDismiss={closeMenu}
anchor={<Button onPress={openMenu}>Show menu</Button>}>
<Menu.Item onPress={() => {}} title="Item 1" />
<Menu.Item onPress={() => {}} title="Item 2" />
<Divider />
<Menu.Item onPress={() => {}} title="Item 3" />
</Menu>
</View>
<View style={styles.sectionContainer}>
<Button onPress={barCharts}>Show barCharts</Button>
</View>
</Provider>
);
};
export default App;
you are rendering the entire component inside the button - this will not be able to show. What you actually intended to do is more like
const fill = 'rgb(134, 65, 244)'
const data = [50, 10, 40, 95, -4, -24, null, 85, undefined, 0, 35, 53, -53, 24, 50, -20, -80]
const barCharts = (
<View style={styles.sectionContainer}>
<BarChart style={{ height: 200 }} data={data} svg={{ fill }} contentInset={{ top: 30, bottom: 30 }}>
<Grid />
</BarChart>
</View>
);
<View style={styles.sectionContainer}>
<Button onPress={() => setShowBarCharts(true)}>Show barCharts</Button>
</View>
{showBarCharts && barcharts}
This would ensure that the barcharts show on the click of the button.
edit: adding a bit for how to change the barcharts component - it is a function currently. i suspect you will have to render a jsx element.
There is no transform-origin property in react-native so how can I do it ?
I think I should use transformX & transformY props.
const Marker = () => {
const rotate = new Animated.Value(???);
const transformX = new Animated.Value(???);
const transformY = new Animated.Value(???);
const doRotation = (newDegValue) => {
// ?????
}
return (
<View style={{ width: 200, height: 200, justifyContent: 'center', alignItems: 'center' }}>
<Animated.View transform={[{ rotate }, { transformX }, { transformY }]}>
<ArrowIcon width={30} height={30}>
</Animated.View>
{/* I need to rotate my ArrowIcon around this BaseIcon */}
<BaseIcon width={100} height={100} />
<View/>
)
}
Explanation
In this example, I am using the Animated Library. First, we are defining a Animated Value in the constructor. Then we are creating a triggerAnimation function, where we will animate this new value over time using the timing function.
In the render function, we are defining a new style object called animatedStyle. This style object will handle the rotation of the arrow. We are using the interpolate function to incrementally rotate the arrow. As inputRange we are allowing -1 to +1. -1 means a rotation of -360° and +1 a rotation of 360°-, as you can see in the outputRange.The interpolate function will automatically handle the mapping between input and output range.
In the return statement we are passing the animatedStyle to our <Animated.View>.
Now we can call the triggerAnimation function. As parameter we have to pass the desired rotation value.
Some Examples:
this.triggerAnimation(0.5) would result in a rotation of +180°.
this.triggerAnimation(-0.5) would result in a rotation of -180°.
this.triggerAnimation(0.25) would result in a rotation of +90°.
this.triggerAnimation(0.75) would result in a rotation of +270°.
Code
Constructor and triggerAnimation function:
constructor(props){
super(props);
this.state={
currentRotation: 0,
value: new Animated.Value(0),
}
}
triggerAnimation(newValue){
Animated.timing(this.state.value, {
toValue: newValue,
duration: 500,
}).start(() => {
// set rotated again
this.setState({currentRotation: newValue})
});
}
render function:
render() {
const animatedStyle={
transform: [
{
rotateZ: this.state.value.interpolate({
inputRange: [-1,1],
outputRange: ['-360deg', `360deg`],
}),
},
],
};
return (
<View style={styles.container}>
<View style={{justifyContent: 'center', flexDirection: 'column', alignItems: 'center', width: 150, height: 150}}>
<Animated.View style={[{justifyContent: 'center', flexDirection: 'row', alignItems: 'flex-start'}, StyleSheet.absoluteFill,animatedStyle]} >
<Icon name={"location-arrow"} size={30} style={{transform: [ {rotateZ: '-45deg' }]}}/>
</Animated.View>
<View style={{width: 70, height: 70, borderRadius: 35, backgroundColor: 'blue'}}/>
</View>
<TouchableOpacity onPress={()=>this.triggerAnimation(0.5)} >
<Text> 180° Trigger Animation </Text>
</TouchableOpacity>
...
</View>
);
}
Working Demo
https://snack.expo.io/B1UzO79Cr
You can use
transform: [{ rotate: '40deg' }] in styles
Example: => https://snack.expo.io/#msbot01/mad-cookie
export default class App extends React.Component {
constructor(props){
super(props)
this.state=({
angle:0
})
}
rotate(){
this.setState({
angle: this.state.angle + 5
})
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>{this.rotate()}} style={{position: "absolute", top:20, width:'100%'}}>
<Text style={{marginTop:20, position:'absolute', top:20}}>Click on me to rotate</Text>
</TouchableOpacity >
<Image style={{height:40, width:40}} source={require('./bus.png')} />
<View onPress={()=>{this.rotate()}} style={{position: "absolute"}}>
<Image style={{height:150, width:150, transform: [{ rotate: (this.state.angle+'deg') }]}} source={require('./ss.png')} />
</View >
</View>
);
}
}
I'm using react-native-lightbox for an image viewer, and when I open the image I want to take a full-screen width, but it doesn't do that; it just takes a default width before it opens. I'm using the width and height as a state and, when opening the lightbox, for updating the value, but that didn't work (it gave the error "lightbox Not Working".) So, how do I handle these images to take full width and height?
import React, { Component, Fragment } from "react";
import firebase from "react-native-firebase";
import Icon from "react-native-vector-icons/Ionicons";
import Lightbox from "react-native-lightbox";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
FlatList,
ScrollView,
Dimensions
} from "react-native";
const { width, height } = Dimensions.get("window");
class GalleryScreen extends Component {
constructor(props) {
super(props);
this.state = {
images: [],
widthX: width / 3 - 17,
heightX: 110,
flexO: 0
};
}
componentDidMount() {
const uid = firebase.auth().currentUser.uid;
firebase
.database()
.ref(`providers/${uid}`)
.on("value", snapshot => {
let uri = snapshot.val().Images;
let images = [];
Object.values(uri).forEach(img => {
images.push({ uri: img.uri });
});
this.setState({ images });
});
}
render() {
return (
<View style={styles.container}>
<FlatList
numColumns={3}
key={Math.floor(Math.random() * 1000)}
data={this.state.images}
style={{
alignSelf: "center",
marginTop: 10,
marginBottom: 0
}}
renderItem={({ item }) => {
return (
<TouchableOpacity style={{ margin: 5 }}>
<Lightbox
underlayColor="#fff"
style={{ flex: 1 }}
backgroundColor="#001"
>
<Image
key={Math.floor(Math.random() * 100)}
source={{ uri: item.uri }}
style={{
alignSelf: "center",
width: width / 3 - 17,
height: 110,
borderRadius: 15
}}
resizeMethod="resize"
resizeMode="cover"
/>
</Lightbox>
</TouchableOpacity>
);
}}
keyExtractor={(item, index) => index.toString()}
/>
<TouchableOpacity
style={{
alignItems: "center",
justifyContent: "center",
alignSelf: "flex-end",
width: 70,
height: 70,
right: 20,
bottom: 15,
borderRadius: 50
// backgroundColor: "#fff"
}}
>
<Icon name="ios-add-circle" size={70} color="#2F98AE" />
{/* <Text style={{ fontSize: 40, color: "#fff" }}>+</Text> */}
</TouchableOpacity>
</View>
);
}
}
export default GalleryScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f1f1f1"
}
});
You need to set size for outer container intead of the image and give the image with width and height 100% so that when the lightbox shows up, the image will take a full-screen size
<TouchableOpacity
key={Math.floor(Math.random() * 100)}
style={{ margin: 5, width: width / 3 - 17, height: 110 }}
>
<Lightbox
underlayColor="#fff"
style={{ flex: 1 }}
backgroundColor="#001"
>
<Image
source={{ uri: item.uri }}
style={{
borderRadius: 15,
width: "100%",
height: "100%"
}}
resizeMethod="resize"
resizeMode="cover"
/>
</Lightbox>
</TouchableOpacity>
lightBox have property called (activeProps). for make image full screen just delete style={{ flex: 1 }}. and add that property. set its value like this:
activeProps={{width: '100%', height: '100%'}}
as reference check this out : https://github.com/oblador/react-native-lightbox