我对D3折线图总体上感到非常满意,但我一直在努力寻找一种“平滑”折线图的方法。 (欢迎您在bravos.co上查看productio中的图表。
这些线有尖点,尤其是在时间序列不稳定或快速上升或下降时。
有什么方法可以对其应用“平滑”效果,或者是否可以通过其他方法使线条看起来不那么“锯齿”?
(很抱歉,通常我会把它放在Fiddle中,除非有许多依赖项和API调用。)谢谢!!!
constructor(props) {
super(props)
this.state = {
id: this.props.id,
change: this.props.change,
style: {},
showTooltip: false,
label: '',
value: '',
}
this.chartWidth = this.props.width || CHART_WIDTH
this.chartHeight = this.props.height || CHART_HEIGHT
this._ref = null
this._svg = null
this._chartData = props.chart
// prevent crashes when no chart data is provided
if (!this._chartData || this._chartData.length===0) this._chartData = []
// prepare chart data
const { previousClose } = props
let value = null
let item = null
let tmpValue = null
// normalize the data
for(let i=0; i < this._chartData.length; i++) {
item = this._chartData[i]
// detect crypto vs public
item.time = item.minute ? parseTime(item.minute) : item.timestamp
value = item.marketOpen || item.price
if (value && value !== 0 && value !== -1) {
item.marketOpen = value
tmpValue = value
} else if (tmpValue) {
item.marketOpen = tmpValue
} else {
item.marketOpen = null
}
}
// filer out null values (corrupted data)
this._chartData = this._chartData.filter((item) => (item.marketOpen !== null))
// define min/max values for tooltip info
let minValue = {disabled:true, marketOpen: this._chartData[0] && this._chartData[0].marketOpen}
let maxValue = {disabled:true, marketOpen: this._chartData[0] && this._chartData[0].marketOpen}
for(let i=0; i < this._chartData.length; i++) {
item = this._chartData[i]
if (item.marketOpen < minValue.marketOpen) minValue = item
if (item.marketOpen > maxValue.marketOpen) maxValue = item
}
// define _keyPointsData array for tooltip info
this._keyPointsData = []
if (this._chartData.length>0) {
this._keyPointsData.push(this._chartData[0])
this._keyPointsData[this._keyPointsData.length-1].tooltipLabel = 'Open'
}
if (this._chartData.length>1) {
this._keyPointsData.push(this._chartData[this._chartData.length-1])
this._keyPointsData[this._keyPointsData.length-1].tooltipLabel = 'Close'
}
if (this._chartData.length>3 && !minValue.disabled) {
this._keyPointsData.push(minValue)
this._keyPointsData[this._keyPointsData.length-1].tooltipLabel = 'Low'
}
if (this._chartData.length>3 && !maxValue.disabled) {
this._keyPointsData.push(maxValue)
this._keyPointsData[this._keyPointsData.length-1].tooltipLabel = 'High'
}
// calculate variable graph height
let interpolatedChartHeight = this.chartHeight
if (!this.props.crypto) { // use interpolated Y scale
interpolatedChartHeight = Math.min( Math.pow(Math.abs(this.props.changePercent*4),(1/3)), 1) * this.chartHeight
} else {
interpolatedChartHeight = Math.min( Math.pow(Math.abs(this.props.changePercent*4),(1/3)), 1) * this.chartHeight
}
let interpolatedChartWidth = this.chartWidth
if (!this.props.crypto) {
interpolatedChartWidth = this.chartWidth * this._chartData.length/X_INTERPOLATION_TOTAL
}
this._interpolatedChartHeight = interpolatedChartHeight
this._interpolatedChartWidth = interpolatedChartWidth
// prepare data domain
let added = false
if (this._chartData[0]) {
// add to domain for proper prev close line placement
added = true
this._chartData.unshift({time:this._chartData[0].time, marketOpen:previousClose})
}
this.xDomain = d3.extent(this._chartData, d => d.time)
this.yDomain = d3.extent(this._chartData, d => d.marketOpen)
if (added) this._chartData.shift() // remove it so it's not showing on the chart
// define data scale
this.xScale = d3.scaleTime()
.rangeRound([0, interpolatedChartWidth])
.domain(this.xDomain)
this.yScale = d3.scaleLinear()
.rangeRound([interpolatedChartHeight, 0])
.domain(this.yDomain)
// define line function
this.line = d3.line()
.x(d => this.xScale(d.time) )
.y(d => this.yScale(d.marketOpen) )
// define area function
this.area = d3.area()
.x(d => this.xScale(d.time) )
.y0(d => this.yScale(d.marketOpen >= previousClose ? d.marketOpen : previousClose) )
.y1(d => this.yScale(d.marketOpen <= previousClose ? d.marketOpen : previousClose) )
// store chart scaled coordinates for easy tooltip generation
this._keyPointsData.forEach(d => d.pos={x: this.xScale(d.time), y: this.yScale(d.marketOpen)} )
}