UNPKG

react-native-chart-kit

Version:

If you're looking to **build a website or a cross-platform mobile app** – we will be happy to help you! Send a note to clients@ui1.io and we will be in touch with you shortly.

350 lines (330 loc) 9.48 kB
import React from 'react' import {View} from 'react-native' import {Svg, Circle, Polygon, Polyline, Path, Rect, G} from 'react-native-svg' import AbstractChart from './abstract-chart' class LineChart extends AbstractChart { getColor = (dataset, opacity) => { return (dataset.color || this.props.chartConfig.color)(opacity) } getStrokeWidth = dataset => { return dataset.strokeWidth || this.props.chartConfig.strokeWidth || 3 } getDatas = data => data.reduce((acc, item) => (item.data ? [...acc, ...item.data] : acc), []) renderDots = config => { const { data, width, height, paddingTop, paddingRight, onDataPointClick } = config const output = [] const datas = this.getDatas(data) data.map((dataset, index) => { dataset.data.map((x, i) => { const cx = paddingRight + (i * (width - paddingRight)) / dataset.data.length const cy = (height / 4) * 3 * (1 - (x - Math.min(...datas)) / this.calcScaler(datas)) + paddingTop const onPress = () => { if (!onDataPointClick) { return } onDataPointClick({ value: x, dataset, getColor: opacity => this.getColor(dataset, opacity) }) } output.push( <Circle key={Math.random()} cx={cx} cy={cy} r="4" fill={this.getColor(dataset, 0.9)} onPress={onPress} />, <Circle key={Math.random()} cx={cx} cy={cy} r="12" fill="#fff" fillOpacity={0} onPress={onPress} /> ) }) }) return output } renderShadow = config => { if (this.props.bezier) { return this.renderBezierShadow(config) } const {data, width, height, paddingRight, paddingTop} = config const output = [] const datas = this.getDatas(data) const baseHeight = this.calcBaseHeight(datas, height) config.data.map((dataset, index) => { output.push( <Polygon key={index} points={ dataset.data .map( (d, i) => { const x = paddingRight + (i * (width - paddingRight)) / dataset.data.length const y = (baseHeight - this.calcHeight(d, datas, height)) / 4 * 3 + paddingTop return `${x},${y}` } ) .join(' ') + ` ${paddingRight + ((width - paddingRight) / dataset.data.length) * (dataset.data.length - 1)},${(height / 4) * 3 + paddingTop} ${paddingRight},${(height / 4) * 3 + paddingTop}` } fill="url(#fillShadowGradient)" strokeWidth={0} /> ) }) return output } renderLine = config => { if (this.props.bezier) { return this.renderBezierLine(config) } const {width, height, paddingRight, paddingTop, data} = config const output = [] const datas = this.getDatas(data) const baseHeight = this.calcBaseHeight(datas, height) data.forEach((dataset, index) => { const points = dataset.data.map( (d, i) => { const x = (i * (width - paddingRight)) / dataset.data.length + paddingRight const y = (baseHeight - this.calcHeight(d, datas, height)) / 4 * 3 + paddingTop return `${x},${y}` } ) output.push( <Polyline key={index} points={points.join(' ')} fill="none" stroke={this.getColor(dataset, 0.2)} strokeWidth={this.getStrokeWidth(dataset)} /> ) }) return output } getBezierLinePoints = (dataset, config) => { const {width, height, paddingRight, paddingTop, data} = config if (dataset.data.length === 0) { return 'M0,0' } const datas = this.getDatas(data) const x = i => Math.floor( paddingRight + (i * (width - paddingRight)) / dataset.data.length ) const baseHeight = this.calcBaseHeight(datas, height) const y = i => { const yHeight = this.calcHeight(dataset.data[i], datas, height) return Math.floor((baseHeight - yHeight) / 4 * 3 + paddingTop) } return [`M${x(0)},${y(0)}`] .concat( dataset.data.slice(0, -1).map((_, i) => { const x_mid = (x(i) + x(i + 1)) / 2 const y_mid = (y(i) + y(i + 1)) / 2 const cp_x1 = (x_mid + x(i)) / 2 const cp_x2 = (x_mid + x(i + 1)) / 2 return ( `Q ${cp_x1}, ${y(i)}, ${x_mid}, ${y_mid}` + ` Q ${cp_x2}, ${y(i + 1)}, ${x(i + 1)}, ${y(i + 1)}` ) }) ) .join(' ') } renderBezierLine = config => { const output = [] config.data.map((dataset, index) => { const result = this.getBezierLinePoints(dataset, config) output.push( <Path key={index} d={result} fill="none" stroke={this.getColor(dataset, 0.2)} strokeWidth={this.getStrokeWidth(dataset)} /> ) }) return output } renderBezierShadow = config => { const {width, height, paddingRight, paddingTop, data} = config const output = [] data.map((dataset, index) => { const d = this.getBezierLinePoints(dataset, config) + ` L${paddingRight + ((width - paddingRight) / dataset.data.length) * (dataset.data.length - 1)},${(height / 4) * 3 + paddingTop} L${paddingRight},${(height / 4) * 3 + paddingTop} Z` output.push( <Path key={index} d={d} fill="url(#fillShadowGradient)" strokeWidth={0} /> ) }) return output } render() { const paddingTop = 16 const paddingRight = 64 const { width, height, data, withShadow = true, withDots = true, withInnerLines = true, withOuterLines = true, withHorizontalLabels = true, withVerticalLabels = true, style = {}, decorator, onDataPointClick } = this.props const {labels = []} = data const {borderRadius = 0} = style const config = { width, height } const datas = this.getDatas(data.datasets) return ( <View style={style}> <Svg height={height} width={width}> <G> {this.renderDefs({ ...config, ...this.props.chartConfig })} <Rect width="100%" height={height} rx={borderRadius} ry={borderRadius} fill="url(#backgroundGradient)" /> <G> {withInnerLines ? this.renderHorizontalLines({ ...config, count: 4, paddingTop, paddingRight }) : withOuterLines ? this.renderHorizontalLine({ ...config, paddingTop, paddingRight }) : null} </G> <G> {withHorizontalLabels ? this.renderHorizontalLabels({ ...config, count: Math.min(...datas) === Math.max(...datas) ? 1 : 4, data: datas, paddingTop, paddingRight }) : null} </G> <G> {withInnerLines ? this.renderVerticalLines({ ...config, data: data.datasets[0].data, paddingTop, paddingRight }) : withOuterLines ? this.renderVerticalLine({ ...config, paddingTop, paddingRight }) : null} </G> <G> {withVerticalLabels ? this.renderVerticalLabels({ ...config, labels, paddingRight, paddingTop }) : null} </G> <G> {this.renderLine({ ...config, paddingRight, paddingTop, data: data.datasets })} </G> <G> {withShadow && this.renderShadow({ ...config, data: data.datasets, paddingRight, paddingTop })} </G> <G> {withDots && this.renderDots({ ...config, data: data.datasets, paddingTop, paddingRight, onDataPointClick })} </G> <G> {decorator && decorator({ ...config, data: data.datasets, paddingTop, paddingRight })} </G> </G> </Svg> </View> ) } } export default LineChart