使用d3.js在插值曲线中围绕单点进行短划线

时间:2016-11-25 15:31:51

标签: javascript css d3.js svg

我想为某些给定点绘制带插值的曲线。在这里,这些点表示太阳能电池板可以产生多少太阳能,因此每天每小时有一点阳光。点数可能会根据一年中的月份而有所不同(例如,12月份为10点,6月份为16点,因为这些月份每天的太阳分别为10小时和16小时)。

直到这里一切都很好,但现在我们想要在你看到图形的那一天的小时添加太阳图像。为此,我创建了2行:一行在当前小时之前,一行之后,并将太阳图像放在当前小时位置。 6月看起来像是16点,下午1点: Curve in June

看起来很好。问题是当点数较少时,当前小时之前和之后的点之间的空间更大,并且图形太大。这是1月9日上午10点(错误的图形渲染):

Curve in January

(在两个图像中,底部的结束/开始时间是静态的)

我希望留给太阳的空白始终是一样的。

我尝试过各种各样的事情:

  • 在数据中添加“靠近太阳”的一些点:不起作用,因为它弄乱了比例,即使在添加点后更新比例,曲线的顶部也不再居中
  • 在太阳图像上放置背景:图形必须集成在透明容器中
  • 使用stroke-dasharray:我无法理解足以计算它的百分比/像素值。例如,与短划线的距离为100%时,它会在线的末尾之前划线。对于像素单位,我还没有找到任何方法来计算曲线图所生成的像素数,因此无法计算短划线的确切位置
  • 使用linearGradiant:我无法扩展适当的百分比定位。无论如何,渲染是丑陋的,因为它垂直地切割线条颜色,这在图形上是不好的

如果有人知道如何正确地完成这项工作,那就太棒了。此外,我可能已经错过了一些显而易见的事情,或者对这个问题采取了错误的方式,但经过3天的思考,我有点超载哈哈。感谢您阅读

4 个答案:

答案 0 :(得分:1)

解决此问题的一个方法是使用svg掩码。

像这里解释的那样:"SVG clipPath to clip the *outer content out",你可以创建一个掩码来创建一个"非显示区域"你应用它的元素。换句话说,我创建了一个带有圆圈的面具,该圆圈一直处于太阳位置,隐藏了圆圈内部的曲线部分。

答案 1 :(得分:1)

听起来你有答案,但我会提出一个不同的方法。使用stroke-dasharray听起来非常容易解决。这是一个快速演示:



<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <script>
    var svg = d3.select("body")
      .append("svg")
      .attr("width", 500)
      .attr("height", 500);

    var line = d3.line()
      .curve(d3.curveCardinal)
      .x(function(d) {
        return d[0];
      })
      .y(function(d) {
        return d[1];
      });
      
    var data = [[10,450], [250, 50], [490, 450]];
    
    var p = svg.append("path")
      .datum(data)
      .attr("d", line)
      .style("stroke", "orange")
      .style("stroke-width", "5px")
      .style("fill", "none");
    
    var l = p.node().getTotalLength(),
        sunSpace = l  / 12;
    
    function createSpace(){
      var sunPos = Math.random() * l;
      p.attr("stroke-dasharray", (sunPos - sunSpace/2) + "," + sunSpace + "," + l);
    }
    
    createSpace();
    setInterval(createSpace, 1000);    
  </script>
</body>

</html>
&#13;
&#13;
&#13;

评论的编辑

&#13;
&#13;
<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <svg width="500" height="500"></svg>
  <script>
    var svg = d3.select("svg"),
      margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom,
      g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var y = d3.scaleLinear()
      .rangeRound([height, 0])
      .domain([0, 10]);

    var x = d3.scaleLinear()
      .rangeRound([0, width])
      .domain([0, 10]);

    var line = d3.line()
      .curve(d3.curveCardinal)
      .x(function(d) {
        return x(d[0]);
      })
      .y(function(d) {
        return y(d[1]);
      });

    var data = [
      [1, 1],
      [5, 9],
      [9, 1]
    ];

    g.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

    g.append("g")
      .attr("class", "axis axis--y")
      .call(d3.axisLeft(y))

    var p = g.append("path")
      .datum(data)
      .attr("d", line)
      .style("stroke", "orange")
      .style("stroke-width", "5px")
      .style("fill", "none");



    var pathLength = p.node().getTotalLength(),
      sunSpace = pathLength / 12;

    function createSpace() {

      var sunPos = x(3);

      var beginning = 0,
        end = pathLength,
        target;

      while (true) {
        target = Math.floor((beginning + end) / 2);
        pos = p.node().getPointAtLength(target);
        if ((target === end || target === beginning) && pos.x !== sunPos) {
          break;
        }
        if (pos.x > sunPos) end = target;
        else if (pos.x < sunPos) beginning = target;
        else break; //position found
      }
      p.attr("stroke-dasharray", (target - sunSpace/2) + "," + sunSpace + "," + pathLength);
    }

    createSpace();
  </script>
</body>

</html>
&#13;
&#13;
&#13;

答案 2 :(得分:-1)

你可能会过度思考它。您是否考虑过绘制整条线并在太阳图标周围加上一个白色圆圈?只要你在线后绘制图标,它就会留下适当的空间。

答案 3 :(得分:-1)

您应该能够将数据与渲染模型分开。您没有包含代码,因此您的具体解决方案会有所不同。但一般的想法是,您将实际数据转换为更适合您的渲染需求的内容。例如:

{{1}}

现在,你可以在第一个段应该结束的路径上有精确的点,应该渲染图标,第二个段应该开始。