如何在 d3.js 中制作带有圆角和扩展网格的条形图? [英] How to Make a bar chart with rounded corners with extended grid in d3.js?

查看:24
本文介绍了如何在 d3.js 中制作带有圆角和扩展网格的条形图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

var rectCluster = svg.selectAll(".bar").数据(数据);矩形簇.enter().append("rect").attr(类",函数(d){返回酒吧";}).attr(属性).attr({ry: (20), rx: 20 }).attr("x", function(d) { return x(d.text); }).attr(y",函数(d){返回高度;}).style(填充",功能(d){返回颜色(d.text);}).attr("宽度", x.rangeBand()).attr("高度", 0).过渡().duration(animationDelay).延迟(功能(d,我){返回 i * 动画延迟;}).attr("y", function(d) { return y(d.score); }).attr("height", function(d) { return height - y(d.score) });变量属性 = {宽度:函数(d,i){返回 o.rangeBand();},高度:函数(d,i){返回 yScale(d);},填写:#384252",x: 函数(d, i){返回 xScale(i);},y:函数(d,i){返回高度 - yScale(d) - margin.bottom;}};

解决方案

一个选项是使用剪辑路径,但您也可以使用与构建矩形相同的信息来创建一个简单的路径生成器: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. 长 x+宽-r,y-高
  5. 半径,radius,0,0,1,x+width,y-height+radius
  6. 长 x+宽,y
  7. Z

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

function bar(x,y,w,h,r,f) {//扫描标志:如果(f == 未定义)f = 1;//圆弧顶部的 x 坐标var x0 = x+r;var x1 = x+w-r;//圆弧底部的 y 坐标var y0 = y-h+r;//组装路径:变量部分 = ["M",x,y,//第 1 步"L",x,y0,//步骤 2"A",r,r,0,0,f,x0,y-h,//步骤 3"L",x1,y-h,//第 4 步"A",r,r,0,0,f,x+w,y0,//步骤 5"L",x+w,y,//第 6 步"Z"//步骤 7];返回parts.join(" ");}

我已经包含了一个可选的扫描标志 (f) - 如果设置为 0,它将反转弧线.

并应用如下:

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

放在一起你可能会得到类似的东西:

var width = 500;变量高度 = 200;var svg = d3.select("body").append("svg").attr("width",width).attr("height",height);无功数据 = [ 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 bar = svg.selectAll(null).data(数据).进入().append("路径").attr(d",函数(d,i){return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),10);})功能条(x,y,w,h,r,f){//扫描标志:如果(f == 未定义)f = 1;//圆弧顶部的 x 坐标var x0 = x+r;var x1 = x+w-r;//圆弧底部的 y 坐标var y0 = y-h+r;//只是为了方便(与上面略有不同):var l = "L", a = "A";var 部分 = ["M",x,y,l,x,y0,a,r,r,0,0,f,x0,yh,l,x1,yh,a,r,r,0,0,f,x+w,y0,l,x+w,y,"Z"];返回parts.join(" ");}//仍然可以转换:bar.data(data.reverse()).过渡().attr(d",函数(d,i){return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),30);}).duration(2000).过渡().attr(d",函数(d,i){return bar(x(i),y(0),x.bandwidth(),y(0)-y(d),0);}).duration(2000).过渡().attr(d",函数(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>

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

I am trying to create a Bar chart using D3.js. The condition is that Bar should have fixed width and padding between the bars and it should be plot at center of grid lines.

Tooltip should appear on click with a vertical line

I am able to create the grid lines with plotted bar, somehow rx,ry is rounding from both sides. How can i acheive the same sort of result.

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

I am trying to achieve like this.

解决方案

One option is to use a clip path, but you can also create a simple path generator using the same information you use to build the rectangles: x,y,width,height, plus radius. The path is fairly simple:

  1. Move to bottom left corner
  2. Line to bottom of top left arc
  3. Arc to top of top left arc
  4. Line to the top of the top right arc
  5. Arc to the bottom of the top right arc.
  6. Line to the bottom right corner
  7. Close path.

Which is comes together something like:

  1. M x,y
  2. L x,y-height+radius
  3. A radius,radius,0,0,1,x+radius,y-height
  4. L x+width-r,y-height
  5. A radius,radius,0,0,1,x+width,y-height+radius
  6. L x+width,y
  7. Z

Which could look like this (a rather lazy implementation):

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

I've included an optional sweep flag (f) - it'll invert the arcs if set to 0.

And applied something like so:

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

Put together you might get something like:

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>

If you radius exceeds half bar height or width you'll get some funkier results, a complete implementation of this would consider a check to make sure that the radius for bar isn't too large

这篇关于如何在 d3.js 中制作带有圆角和扩展网格的条形图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆