如何在SVG中创建此形状?

Rah*_*sai 3 svg bar-chart d3.js

现在,我的条形底座用椭圆形作为阴影: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; })
Run Code Online (Sandbox Code Playgroud)

Ame*_*aBR 9

您将需要使用自定义路径数据生成功能.如果您不熟悉路径数据属性的工作原理,您可能会发现本教程是一个有用的起点(在此处此处继续).

由于它不再是任何工作,我建议将整个条形图绘制为自定义路径,而不是单独的基础.如果你真的想要单独的元素,它应该是相当简单的适应.

你的酒吧代码是:

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);   
    });
Run Code Online (Sandbox Code Playgroud)

工具提示鼠标悬停功能不会改变,所以我将把它们从讨论的其余部分中删除.填充样式也不会改变,类也不会改变.但是,我们要将<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 */
Run Code Online (Sandbox Code Playgroud)

现在,让我们分析一下这种形状.从左下角开始,您需要以下指示来绘制它:

  • 移动到扩口基座的起点,在杆之间的填充中间,在轴线上;
  • 曲线到条的左边缘,距离轴的距离等于喇叭半径;
  • 直线几乎到了杆的顶部,由顶部曲线半径停止,这是杆宽的一半;
  • 半圆(或两条曲线),直到整个条高,然后向下到另一边;
  • 直线几乎回到轴线;
  • 制作另一个喇叭形底座,弯曲到衬垫的中间;
  • 关闭连接回到开始的路径.

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

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

通过该攻击计划,并确定作为参数给出的paddingProportion的耀斑宽度x.rangeRoundBands(),您得到:

.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(" ");

})
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

现在,这主要是你想要的形状,但是在最短的条形上会有点凌乱,其中顶部曲线的半径大于条形的高度.几次快速最大/最小检查,以确保点的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(" ");

})
Run Code Online (Sandbox Code Playgroud)

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

这需要花很多时间,所以我建议你花一些时间来玩它,尝试改变形状,尝试使用弧而不是二次曲线,尝试从样本图像的第一个和最后一个条重建形状.或者回到原来的计划,只是将基本形状创建为单独的元素.