UNPKG

react-native-simple-charts

Version:
443 lines (402 loc) 13.6 kB
import React, { Component } from 'react'; import { View, ScrollView, Dimensions, TouchableOpacity, InteractionManager, ActivityIndicator, Platform } from 'react-native'; import Svg,{ LinearGradient, Path, Text, Defs, Stop, G, Circle, Rect, } from 'react-native-svg'; import * as Animatable from 'react-native-animatable' import styles from './ChartStyle' import { getAxisXSectorsArray, getAxisXLinesArray, getAxisYLinesArray, getPointPathArray, transformYAxeData, } from './ChartHelper' import { BlurView } from 'react-native-blur'; const {width, height} = Dimensions.get('window'); export default class Chart extends Component { constructor(props){ super(props) if (this.props.data) { const {transformedData, axisYData} = transformYAxeData(this.props.data, this.props.stepsOY, this.props.maxYValue) this.data = transformedData this.axisYData = axisYData this.activeAxisXTextArray = this.props.activeAxisXTextArray || [] this.height = this.props.height this.width = this.props.width || width * 1.2 this.graphHeight = this.height * 0.75 this.graphWidth = this.width * 0.75 this.Xwidth = this.graphWidth / this.props.scaleXAxis this.Ywidth = this.graphHeight / (this.props.stepsOY - 0.3) this.OX = this.props.leftPanelWidth this.OY = this.Ywidth * this.props.stepsOY * 1.07 this.maxOY = 0 this.realWidth = this.OX + this.Xwidth * (this.data[0].chart.length) this.maxOX = this.realWidth } this.state = {interactionsComplete: false} } shouldComponentUpdate(nextProps, nextState) { if (this.props.data !== nextProps.data || this.state.interactionsComplete !== nextState.interactionsComplete || this.props.height !== nextProps.height) { return true; } return false; } componentWillMount(){ if (this.props.data) { this.calculateInitData() } } componentDidMount() { InteractionManager.runAfterInteractions(() => { this.setState({interactionsComplete: true}); }); } render() { if (!this.props.data) { return null } this.resetVariables() let pathX = 'M ' + this.OX + ' ' + this.OY + ' ' + this.maxOX + ' ' + this.OY return ( <View style={[styles.container, {backgroundColor: this.props.backgroundColor}]}> {this.renderRootElement()} <View style={[styles.leftPanel, !this.props.renderBlur || Platform.OS === 'android' ? {backgroundColor: this.props.leftPanelBG} : {}]}> <Svg height={this.height + 2} width={this.props.leftPanelWidth}> <Rect x="0" y="0" width={this.props.leftPanelWidth} height={this.height} fill={'transparent'} /> {this.renderYAxe()} {this.axisYData.map((text) => { this.axisYTextCoordinates = this.axisYTextCoordinates ? this.axisYTextCoordinates - this.Ywidth : (this.OY - this.Ywidth) return this.renderText(text, this.width * 0.05, this.axisYTextCoordinates, this.props.axisTextColor, this.props.axisTextOpacity)})} </Svg> </View> {this.renderBlur()} </View> ); } calculateInitData(){ this.axisXData = this.getAxisXData() // this.axisYData = this.props.axisYData this.chartHeight = this.graphHeight * 1.2 + this.Ywidth this.convertedData = [] this.data.map((data) => { this.convertedData.push( this.convertData(data) ) }) const stateData = { axisXData: this.axisXData, Xwidth: this.Xwidth, Ywidth: this.Ywidth, realWidth: this.realWidth, OX: this.OX, OY: this.OY, maxOY: this.maxOY, pointsData: this.data, convertedData: this.convertedData, height: this.height, } this.axisXSectorsArray = getAxisXSectorsArray(stateData) this.axisXLinesArray = getAxisXLinesArray(stateData) this.axisYLinesArray = getAxisYLinesArray(stateData, this.axisYData) this.pointPathArray = getPointPathArray(stateData).pointPathArray this.gradientPathArray = getPointPathArray(stateData).gradientPathArray } renderRootElement(){ return ( <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.scrollView}> <Svg height={this.chartHeight} width={this.realWidth}> {this.axisXData.map((text, i) => { this.axisXSectorsCoordinates = this.axisXSectorsCoordinates ? this.axisXSectorsCoordinates + this.Xwidth : this.OX return this.renderAxisXSectors(i)})} {this.axisXData.map((text, i) => { this.axisXLinesCoordinates = this.axisXLinesCoordinates ? this.axisXLinesCoordinates + this.Xwidth : this.OX return this.renderAxisXLines(i)})} {this.axisYData.map((text,i) => { this.axisYLinesCoordinates = this.axisYLinesCoordinates ? this.axisYLinesCoordinates - this.Ywidth : (this.OY - this.Ywidth) return this.renderAxisYLines(i)})} {this.renderXAxe()} {this.axisXData.map((text) => { let color = this.activeAxisXTextArray.includes(text) ? this.props.axisTextColorActive : this.props.axisTextColor this.axisXTextCoordinates = this.axisXTextCoordinates ? this.axisXTextCoordinates + this.Xwidth : this.OX return this.renderText(text, this.axisXTextCoordinates + this.Xwidth / 2, this.OY + this.Ywidth / 4, color, this.props.axisTextOpacity)})} </Svg> {this.renderMain()} </ScrollView> ) } renderMain(){ this.globalPointIndex = -1 return ( <Animatable.View animation={'fadeIn'} duration={500} easing={'linear'} style={styles.main}> <Svg height={this.graphHeight + this.Ywidth / 1.5} width={this.realWidth}> {this.data.map((data, i) => { return this.renderData(data, i) })} </Svg> </Animatable.View> ) } renderBlur(){ if (this.props.renderBlur && Platform.OS === 'ios') { return ( <BlurView blurType="light" blurAmount={10} {...this.props.blurProps} style={[styles.blurDark, {width: this.props.leftPanelWidth}]} /> ) } } renderData(data, index){ let convertedData = this.convertedData[index] this.gradientIndex = -1 return ( <G key={Math.random()}> {convertedData.map((chart, i) => { if (i !== data.chart.length - 1) { return this.renderPointPath(chart.x, chart.y, convertedData[i + 1].x, convertedData[i + 1].y, data.chart[i].props) } })} </G> ) } convertData(data){ let convertedData = [] for (var i = 0; i < data.chart.length; i++) { let chartObj = { x: this.x(i), y: this.y(data.chart[i].y) } convertedData.push(chartObj) } return convertedData } renderPointPath(x1,y1,x2,y2,props){ this.globalPointIndex++ return ( <G key={Math.random()}> <Path d={this.pointPathArray[this.globalPointIndex]} stroke={props.strokeColor} strokeWidth={props.strokeWidth} opacity={props.strokeOpacity} fill={'none'}/> {props.fillGradient ? this.renderGradient(props.gradientStartColor, props.gradientEndColor) : ()=>{}} {props.renderPoints ? this.renderPoints(x1,y1,props.pointColor1, props.strokeColor) : ()=>{}} {props.renderPoints ? this.renderPoints(x2,y2,props.pointColor2, props.strokeColor) : ()=>{}} </G> ) } renderGradient(startColor, endColor){ this.gradientIndex++ return ( <G key={Math.random()} style={{flex: 1}}> <Defs> <LinearGradient id="grad" x1="0" y1="0" x2="0" y2={this.OY}> <Stop offset="1" stopColor={startColor || 'transparent'} stopOpacity={this.props.gradientOpacityStart} /> <Stop offset="0" stopColor={endColor} stopOpacity={this.props.gradientOpacityEnd} /> </LinearGradient> </Defs> <Path d={this.gradientPathArray[this.gradientIndex]} stroke="none" fill={'url(#grad)'}/> </G> ) } renderPoints(x,y, color, strokeColor){ return ( <Circle key={Math.random()} cx={x} cy={y} fill={color} r={this.props.pointRadius ? this.props.pointRadius : '3'}/> ) } renderAxisXSectors(i){ if (this.props.renderAxisXSectors && this.axisXSectorsArray[i]) { return ( <Path d={this.axisXSectorsArray[i]} key={Math.random()} stroke="none" opacity={this.props.axisXSectorsOpacity ? this.props.axisXSectorsOpacity : 1} fill={this.props.axisXSectorsColor ? this.props.axisXSectorsColor : 'black'}/> ) } } renderAxisXLines(i){ if (this.props.renderAxisXLines) { return ( <Path d={this.axisXLinesArray[i]} key={Math.random()} stroke={this.props.axisLinesColor} opacity={this.props.axisLinesOpacity} strokeWidth={this.props.axisLinesWidth}/> ) } } renderAxisYLines(i){ if (this.props.renderAxisYLines) { return ( <Path d={this.axisYLinesArray[i]} key={Math.random()} stroke={this.props.axisLinesColor} opacity={this.props.axisLinesOpacity} strokeWidth={this.props.axisLinesWidth}/> ) } } renderXAxe(){ if (!this.props.hideXAxe) { let pathX = 'M ' + this.OX + ' ' + this.OY + ' ' + this.maxOX + ' ' + this.OY return ( <Path d={pathX} stroke={this.props.axisColor} opacity={this.props.axisOpacity} strokeWidth={this.props.axisStrokeWidth}/> ) } } renderYAxe(){ if (!this.props.hideYAxe) { let pathY = 'M ' + this.OX + ' ' + this.OY + ' ' + this.OX + ' ' + this.maxOY return ( <Path d={pathY} stroke={this.props.axisColor} opacity={this.props.axisOpacity} strokeWidth={this.props.axisStrokeWidth}/> ) } } x(data){ return this.OX + data * this.Xwidth + this.Xwidth / 2 } y(data){ return this.OY - data * this.Ywidth / 2 } renderText(text, x, y, color, opacity){ if (Number(text) === text && text % 1 !== 0) { text = text.toFixed(1) } return ( <Text x={x} y={y} key={text + x + y} stroke={color} fill={color} opacity={opacity} textAnchor="middle">{text}</Text> ) } getAxisXData(){ let axisXData = [] for (var i = 0; i < this.data[0].chart.length; i++) { axisXData.push(this.data[0].chart[i].x.toString()) } return axisXData } resetVariables(){ this.axisXSectorsCoordinates = null this.axisXLinesCoordinates = null this.axisYLinesCoordinates = null this.axisXTextCoordinates = null this.axisYTextCoordinates = null this.renderAxisXSector = false } } Chart.defaultProps = { height: 150, backgroundColor: 'white', stepsOY: 4, axisTextColor: 'black', axisTextOpacity: 1, axisColor: 'black', axisOpacity: 1, axisLinesOpacity: 1, axisLinesWidth: 1, axisStrokeWidth: 2, axisLinesColor: 'black', scaleXAxis: 5.5, renderAxisXLines: true, renderAxisYLines: true, leftPanelWidth: 50, leftPanelBG: 'white', renderBlur: true, gradientOpacityStart: '0.0', gradientOpacityEnd: '0.55', } Chart.propTypes = { data: React.PropTypes.array.isRequired, height: React.PropTypes.number, width: React.PropTypes.number, backgroundColor: React.PropTypes.any, gradientOpacityStart: React.PropTypes.string, gradientOpacityEnd: React.PropTypes.string, axisTextColor: React.PropTypes.any, axisTextOpacity: React.PropTypes.number, activeAxisXTextArray: React.PropTypes.array, // you can set unique text color for special OX axis values axisTextColorActive: React.PropTypes.any, //there is this color hideXAxe: React.PropTypes.bool, hideYAxe: React.PropTypes.bool, axisStrokeWidth: React.PropTypes.number, axisColor: React.PropTypes.any, axisOpacity: React.PropTypes.number, pointRadius: React.PropTypes.number, // chart point radius axisLinesColor: React.PropTypes.string, // horizontal and vertical chart lines axisLinesOpacity: React.PropTypes.number, axisLinesWidth: React.PropTypes.number, renderAxisXSectors: React.PropTypes.bool, axisXSectorsColor: React.PropTypes.string, axisXSectorsOpacity: React.PropTypes.number, renderAxisXLines: React.PropTypes.bool, // vertical lines renderAxisYLines: React.PropTypes.bool, //horizontal lines leftPanelBG: React.PropTypes.any, // OY axis text backgroundColor renderBlur: React.PropTypes.bool, // IOS only blured OY axis text blurProps: React.PropTypes.any, leftPanelWidth: React.PropTypes.number, stepsOY: React.PropTypes.number, // number of section on axis Y scaleXAxis: React.PropTypes.number, // define chart condensation maxYValue: React.PropTypes.number, // set max OY value that can be rendered }