You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在dc.js的SeriesChart中添加水平基线?

dc.js SeriesChart 添加水平基线问题修复

问题背景

已通过代码实现dc.js的SeriesChart,图表运行正常。需在y轴0.5数值位置添加黑色水平基线,不想修改数据源。转换垂直基线方案后出现两个问题:

  • 线条起始位置未对齐y轴
  • 线条绘制在0.51而非0.50位置

基于相关方案封装了工具函数,可在pretransition事件中调用。

原代码示例

'use strict';

var sectorChart = new dc.SeriesChart('#test-chart');

d3.json('php/query.php?option=sectors').then(data => {
    const dateFormatSpecifier = '%Y-%m-%d';
    const dateFormat = d3.timeFormat(dateFormatSpecifier);
    const dateFormatParser = d3.timeParse(dateFormatSpecifier);
    const numberFormat = d3.format('.2f');
    var minDate = new Date();
    var maxDate = new Date();
    var minRatio = .5
    var maxRatio = .5
    data.forEach(d => {
        d.dd = dateFormatParser(d.date);
        d.ratio = +d.ratio; // 转换为数字
        if (d.dd < minDate ) minDate = d.dd;
        if (d.dd > maxDate ) maxDate = d.dd;
        if (d.ratio < minRatio ) minRatio = d.ratio
        if (d.ratio > maxRatio ) maxRatio = d.ratio
    });
    
    const ndx = crossfilter(data);
    const all = ndx.groupAll();

    // 维度是行业和日期的数组,后续用日期作为x轴,行业作为标签
    const dateDimension = ndx.dimension(d => [d.name, d.dd]);
    // 按平均值分组,作为绘图值
    var avgGroup = dateDimension.group().reduceSum(d => d.average);
    
    sectorChart
        .width(990)
        .height(500)
        .chart(c => new dc.LineChart(c))
        .x(d3.scaleTime().domain([minDate, maxDate]))
        .y(d3.scaleLinear().domain([minRatio, maxRatio]))
        .margins({top: 30, right: 10, bottom: 20, left: 40})
        .brushOn(false)
        .yAxisLabel("ratio avg 10")
        .renderHorizontalGridLines(true)
        .dimension(dateDimension)
        .group(avgGroup)
        .seriesAccessor(d => d.key[0])
        .keyAccessor(d => d.key[1])
        .valueAccessor(d => +d.value)
        .legend(dc.legend().x(450).y(15).itemHeight(13).gap(5).horizontal(1).legendWidth(340).autoItemWidth(true));        
    dc.renderAll();
});

原工具函数

/**
    * 在dc.js图表中绘制固定的水平或垂直线
    * 需要在"pretransition"事件中调用
    * @example
    * .on('pretransition', function (chart) {
    *   dcUtilFixedLine(chart, true, 0.5, [{attr: 'stroke', value: 'red'}] )
    *  }
    * @param (Chart} [chart] 事件触发时传入的图表实例
    * @param (Boolean) [horizontal] true表示水平线,false表示垂直线
    * @param (Object) [value] 线条所在位置的数值,垂直线对应维度值,水平线对应度量值
    * @param (Array) [attributes] 包含attr/value键值对的对象数组,用于设置线条属性,默认黑色、1px宽度
    */
 function dcUtilFixedLine(chart, horizontal, value, attributes ) {
    if (horizontal) {
        var extra_data = [
           {y: chart.y()(value) + chart.margins().top, x: chart.margins().left},
           {y: chart.y()(value) + chart.margins().top, x: chart.margins().left + chart.effectiveWidth()}
       ]
    } else {
        var extra_data = [
            {x: chart.x()(value) + chart.margins().left, y: chart.margins().top},
            {x: chart.x()(value) + chart.margins().left, y: chart.margins().top + chart.effectiveHeight()}
        ]
    }
    var addedPath = 'extraLine'  + Math.random().toString().substr(2, 8)
    var line = d3.line().x(d => d.x ).y(d => d.y );
    var chartBody = chart.select('g');
    var path = chartBody.selectAll('path.'+addedPath).data([extra_data]);
            
    path = path.enter()
            .append('path')
            .attr('class', addedPath)
            .attr('id', 'oeLine')
            .attr('stroke', 'black')
            .merge(path);
    
    attributes.forEach( attribute => path.attr(attribute.attr, attribute.value))
            
    path.attr('d', line);
 }

问题修复及修正后的工具函数

问题原因分析

  1. 线条未对齐y轴:原代码错误地给坐标添加了margin偏移,而SeriesChart的绘图区域<g.chart-body>已经包含了margin的位移,重复添加导致偏移。
  2. 线条位置偏移:使用chart.y()获取的是顶层配置的比例尺,而非子图表实际使用的y轴比例尺,导致计算出的位置不准确。

修正后的工具函数

/**
 * 在dc.js图表中绘制固定的水平或垂直线
 * 需要在"pretransition"事件中调用
 * @example
 * .on('pretransition', function (chart) {
 *   dcUtilFixedLine(chart, true, 0.5, [{attr: 'stroke', value: 'red'}])
 * })
 * @param {Chart} chart 事件触发时传入的图表实例
 * @param {Boolean} horizontal true表示水平线,false表示垂直线
 * @param {Object} value 线条所在位置的数值,垂直线对应维度值,水平线对应度量值
 * @param {Array} attributes 可选,包含attr/value键值对的对象数组,用于设置线条属性,默认黑色、1px宽度
 */
function dcUtilFixedLine(chart, horizontal, value, attributes) {
    // 获取实际使用的轴比例尺
    var scale = horizontal ? chart.yAxis().scale() : chart.xAxis().scale();
    var extra_data;

    if (horizontal) {
        var yPos = scale(value);
        // 绘图区域内坐标从0开始,无需添加margin偏移
        extra_data = [
            {y: yPos, x: 0},
            {y: yPos, x: chart.effectiveWidth()}
        ];
    } else {
        var xPos = scale(value);
        extra_data = [
            {x: xPos, y: 0},
            {x: xPos, y: chart.effectiveHeight()}
        ];
    }

    var addedPath = 'extraLine' + Math.random().toString().substr(2, 8);
    var line = d3.line().x(d => d.x).y(d => d.y);
    // 选择实际绘图区域的g元素
    var chartBody = chart.select('g.chart-body');
    var path = chartBody.selectAll('path.' + addedPath).data([extra_data]);

    path = path.enter()
        .append('path')
        .attr('class', addedPath)
        .attr('id', 'oeLine')
        .attr('stroke', 'black')
        .attr('stroke-width', 1)
        .merge(path);

    // 处理自定义属性
    if (attributes && Array.isArray(attributes)) {
        attributes.forEach(attribute => path.attr(attribute.attr, attribute.value));
    }

    path.attr('d', line);
}

使用方式

在SeriesChart的配置中添加pretransition事件监听:

sectorChart
    // ... 其他原有配置
    .on('pretransition', function(chart) {
        // 绘制y=0.5的黑色水平基线
        dcUtilFixedLine(chart, true, 0.5, [{attr: 'stroke', value: 'black'}, {attr: 'stroke-width', value: 1}]);
    });

内容的提问来源于stack exchange,提问作者Julen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 07:54:18