From dd46944488927d09e35d6cf0fea7dab3fc10b2b4 Mon Sep 17 00:00:00 2001 From: k-yokoishi Date: Tue, 16 Apr 2019 23:26:50 +0900 Subject: [PATCH 1/2] Draw BarChart and LineChart from 0 --- src/abstract-chart.js | 16 ++++++++++++++-- src/bar-chart.js | 14 +++++++------- src/line-chart.js | 41 +++++++++++++++++------------------------ 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/abstract-chart.js b/src/abstract-chart.js index d7965e31..a0f460b5 100644 --- a/src/abstract-chart.js +++ b/src/abstract-chart.js @@ -3,7 +3,19 @@ import React, {Component} from 'react' import {LinearGradient, Line, Text, Defs, Stop} from 'react-native-svg' class AbstractChart extends Component { - calcScaler = data => Math.max(...data) - Math.min(...data) || 1 + calcScaler = data => Math.max(...data, 0) - Math.min(...data, 0) || 1 + + calcBaseHeight = (data, height) => { + const min = Math.min(...data) + const max = Math.max(...data) + if (min >= 0 && max >= 0) { + return height + } else if (min < 0 && max <= 0) { + return 0 + } else if (min < 0 && max > 0) { + return height * max / this.calcScaler(data) + } + } renderHorizontalLines = config => { const {count, width, height, paddingTop, paddingRight} = config @@ -58,7 +70,7 @@ class AbstractChart extends Component { yLabel = `${yAxisLabel}${data[0].toFixed(decimalPlaces)}` } else { const label = - (this.calcScaler(data) / (count - 1)) * i + Math.min(...data) + (this.calcScaler(data) / (count - 1)) * i + Math.min(...data, 0) yLabel = `${yAxisLabel}${label.toFixed(decimalPlaces)}` } diff --git a/src/bar-chart.js b/src/bar-chart.js index 92a597f9..115c2ad6 100644 --- a/src/bar-chart.js +++ b/src/bar-chart.js @@ -8,9 +8,9 @@ const barWidth = 32 class BarChart extends AbstractChart { renderBars = config => { const {data, width, height, paddingTop, paddingRight} = config - const baseHeight = (height / 4 * 3) * (Math.max(...data) / this.calcScaler(data)) + paddingTop + const baseHeight = this.calcBaseHeight(data, height) return data.map((x, i) => { - const barHeight = height / 4 * 3 * (x / this.calcScaler(data)) + const barHeight = height * (x / this.calcScaler(data)) const barWidth = 32 return ( ) @@ -31,9 +31,9 @@ class BarChart extends AbstractChart { renderBarTops = config => { const {data, width, height, paddingTop, paddingRight} = config - const baseHeight = (height / 4 * 3) * (Math.max(...data) / this.calcScaler(data)) + paddingTop + const baseHeight = this.calcBaseHeight(data, height) return data.map((x, i) => { - const barHeight = height / 4 * 3 * (x / this.calcScaler(data)) + const barHeight = height * (x / this.calcScaler(data)) return ( { output.push( - paddingRight + - (i * (width - paddingRight)) / dataset.data.length + - ',' + - ((height / 4) * - 3 * - (1 - (x - Math.min(...datas)) / this.calcScaler(datas)) + - paddingTop) + (d, i) => { + const x = paddingRight + (i * (width - paddingRight)) / dataset.data.length + const y = (baseHeight - height * (d / this.calcScaler(datas))) / 4 * 3 + paddingTop + return `${x},${y}` + } ) .join(' ') + ` ${paddingRight + @@ -116,16 +114,14 @@ class LineChart extends AbstractChart { 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( - (x, i) => - paddingRight + - (i * (width - paddingRight)) / dataset.data.length + - ',' + - ((height / 4) * - 3 * - (1 - (x - Math.min(...datas)) / this.calcScaler(datas)) + - paddingTop) + (d, i) => { + const x = (i * (width - paddingRight)) / dataset.data.length + paddingRight + const y = (baseHeight - height * (d / this.calcScaler(datas))) / 4 * 3 + paddingTop + return `${x},${y}` + } ) output.push( @@ -153,14 +149,11 @@ class LineChart extends AbstractChart { Math.floor( paddingRight + (i * (width - paddingRight)) / dataset.data.length ) - const y = i => - Math.floor( - (height / 4) * - 3 * - (1 - - (dataset.data[i] - Math.min(...datas)) / this.calcScaler(datas)) + - paddingTop - ) + const baseHeight = this.calcBaseHeight(datas, height) + const y = i => { + const yHeight = height * (dataset.data[i] / this.calcScaler(datas)) + return Math.floor((baseHeight - yHeight) / 4 * 3 + paddingTop) + } return [`M${x(0)},${y(0)}`] .concat( From 1e425a9e3760c6374c3bd3964200637941c65558 Mon Sep 17 00:00:00 2001 From: k-yokoishi Date: Tue, 23 Apr 2019 22:54:35 +0900 Subject: [PATCH 2/2] Add prop `fromZero` Add prop `fromZero` to BarChart and LineChart to render the charts from 0 not from the minimum value. --- README.md | 4 +++- src/abstract-chart.js | 29 ++++++++++++++++++++++++++--- src/bar-chart.js | 4 ++-- src/line-chart.js | 6 +++--- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3e52d44a..60ce098f 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ const data = { | withShadow | boolean | Show shadow for line - default: True | | withInnerLines | boolean | Show inner dashed lines - default: True | | withOuterLines | boolean | Show outer dashed lines - default: True | +| fromZero | boolean | Render charts from 0 not from the minimum value. - default: False | | yAxisLabel | string | Prepend text to horizontal labels -- default: '' | | chartConfig | Object | Configuration object for the chart, see example config object above | |decorator | Function | This function takes a [whole bunch](https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart.js#L266) of stuff and can render extra elements, such as data point info or additional markup. | @@ -197,6 +198,7 @@ const data = { | data | Object | Data for the chart - see example above | | width | Number | Width of the chart, use 'Dimensions' library to get the width of your screen for responsive | | height | Number | Height of the chart | +| fromZero | boolean | Render charts from 0 not from the minimum value. - default: False | | yAxisLabel | string | Prepend text to horizontal labels -- default: '' | | chartConfig | Object | Configuration object for the chart, see example config in the beginning of this file | @@ -210,7 +212,7 @@ const data ={ legend: ['L1', 'L2', 'L3'], data: [ [60, 60, 60], - [30,30,60], + [30,30,60], ], barColors: ['#dfe4ea', '#ced6e0', '#a4b0be'], } diff --git a/src/abstract-chart.js b/src/abstract-chart.js index a0f460b5..1408b984 100644 --- a/src/abstract-chart.js +++ b/src/abstract-chart.js @@ -3,7 +3,13 @@ import React, {Component} from 'react' import {LinearGradient, Line, Text, Defs, Stop} from 'react-native-svg' class AbstractChart extends Component { - calcScaler = data => Math.max(...data, 0) - Math.min(...data, 0) || 1 + calcScaler = data => { + if (this.props.fromZero) { + return Math.max(...data, 0) - Math.min(...data, 0) || 1 + } else { + return Math.max(...data) - Math.min(...data) || 1 + } + } calcBaseHeight = (data, height) => { const min = Math.min(...data) @@ -17,6 +23,22 @@ class AbstractChart extends Component { } } + calcHeight = (val, data, height) => { + const max = Math.max(...data) + const min = Math.min(...data) + if (min < 0 && max > 0) { + return height * (val / this.calcScaler(data)) + } else if (min >= 0 && max >= 0) { + return this.props.fromZero ? + height * (val / this.calcScaler(data)) : + height * ((val - min) / this.calcScaler(data)) + } else if (min < 0 && max <= 0) { + return this.props.fromZero ? + height * (val / this.calcScaler(data)) : + height * ((val - max) / this.calcScaler(data)) + } + } + renderHorizontalLines = config => { const {count, width, height, paddingTop, paddingRight} = config return [...new Array(count)].map((_, i) => { @@ -69,8 +91,9 @@ class AbstractChart extends Component { if (count === 1) { yLabel = `${yAxisLabel}${data[0].toFixed(decimalPlaces)}` } else { - const label = - (this.calcScaler(data) / (count - 1)) * i + Math.min(...data, 0) + const label = this.props.fromZero ? + (this.calcScaler(data) / (count - 1)) * i + Math.min(...data, 0) : + (this.calcScaler(data) / (count - 1)) * i + Math.min(...data) yLabel = `${yAxisLabel}${label.toFixed(decimalPlaces)}` } diff --git a/src/bar-chart.js b/src/bar-chart.js index 115c2ad6..0efd4058 100644 --- a/src/bar-chart.js +++ b/src/bar-chart.js @@ -10,7 +10,7 @@ class BarChart extends AbstractChart { const {data, width, height, paddingTop, paddingRight} = config const baseHeight = this.calcBaseHeight(data, height) return data.map((x, i) => { - const barHeight = height * (x / this.calcScaler(data)) + const barHeight = this.calcHeight(x, data, height) const barWidth = 32 return ( { - const barHeight = height * (x / this.calcScaler(data)) + const barHeight = this.calcHeight(x, data, height) return ( { const x = paddingRight + (i * (width - paddingRight)) / dataset.data.length - const y = (baseHeight - height * (d / this.calcScaler(datas))) / 4 * 3 + paddingTop + const y = (baseHeight - this.calcHeight(d, datas, height)) / 4 * 3 + paddingTop return `${x},${y}` } ) @@ -119,7 +119,7 @@ class LineChart extends AbstractChart { const points = dataset.data.map( (d, i) => { const x = (i * (width - paddingRight)) / dataset.data.length + paddingRight - const y = (baseHeight - height * (d / this.calcScaler(datas))) / 4 * 3 + paddingTop + const y = (baseHeight - this.calcHeight(d, datas, height)) / 4 * 3 + paddingTop return `${x},${y}` } ) @@ -151,7 +151,7 @@ class LineChart extends AbstractChart { ) const baseHeight = this.calcBaseHeight(datas, height) const y = i => { - const yHeight = height * (dataset.data[i] / this.calcScaler(datas)) + const yHeight = this.calcHeight(dataset.data[i], datas, height) return Math.floor((baseHeight - yHeight) / 4 * 3 + paddingTop) }