【问题标题】:How to Make a bar chart with rounded corners with extended grid in d3.js? [duplicate]如何在 d3.js 中制作带有扩展网格的圆角条形图? [复制]
【发布时间】:2019-02-21 16:15:29
【问题描述】:

我正在尝试使用 D3.js 创建条形图。条件是 Bar 应该具有固定的宽度和条之间的填充,并且应该绘制在网格线的中心。

工具提示应在点击时出现一条垂直线

我能够创建带有绘图条的网格线,不知何故 rx,ry 从两侧四舍五入。 我怎样才能获得同样的结果。

var rectCluster = svg.selectAll(".bar")
                    .data(data);

                rectCluster
                    .enter().append("rect")
                    .attr("class", function(d) {
                        return "bar";
                    })
                    .attr(attrs)
                    .attr({ry : (20), rx : 20 })
                    .attr("x", function(d) { return x(d.text); })
                    .attr("y", function(d) {
                        return height;
                    })
                    .style("fill", function(d) {
                        return color(d.text);
                    })
                    .attr("width", x.rangeBand())
                    .attr("height", 0)
                    .transition()
                    .duration(animationDelay)
                    .delay(function(d, i) {
                        return i * animationDelay;
                    })
                    .attr("y", function(d) { return y(d.score); })
                    .attr("height", function(d) { return height - y(d.score) });

                    var attrs = {
    width: function(d, i){
      return o.rangeBand();
    },
    height: function(d, i){
      return yScale(d);
    },
    fill: "#384252",
    x: function(d, i){
      return xScale(i);
    },
    y: function(d, i){
      return height - yScale(d) - margin.bottom;
    }
  };

Js Fiddle for the same

我正在努力实现这样的目标。

【问题讨论】:

  • 就像nixkuroi说的那​​样,一个剪辑路径并创建一个更大的rect将x轴广告2*半径吹到你的高度(1也应该足够了)

标签: javascript d3.js charts


【解决方案1】:

一种选择是使用剪辑路径,但您也可以使用用于构建矩形的相同信息创建一个简单的路径生成器:x、y、宽度、高度和半径。路径相当简单:

  1. 移动到左下角
  2. 到左上弧底部的直线
  3. 到左上弧顶部的弧
  4. 直线到右上角弧的顶部
  5. 弧线到右上角弧线的底部。
  6. 到右下角的线
  7. 关闭路径。

它们结合在一起是这样的:

  1. M x,y
  2. L x,y-高度+半径
  3. 半径,radius,0,0,1,x+radius,y-height
  4. L x+width-r,y-height
  5. 半径,radius,0,0,1,x+width,y-height+radius
  6. L x+宽度,y
  7. Z

可能看起来像这样(一个相当懒惰的实现):

function bar(x,y,w,h,r,f) {
    // Flag for sweep:
    if(f == undefined) f = 1;
    // x coordinates of top of arcs
    var x0 = x+r;
    var x1 = x+w-r;
    // y coordinates of bottom of arcs
    var y0 = y-h+r;

    // assemble path:
    var parts = [
      "M",x,y,               // step 1
      "L",x,y0,              // step 2
      "A",r,r,0,0,f,x0,y-h,  // step 3
      "L",x1,y-h,            // step 4
      "A",r,r,0,0,f,x+w,y0,  // step 5
      "L",x+w,y,             // step 6
      "Z"                    // step 7
     ];
    return parts.join(" ");
}

我添加了一个可选的扫描标志 (f) - 如果设置为 0,它将反转弧线。

并应用了类似的东西:

 .attr("d", function(d) { 
    return bar(x(d),y(0),x.bandwidth(),y(0)-y(d),15);  
  })

综合起来你可能会得到类似的东西:

var width = 500;
var height = 200;
var svg = d3.select("body").append("svg").attr("width",width).attr("height",height);
var data = [ 10,20,30,40,20,50,60 ];

var x = d3.scaleBand().domain(d3.range(data.length)).range([10,width-10]).paddingInner(0.1);
var y = d3.scaleLinear().domain([0,60]).range([height-10,10]);

var bars = svg.selectAll(null)
  .data(data)
  .enter()
  .append("path")
  .attr("d", function(d,i) { 
	  return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),10);  
  })
  
function bar(x,y,w,h,r,f) {
	// Flag for sweep:
	if(f == undefined) f = 1;
	
	// x coordinates of top of arcs
	var x0 = x+r;
	var x1 = x+w-r;
	// y coordinates of bottom of arcs
	var y0 = y-h+r;
	// just for convenience (slightly different than above):
	var l = "L", a = "A";

	var parts = ["M",x,y,l,x,y0,a,r,r,0,0,f,x0,y-h,l,x1,y-h,a,r,r,0,0,f,x+w,y0,l,x+w,y,"Z"];
	return parts.join(" ");
}

// Still transitionable:
bars.data(data.reverse())
 .transition()
 .attr("d", function(d,i) { 
  return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),30);  
 })
 .duration(2000)
 .transition()
 .attr("d", function(d,i) { 
  return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),0);  
 })
 .duration(2000)
 .transition()
 .attr("d", function(d,i) { 
  return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),15);  
  })
 .duration(2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

如果你的半径超过半个条的高度或宽度,你会得到一些更有趣的结果,一个完整的实现会考虑检查以确保条的半径不会太大

【讨论】:

  • 我非常感谢这个家伙提供了很好的示例代码!这只是您不想花时间发明轮子的事情之一。
  • 当最大数量和最小数量之间存在较大差距时,此解决方案会出现一些小问题。 jsfiddle.net/ramseyfeng/3yaz79cn/2我已经将max y值从60更新到10000,底部有一些多余的路径,你知道如何解决这个问题吗?谢谢!
  • @huanfeng。如果半径超过条的绘制高度,它将低于轴,这就是您所看到的。您可以使用Math.min(10,y(0)-y(d))); 将半径和条高的最小值作为半径参数传递,这样半径就不能超过条高eg。这将避免这个问题,或者,计算 bar 函数本身的最小值。
【解决方案2】:

我的第一个选择是使用 clipPath(在这里回答:Rounded corner only on one side of svg <rect>

另一种选择是简单地在底部绘制另一个具有方角的矩形:

rectCluster
                    .enter().append("rect")
                    .attr("class", function(d) {
                        return "bar";
                    })
                    .attr(attrs)
                    .attr({ry : (20), rx : 20 })
                    .attr("x", function(d) { return x(d.text); })
                    .attr("y", function(d) {
                        return height;
                    })
                    .style("fill", function(d) {
                        return color(d.text);
                    })
                    .attr("width", x.rangeBand())
                    .attr("height", 0)
                    .transition()
                    .duration(animationDelay)
                    .delay(function(d, i) {
                        return i * animationDelay;
                    })
                    .attr("y", function(d) { return y(d.score); })
                    .attr("height", function(d) { return height - y(d.score) });

                 // SQUARE CORNERS
                 rectCluster
                    .enter().append("rect")
                    .attr("class", function(d) {
                        return "bar";
                    })
                    .attr(attrs)
                    .attr({ry : (20), rx : 0 })
                    .attr("x", function(d) { return x(d.text); })
                    .attr("y", 0)
                    .style("fill", function(d) {
                        return color(d.text);
                    })
                    .attr("width", x.rangeBand())
                    .attr("height", 0)
                    .transition()
                    .duration(animationDelay)
                    .delay(function(d, i) {
                        return i * animationDelay;
                    })
                    .attr("y", height-20)
                    .attr("height", 20);

见小提琴:

https://jsfiddle.net/6x2y35gn/40/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    • 2018-02-03
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 2015-07-23
    相关资源
    最近更新 更多