d3.js - 如何“平滑” D3.js 折线图?(边缘非常锯齿)
问题描述
我有一个我非常满意的 D3 折线图,但我正在努力寻找一种方法来“平滑”折线图。(欢迎您在bravos.co上查看生产中的图表。
线条有尖锐的点,特别是当时间序列波动或快速上升和下降时。
有什么方法可以对其应用“平滑”效果,或者其他一些使线条看起来不那么“锯齿状”的方法?
(我很抱歉,通常我会把它放在 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)} )
}
解决方案
推荐阅读
- python - 使用python中的相对路径寻址到上层目录
- r - 未能在 R 中使用 ggmap 和 Google Geocoding API 进行地理编码;在公司防火墙后面
- r - 在R中如何转义变量中的单个',然后将其发送到sqlQuery
- c# - 如何根据组合框wpf中的用户输入显示数据
- error-handling - 使用规划器运行问题文件时出现“未声明的要求”错误
- ruby-on-rails - 自动为用户设置密码 - 无需设计
- php - 将缺货库存状态添加到 Woocommerce 可变产品下拉列表
- ruby-on-rails - 收到证书后意外生成新的 openssl 密钥
- c - Linux内核:定义内部调用'jiffies'的函数
- react-redux - Redux 状态已更改,但组件 props 未更新