【问题标题】:How do I create this shape in SVG?如何在 SVG 中创建这个形状?
【发布时间】:2014-03-30 09:47:41
【问题描述】:

现在,我使用椭圆将条形底座设置为阴影。:http://jsfiddle.net/rdesai/MjFgK/55/

如何更改样式以使基础如下图所示?

编辑

这个问题可以简化为创建一个类似下面的形状:

我可以用这个形状来替换椭圆。

如何将下面的椭圆代码修改成上面的形状?

svg.selectAll(".ellipse")
.data(data)
.enter()
.append("ellipse")
.attr("cx", function(d) { return x(d.episode) + 17; })
.attr("cy", function(d) { return y(0); })
.attr("rx", 20)
.attr("ry", 5)
.style("fill", function(d) { return d.shadow_color; })

【问题讨论】:

  • 您从哪里获得这些样式化的图形图像?如果你不介意,我可以得到链接吗?
  • @UnknownUser 来自我公司的设计团队。
  • 啊,每个网络程序员都喜欢从他们的老板那里听到的话:“这是一个专业设计的静态图形。让它看起来一样,但要在线、可更新和互动。”

标签: svg d3.js bar-chart


【解决方案1】:

您将需要使用自定义路径数据生成函数。如果您不熟悉路径数据属性的工作原理you may find this tutorial a useful starting point(续herehere)。

由于它不再起作用,我建议将整个条形图绘制为自定义路径,而不是使用单独的基础。如果你真的想要单独的元素,它应该很容易适应。

你的酒吧代码是:

svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("class", "bar")
    .attr("x", function(d) { return x(d.episode); })
    .attr("width", x.rangeBand())
    .attr("y", function(d) { return y(d.donations); })
    .attr("height", function(d) { return height - y(d.donations); })
    .style("fill", function(d) { return d.bar_color; })
    .attr("rx", 10)
    .attr("ry", 10)
    .on("mouseover", function(d) {      
        tooltip.transition().duration(200).style("opacity", .9);      
        tooltip.html("NGO: " + d.ngo)  
        .style("left", (d3.event.pageX) + "px")     
        .style("top", (d3.event.pageY - 28) + "px");    
    })                  
    .on("mouseout", function(d) {       
        tooltip.transition().duration(500).style("opacity", 0);   
    });

工具提示鼠标悬停功能不会改变,因此我将在其余讨论中忽略这些功能。填充样式也不会改变,类也不会改变。但是,我们要将<rect> 元素转换为<path> 元素,并且我们将用路径数据“d”替换所有其他属性(x/y/width/height/rx/ry)属性:

svg.selectAll(".bar")
    .data(data)
    .enter().append("path")
    .attr("class", "bar")
    .style("fill", function(d) { return d.bar_color; })
    .attr("d", function(d) { 
         return /* a path data string that completely describes this shape */; 
    })
    .on("mouseover",
        /* etc */

现在,让我们分析一下这个形状。从左下角开始,需要以下方向来绘制:

  • 移动到喇叭形底座的起点,在钢筋之间的填充中间,在轴线上;
  • 弯曲到钢筋的左边缘,与轴向上的距离与耀斑半径相等;
  • 几乎到条形顶部的直线,在顶部曲线半径处停止,该半径是条形宽度的一半;
  • 半圆(或两条曲线)直到整个条形高度,然后回到另一侧;
  • 直线回到几乎到轴线;
  • 创建另一个喇叭形底座,弯曲到填充的一半;
  • 关闭路径以连接回到起点。

对于move命令和line命令,只需要给出目的地(x,y)点即可。对于关闭命令,您根本不需要给任何分数。对于曲线,它变得有点复杂。您可以使用弧线来做到这一点(有关语法,请参阅我的第二个教程),但如果您使用二次曲线,它会更简单一些,并且看起来大致相同,控制点是您正在弯曲的拐角位置。也就是说,控制点为 (0,1) 从 (0,0) 到 (1,1) 的二次曲线几乎 是一条以 (1,0) 为中心的圆弧。

(0,0)
  @ —  *  << control point (0,1)
      \
       |     ...something like this, but prettier!
       @
     (1,1)

使用该攻击计划,并根据作为参数提供给x.rangeRoundBands() 的 paddingProportion 确定耀斑的宽度,您会得到:

.attr("d", function(d) { 
    var barWidth = x.rangeBand();
    var paddingWidth = barWidth*(paddingProportion)/(1-paddingProportion);
    var flareRadius = paddingWidth/2;
    var topRadius = barWidth/2;

    var xPos =  x(d.episode);
    var yPos =  y(d.donations);

    return [ "M", [ (xPos - flareRadius), height], //start at bottom left of base
             "Q", [xPos, height], //control point for flare curve
                    [xPos, (height-flareRadius)], //end point of flare curve
             "L", [xPos, (yPos + topRadius)], //line to start of top curve
             "Q", [xPos, yPos], //control point for left top curve
                    [(xPos + topRadius), yPos],  //end point for left top curve
             "Q", [(xPos + barWidth), yPos],  //control point for right top curve
                    [(xPos + barWidth), (yPos + topRadius)], //end point for right top
             "L", [(xPos + barWidth), (height-flareRadius)], //line to start of right flare
             "Q", [(xPos + barWidth), height], //control for right flare
                    [(xPos + barWidth + flareRadius), height], //end of right flare
             "Z" //close the path
            ].join(" ");

})

http://jsfiddle.net/rdesai/MjFgK/62/

最终的返回值是一个字符串,我将它作为一个字母数组和每个 (x,y) 点的二元素数组放在一起,然后用空格将它们全部连接在一起。它比使用+ 稍微快一些,并且将数据保持在有组织的结构中。最终返回的字符串看起来像

M 47.75,195 Q 52,195 52,190.75 L 52,26.056240786240778 Q 52,9.056240786240778 69,9.056240786240778 Q 86,9.056240786240778 86,26.056240786240778 L 86,190.75 Q 86,195 90.25,195 Z

现在,这主要是您想要的形状,但在最短的条形图上会有些混乱,因为顶部曲线的半径大于条形图的高度。几个快速的最大/最小检查,以确保点的 y 值永远不会小于 yPos 或大于 height,清理:

.attr("d", function(d) { 
    var barWidth = x.rangeBand();
    var paddingWidth = barWidth*(paddingProportion)/(1-paddingProportion);
    var flareRadius = paddingWidth/2;
    var topRadius = barWidth/2;

    var xPos =  x(d.episode);
    var yPos =  y(d.donations);

    return [ "M", [ (xPos - flareRadius), height], //start at bottom left of base
             "Q", [xPos, height], 
                    [xPos, Math.max((height-flareRadius), yPos)], 
             "L", [xPos,  Math.min((yPos + topRadius), height)],
             "Q", [xPos, yPos], 
                    [(xPos + topRadius), yPos],
             "Q", [(xPos + barWidth), yPos], 
                    [(xPos + barWidth), Math.min((yPos + topRadius), height)],
             "L", [(xPos + barWidth), Math.max((height-flareRadius), yPos)],
             "Q", [(xPos + barWidth), height], 
                    [(xPos + barWidth + flareRadius), height],
             "Z"
            ].join(" ");

})

http://jsfiddle.net/MjFgK/63/

要吸收的内容很多,所以我建议您花点时间尝试一下,尝试改变形状,尝试使用弧线而不是二次曲线,尝试从样本上的第一个和最后一个小节重新创建形状图片。或者回到最初的计划,将基本形状创建为单独的元素。

【讨论】:

  • 感谢您的详细解答。实际上还剩下一件小事。如果您看到第一个和最后一个条的底角,它们在一侧是弯曲的。我该如何做到这一点?
  • 您需要使函数依赖于i(柱的索引),如果i 为零,则将起点更改为柱内而不是柱外条(并且可能更改第一条曲线的半径以使用与条顶部相同的半径)。同样,如果i 等于数据长度,则移动最后一条曲线的点。或者作为替代方案,您可以在整个绘图中添加一个clipPath,它是一个带有弯角的矩形......这可能更容易!
  • 我明白了。任何带圆圈的 x 轴线?
  • 对于轴:绘制一个默认的 d3 轴,然后重新选择所有“g.tick”元素并在每个元素中添加一个圆圈。您可以选择并删除刻度线,也可以将它们设置为零长度。 More on customizing d3 axes here。圆形刻度的快速示例,改编自该链接中的示例:fiddle.jshell.net/zUj3E/6
  • 用 Firefox 修复更新(如果 SVG 没有明确声明为块或内联块元素,他们在 .getComputedStyle() 上有一个错误),并更改了水平轴(您需要设置轴的.orient() 属性,x 和 y 只是标签/类名)。 fiddle.jshell.net/zUj3E/7
猜你喜欢
  • 2017-05-03
  • 2014-03-20
  • 1970-01-01
  • 2012-01-30
  • 2021-08-27
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多