【问题标题】:Is there a way alter the color lines on D3.js line chart?有没有办法改变 D3.js 折线图上的颜色线?
【发布时间】:2021-03-03 18:26:04
【问题描述】:

D3.js 相当新,但热衷于更新我当前工作版本的配色方案,颜色:steelblue.css 文件中使用,但似乎被其他地方覆盖,知道如何最好解决这个问题? (如您所见,我已尝试更新为粉红色)

.chart-wrapper .line {
  fill: none;
  stroke: pink;
  stroke-width: 5px;
}
  // Render the chart
  chartObj.render = function () {
    //Create SVG element
    chartObj.svg = chartObj.chartDiv
      .append('svg')
      .attr('class', 'chart-area')
      .attr(
        'width',
        chartObj.width + (chartObj.margin.left + chartObj.margin.right)
      )
      .attr(
        'height',
        chartObj.height + (chartObj.margin.top + chartObj.margin.bottom)
      )
      .append('g')
      .attr(
        'transform',
        'translate(' + chartObj.margin.left + ',' + chartObj.margin.top + ')'
      );
    // Draw Lines
    for (var y in yObjs) {
      yObjs[y].path = chartObj.svg
        .append('path')
        .datum(chartObj.data)
        .attr('class', 'line')
        .attr('d', yObjs[y].line)
        .style('stroke', color(y))
        .attr('data-series', y)
        .on('mouseover', function () {
          focus.style('display', null);
        })
        .on('mouseout', function () {
          focus.transition().delay(700).style('display', 'none');
        })
        .on('mousemove', mousemove);
    }
  return chartObj;
}

【问题讨论】:

  • chartObj.svg 上的 .style() 调用将 color(y) 应用于 stroke 属性。颜色函数在哪里定义?这将为该行定义一个内联样式设置。
  • 所以——正如约翰所指出的——你也可以删除.style('stroke', color(y))这一行,然后应用css样式
  • 对不起,我无法添加更多的 JS 代码 - 我可以更新 .style('stroke', 'pink')pink 类只是更新了笔触颜色,但它会影响两条线,你知道我该怎么做吗?区分它们?我已经将一个小提琴与我的 JS 相关联(抱歉,图表无法使用,因为我无法添加 CSV)感谢您的帮助!jsfiddle.net/simoncunningham/bzm5ftL0

标签: javascript d3.js data-visualization


【解决方案1】:

您的代码似乎基于此示例 here,如果您是 d3 新手,这是一个非常深入的起点。

我已经修改了代码以利用配置对象中指定的颜色,这似乎是由示例作者提出但未实现的。

以前颜色是根据内置的d3.scale.category10 颜色生成的,但现在已删除。

此示例代码还使用 d3 版本 3,这可能意味着它不是一个很好的起点,因为较新的版本提供了许多 API 改进。如果您有兴趣了解有关 d3 配色方案的更多信息,请参阅this article here

function makeLineChart(dataset, xName, yObjs, axisLables) {
    var chartObj = {};
    var color = d3.scale.category10();
    chartObj.xAxisLable = axisLables.xAxis;
    chartObj.yAxisLable = axisLables.yAxis;
    /*
     yObjsects format:
     {y1:{column:'',name:'name',color:'color'},y2}
     */

    chartObj.data = dataset;
    chartObj.margin = {top: 15, right: 60, bottom: 30, left: 50};
    chartObj.width = 650 - chartObj.margin.left - chartObj.margin.right;
    chartObj.height = 480 - chartObj.margin.top - chartObj.margin.bottom;

// So we can pass the x and y as strings when creating the function
    chartObj.xFunct = function(d){return d[xName]};

// For each yObjs argument, create a yFunction
    function getYFn(column) {
        return function (d) {
            return d[column];
        };
    }

// Object instead of array
    chartObj.yFuncts = [];
    for (var y  in yObjs) {
        yObjs[y].name = y;
        yObjs[y].yFunct = getYFn(yObjs[y].column); //Need this  list for the ymax function
        chartObj.yFuncts.push(yObjs[y].yFunct);
        yObjs[y].color = d3.rgb(yObjs[y].color);
    }

//Formatter functions for the axes
    chartObj.formatAsNumber = d3.format(".0f");
    chartObj.formatAsDecimal = d3.format(".2f");
    chartObj.formatAsCurrency = d3.format("$.2f");
    chartObj.formatAsFloat = function (d) {
        if (d % 1 !== 0) {
            return d3.format(".2f")(d);
        } else {
            return d3.format(".0f")(d);
        }
        
    };

    chartObj.xFormatter = chartObj.formatAsNumber;
    chartObj.yFormatter = chartObj.formatAsFloat;

    chartObj.bisectYear = d3.bisector(chartObj.xFunct).left; //< Can be overridden in definition

//Create scale functions
    chartObj.xScale = d3.scale.linear().range([0, chartObj.width]).domain(d3.extent(chartObj.data, chartObj.xFunct)); //< Can be overridden in definition

// Get the max of every yFunct
    chartObj.max = function (fn) {
        return d3.max(chartObj.data, fn);
    };
    chartObj.yScale = d3.scale.linear().range([chartObj.height, 0]).domain([0, d3.max(chartObj.yFuncts.map(chartObj.max))]);

    chartObj.formatAsYear = d3.format("");

//Create axis
    chartObj.xAxis = d3.svg.axis().scale(chartObj.xScale).orient("bottom").tickFormat(chartObj.xFormatter); //< Can be overridden in definition

    chartObj.yAxis = d3.svg.axis().scale(chartObj.yScale).orient("left").tickFormat(chartObj.yFormatter); //< Can be overridden in definition


// Build line building functions
    function getYScaleFn(yObj) {
        return function (d) {
            return chartObj.yScale(yObjs[yObj].yFunct(d));
        };
    }
    for (var yObj in yObjs) {
        yObjs[yObj].line = d3.svg.line().interpolate("cardinal").x(function (d) {
            return chartObj.xScale(chartObj.xFunct(d));
        }).y(getYScaleFn(yObj));
    }
    

    chartObj.svg;

// Change chart size according to window size
    chartObj.update_svg_size = function () {
        chartObj.width = parseInt(chartObj.chartDiv.style("width"), 10) - (chartObj.margin.left + chartObj.margin.right);

        chartObj.height = parseInt(chartObj.chartDiv.style("height"), 10) - (chartObj.margin.top + chartObj.margin.bottom);

        /* Update the range of the scale with new width/height */
        chartObj.xScale.range([0, chartObj.width]);
        chartObj.yScale.range([chartObj.height, 0]);

        if (!chartObj.svg) {return false;}

        /* Else Update the axis with the new scale */
        chartObj.svg.select('.x.axis').attr("transform", "translate(0," + chartObj.height + ")").call(chartObj.xAxis);
        chartObj.svg.select('.x.axis .label').attr("x", chartObj.width / 2);

        chartObj.svg.select('.y.axis').call(chartObj.yAxis);
        chartObj.svg.select('.y.axis .label').attr("x", -chartObj.height / 2);

        /* Force D3 to recalculate and update the line */
        for (var y  in yObjs) {
            yObjs[y].path.attr("d", yObjs[y].line);
        }
        

        d3.selectAll(".focus.line").attr("y2", chartObj.height);

        chartObj.chartDiv.select('svg').attr("width", chartObj.width + (chartObj.margin.left + chartObj.margin.right)).attr("height", chartObj.height + (chartObj.margin.top + chartObj.margin.bottom));

        chartObj.svg.select(".overlay").attr("width", chartObj.width).attr("height", chartObj.height);
        return chartObj;
    };

    chartObj.bind = function (selector) {
        chartObj.mainDiv = d3.select(selector);
        // Add all the divs to make it centered and responsive
        chartObj.mainDiv.append("div").attr("class", "inner-wrapper").append("div").attr("class", "outer-box").append("div").attr("class", "inner-box");
        chartSelector = selector + " .inner-box";
        chartObj.chartDiv = d3.select(chartSelector);
        d3.select(window).on('resize.' + chartSelector, chartObj.update_svg_size);
        chartObj.update_svg_size();
        return chartObj;
    };

// Render the chart
    chartObj.render = function () {
        //Create SVG element
        chartObj.svg = chartObj.chartDiv.append("svg").attr("class", "chart-area").attr("width", chartObj.width + (chartObj.margin.left + chartObj.margin.right)).attr("height", chartObj.height + (chartObj.margin.top + chartObj.margin.bottom)).append("g").attr("transform", "translate(" + chartObj.margin.left + "," + chartObj.margin.top + ")");

        // Draw Lines
        for (var y  in yObjs) {
            yObjs[y].path = chartObj.svg.append("path").datum(chartObj.data).attr("class", "line").attr("d", yObjs[y].line).style("stroke", yObjs[y].color).attr("data-series", y).on("mouseover", function () {
                focus.style("display", null);
            }).on("mouseout", function () {
                focus.transition().delay(700).style("display", "none");
            }).on("mousemove", mousemove);
        }
        

        // Draw Axis
        chartObj.svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + chartObj.height + ")").call(chartObj.xAxis).append("text").attr("class", "label").attr("x", chartObj.width / 2).attr("y", 30).style("text-anchor", "middle").text(chartObj.xAxisLable);

        chartObj.svg.append("g").attr("class", "y axis").call(chartObj.yAxis).append("text").attr("class", "label").attr("transform", "rotate(-90)").attr("y", -42).attr("x", -chartObj.height / 2).attr("dy", ".71em").style("text-anchor", "middle").text(chartObj.yAxisLable);

        //Draw tooltips
        var focus = chartObj.svg.append("g").attr("class", "focus").style("display", "none");

        for (var y  in yObjs) {
            yObjs[y].tooltip = focus.append("g");
            yObjs[y].tooltip.append("circle").attr("r", 5);
            yObjs[y].tooltip.append("rect").attr("x", 8).attr("y","-5").attr("width",22).attr("height",'0.75em');
            yObjs[y].tooltip.append("text").attr("x", 9).attr("dy", ".35em");
        }

        // Year label
        focus.append("text").attr("class", "focus year").attr("x", 9).attr("y", 7);
        // Focus line
        focus.append("line").attr("class", "focus line").attr("y1", 0).attr("y2", chartObj.height);

        //Draw legend
        var legend = chartObj.mainDiv.append('div').attr("class", "legend");
        for (var y  in yObjs) {
            series = legend.append('div');
            series.append('div').attr("class", "series-marker").style("background-color", yObjs[y].color);
            series.append('p').text(y);
            yObjs[y].legend = series;
        }

        // Overlay to capture hover
        chartObj.svg.append("rect").attr("class", "overlay").attr("width", chartObj.width).attr("height", chartObj.height).on("mouseover", function () {
            focus.style("display", null);
        }).on("mouseout", function () {
            focus.style("display", "none");
        }).on("mousemove", mousemove);

        return chartObj;
        function mousemove() {
            var x0 = chartObj.xScale.invert(d3.mouse(this)[0]), i = chartObj.bisectYear(dataset, x0, 1), d0 = chartObj.data[i - 1], d1 = chartObj.data[i];
            try {
                var d = x0 - chartObj.xFunct(d0) > chartObj.xFunct(d1) - x0 ? d1 : d0;
            } catch (e) { return;}
            minY = chartObj.height;
            for (var y  in yObjs) {
                yObjs[y].tooltip.attr("transform", "translate(" + chartObj.xScale(chartObj.xFunct(d)) + "," + chartObj.yScale(yObjs[y].yFunct(d)) + ")");
                yObjs[y].tooltip.select("text").text(chartObj.yFormatter(yObjs[y].yFunct(d)));
                minY = Math.min(minY, chartObj.yScale(yObjs[y].yFunct(d)));
            }

            focus.select(".focus.line").attr("transform", "translate(" + chartObj.xScale(chartObj.xFunct(d)) + ")").attr("y1", minY);
            focus.select(".focus.year").text("Year: " + chartObj.xFormatter(chartObj.xFunct(d)));
        }

    };
    return chartObj;
}
.chart-wrapper {
  max-width: 950px;
  min-width: 304px;
  margin: 0 auto;
  background-color: #FAF7F7;
}

.chart-wrapper .inner-wrapper {
  position: relative;
  padding-bottom: 50%;
  width: 100%;
}

.chart-wrapper .outer-box {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.chart-wrapper .inner-box {
  width: 100%;
  height: 100%;
}

.chart-wrapper text {
  font-family: sans-serif;
  font-size: 11px;
}

.chart-wrapper p {
  font-size: 16px;
  margin-top: 5px;
  margin-bottom: 40px;
}

.chart-wrapper .axis path,
.chart-wrapper .axis line {
  fill: none;
  stroke: #1F1F2E;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.chart-wrapper .axis path {
  stroke-width: 2px;
}

.chart-wrapper .line {
  fill: none;
  stroke: steelblue;
  stroke-width: 5px;
}

.chart-wrapper .legend {
  min-width: 200px;
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  font-size: 16px;
  padding: 10px 40px;
}

.chart-wrapper .legend>div {
  margin: 0px 25px 10px 0px;
  flex-grow: 0;
}

.chart-wrapper .legend p {
  display: inline;
  font-size: 0.8em;
  font-family: sans-serif;
  font-weight: 600;
}

.chart-wrapper .legend .series-marker {
  height: 1em;
  width: 1em;
  border-radius: 35%;
  background-color: crimson;
  display: inline-block;
  margin-right: 4px;
  margin-bottom: -0.16rem;
}

.chart-wrapper .overlay {
  fill: none;
  pointer-events: all;
}

.chart-wrapper .focus circle {
  fill: crimson;
  stroke: crimson;
  stroke-width: 2px;
  fill-opacity: 15%;
}

.chart-wrapper .focus rect {
  fill: lightblue;
  opacity: 0.4;
  border-radius: 2px;
}

.chart-wrapper .focus.line {
  stroke: steelblue;
  stroke-dasharray: 2, 5;
  stroke-width: 2;
  opacity: 0.5;
}

@media (max-width:500px) {
  .chart-wrapper .line {
    stroke-width: 3px;
  }
  .chart-wrapper .legend {
    font-size: 14px;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
  <link rel="stylesheet" type="text/css" href="multiline.css">
  <script src="https://d3js.org/d3.v3.js" charset="utf-8"></script>
  <!--<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>-->
</head>

<body>
  <div class="chart-wrapper" id="chart-line1"></div>

  <script type="text/javascript">
    d3.csv('https://gist.githubusercontent.com/asielen/44ffca2877d0132572cb/raw/9c18f31f9ffbb402b070d88efc9a6381d57c0735/multiline_data.csv', function(error, data) {
      data.forEach(function(d) {
        d.year = +d.year;
        d.variableA = +d.variableA;
        d.variableB = +d.variableB;
      });

      var chart = makeLineChart(
        data,
        'year', {
          FinTechs: {
            column: 'variableA',
            color:'pink'
          },
          SMEs: {
            column: 'variableB',
            color: 'green'
          },
        }, {
          xAxis: 'Year',
          yAxis: 'Amount'
        }
      );
      chart.bind('#chart-line1');
      chart.render();
    });
  </script>
</body>

</html>

【讨论】:

  • 约翰非常感谢如此详细的回答!后来我设法改变了颜色,但这个解决方案更干净,应该允许我将标准颜色更新为我最初想要的渐变!谢谢!
  • 嗨,约翰!我刚刚就同一张图表提出了另一个问题(添加第三个数据集)-stackoverflow.com/questions/66546292/… 如果您有任何想法,将不胜感激?
猜你喜欢
  • 2014-05-24
  • 2019-07-24
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 2020-06-27
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
相关资源
最近更新 更多