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 d7965e31..1408b984 100644 --- a/src/abstract-chart.js +++ b/src/abstract-chart.js @@ -3,7 +3,41 @@ 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 => { + 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) + 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) + } + } + + 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 @@ -57,7 +91,8 @@ class AbstractChart extends Component { if (count === 1) { yLabel = `${yAxisLabel}${data[0].toFixed(decimalPlaces)}` } else { - const label = + 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 92a597f9..0efd4058 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 = this.calcHeight(x, data, height) 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 = this.calcHeight(x, data, height) 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 - this.calcHeight(d, datas, height)) / 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 - this.calcHeight(d, datas, height)) / 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 = this.calcHeight(dataset.data[i], datas, height) + return Math.floor((baseHeight - yHeight) / 4 * 3 + paddingTop) + } return [`M${x(0)},${y(0)}`] .concat(