如何“平滑” D3.js折线图? (边缘非常锯齿状)

时间:2018-12-23 20:24:08

标签: d3.js

我对D3折线图总体上感到非常满意,但我一直在努力寻找一种“平滑”折线图的方法。 (欢迎您在bravos.co上查看productio中的图表。

这些线有尖点,尤其是在时间序列不稳定或快速上升或下降时。

有什么方法可以对其应用“平滑”效果,或者是否可以通过其他方法使线条看起来不那么“锯齿”?

(很抱歉,通常我会把它放在Fiddle中,除非有许多依赖项和API调用。)谢谢!!!

D3 charts have jagged edges

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)} )
}

0 个答案:

没有答案