【问题标题】:Axis-range-slider alignment轴范围滑块对齐
【发布时间】:2019-01-17 20:39:30
【问题描述】:

我需要设计一个如下图所示的d3组件。

我参考了来自this link 的现有代码示例,并对其进行了修改以创建类似的内容。

Left 改变了轴的宽度,我通过改变 domain 类stroke-width 属性来尝试。但是,我以这样的方式结束。

问题:

  1. 滑块手柄未与轴对齐。
  2. 轴颜色印在滑块上。
  3. 轴的末端不是完美的圆形。

问题:

  1. 我不知道要平移/变换什么来对齐滑块和轴。
  2. 我尝试调整不透明度值,但没有帮助。
  3. 我将 stroke-linecap 设置为 round,但仍不完全是圆形。

我为此使用 d3 v4。还有我的 final code is here 的 jsfiddle。

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <style>
.tick{
  visibility:hidden;
}

 .domain {
    stroke: grey;
    stroke-width:10px;
    stroke-linecap: round;
  }
  
  .selection {
    fill:red
  }

</style>
</head>

<body>
  <div style="margin-left: 20px;margin-top: 20px;">
    <span></span> to <span></span>
  </div>


<script>

    var margin = 20,
        width = 400 - margin * 2,
        height = 15;

    // v3 = var x = d3.scale.linear()
    var x = d3.scaleLinear()
        .domain([0,100])
        .range([0, width]);

    /*
    var brush = d3.svg.brush()
      .x(x)
      .extent([20, 50]);
    */
    var brush = d3.brushX()
        .extent([[0,0], [width,height]])
        .on("brush", brushed);

    var svg = d3.select("body").append("svg")
        .attr("width", width + margin * 2)
        .attr("height", 100)
      .append("g")
        .attr("transform", "translate(" + margin + "," + margin + ")")
        .call(d3.axisBottom()
            .scale(x)
            .tickSize(0));
  
    var brushg = svg.append("g")
        .attr("class", "brush")
        .call(brush)
        
     // left circle
	
    
    var left_text = brushg.append("text")
    .attr("class", "label")
    .attr("fill", "black") 
    .attr("text-anchor", "middle")
    .text("hello world")
    .attr("transform", "translate(0," + (35) + ")")
    
    var right_text = brushg.append("text")
    .attr("class", "label")
    .attr("fill", "black") 
    .attr("text-anchor", "middle")
    .text("hello world")
    .attr("transform", "translate(0," + (35) + ")")
        
    
    /* 
      Height of the brush's rect is now 
        generated by brush.extent():
    brushg.selectAll("rect")
        .attr("height", height);
    */

    function brushed() {
      /*
        The brush attributes are no longer stored 
        in the brush itself, but rather in the 
        element it is brushing. That's where much of
        the confusion around v4's brushes seems to be.
        The new method is a little difficult to adapt
        to, but seems more efficient. I think much of
        this confusion comes from the fact that 
        brush.extent() still exists, but means
        something completely different.

        Instead of calling brush.extent() to get the 
        range of the brush, call 
        d3.brushSelection(node) on what is being 
        brushed.

      d3.select('#start-number')
        .text(Math.round(brush.extent()[0]));
      d3.select('#end-number')
        .text(Math.round(brush.extent()[1]));
      */


      var range = d3.brushSelection(this)
          .map(x.invert);
      
      console.log('range->'+range)
      d3.selectAll("span")
          .text(function(d, i) {
            console.log(Math.round(range[i]))
            return Math.round(range[i])
          })
          
      left_text.attr("x", x(range[0]));
      left_text.text(Math.round(range[0]));
      right_text.attr("x", x(range[1]));
      right_text.text(Math.round(range[1]));
      
      d3.selectAll("rect").attr("dy", "-5em")
          
    }
    

    // v3:  brushed();
    brush.move(brushg, [20, 40].map(x));

</script>
</body>
</html>

【问题讨论】:

    标签: javascript html d3.js svg


    【解决方案1】:

    轴和画笔实际上是完全对齐的!

    如果您将stroke-width 设置为1px,您可以看到这一点:

    .as-console-wrapper { max-height: 30% !important;}
    <!DOCTYPE html>
    <html>
    
    <head>
    <meta charset="utf-8">
      <script src="https://d3js.org/d3.v5.min.js"></script>
      <style>
    .tick{
      visibility:hidden;
    }
    
     .domain {
        stroke: grey;
        stroke-width:1px;
        stroke-linecap: round;
      }
      
      .selection {
        fill:red
      }
    
    </style>
    </head>
    
    <body>
      <div style="margin-left: 20px;margin-top: 20px;">
        <span></span> to <span></span>
      </div>
    
    
    <script>
    
        var margin = 20,
            width = 400 - margin * 2,
            height = 15;
    
        // v3 = var x = d3.scale.linear()
        var x = d3.scaleLinear()
            .domain([0,100])
            .range([0, width]);
    
        /*
        var brush = d3.svg.brush()
          .x(x)
          .extent([20, 50]);
        */
        var brush = d3.brushX()
            .extent([[0,0], [width,height]])
            .on("brush", brushed);
    
        var svg = d3.select("body").append("svg")
            .attr("width", width + margin * 2)
            .attr("height", 100)
          .append("g")
            .attr("transform", "translate(" + margin + "," + margin + ")")
            .call(d3.axisBottom()
                .scale(x)
                .tickSize(0));
      
        var brushg = svg.append("g")
            .attr("class", "brush")
            .call(brush)
            
         // left circle
    	
        
        var left_text = brushg.append("text")
        .attr("class", "label")
        .attr("fill", "black") 
        .attr("text-anchor", "middle")
        .text("hello world")
        .attr("transform", "translate(0," + (35) + ")")
        
        var right_text = brushg.append("text")
        .attr("class", "label")
        .attr("fill", "black") 
        .attr("text-anchor", "middle")
        .text("hello world")
        .attr("transform", "translate(0," + (35) + ")")
            
        
        /* 
          Height of the brush's rect is now 
            generated by brush.extent():
        brushg.selectAll("rect")
            .attr("height", height);
        */
    
        function brushed() {
          /*
            The brush attributes are no longer stored 
            in the brush itself, but rather in the 
            element it is brushing. That's where much of
            the confusion around v4's brushes seems to be.
            The new method is a little difficult to adapt
            to, but seems more efficient. I think much of
            this confusion comes from the fact that 
            brush.extent() still exists, but means
            something completely different.
    
            Instead of calling brush.extent() to get the 
            range of the brush, call 
            d3.brushSelection(node) on what is being 
            brushed.
    
          d3.select('#start-number')
            .text(Math.round(brush.extent()[0]));
          d3.select('#end-number')
            .text(Math.round(brush.extent()[1]));
          */
    
    
          var range = d3.brushSelection(this)
              .map(x.invert);
          
          console.log('range->'+range)
          d3.selectAll("span")
              .text(function(d, i) {
                console.log(Math.round(range[i]))
                return Math.round(range[i])
              })
              
          left_text.attr("x", x(range[0]));
          left_text.text(Math.round(range[0]));
          right_text.attr("x", x(range[1]));
          right_text.text(Math.round(range[1]));
          
          d3.selectAll("rect").attr("dy", "-5em")
              
        }
        
    
        // v3:  brushed();
        brush.move(brushg, [20, 40].map(x));
    
    </script>
    </body>
    </html>

    那么,这里发生了什么?问题是,当您告诉浏览器画一条线(在这种情况下,它是一条路径,但没关系)并将其笔画增加到 100 像素时,它将向一侧增加 50 像素和 50像素到另一边。因此,粗轴的中间正好位于画笔矩形的顶部。

    这里有几种解决方案,比如画一个矩形。但是,如果您想保持增加 .domain 笔画宽度的方法,让我们中断选择并将轴移动到其 stroke-width 的一半(这里我将宽度增加到 20 像素,所以更容易查看对齐方式):

    .as-console-wrapper { max-height: 30% !important;}
    <!DOCTYPE html>
    <meta charset="utf-8">
    <script src="//d3js.org/d3.v4.min.js"></script>
    <!-- 
      axes and brushes are styled out of the box, 
        so this is no longer needed
    <style>
      
      .axis path, .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
      }
      .brush .extent {
        fill-opacity: .125;
        shape-rendering: crispEdges;
      }
    
    </style>
    -->
    <style>
      .tick {
        visibility: hidden;
      }
    
      .domain {
        stroke: grey;
        stroke-width: 20px;
        stroke-linecap: round;
      }
    
      .selection {
        fill: red
      }
    
    </style>
    
    <body>
      <div style="margin-left: 20px;margin-top: 20px;">
        <span></span> to <span></span>
      </div>
    </body>
    
    <script>
      var margin = 20,
        width = 400 - margin * 2,
        height = 20;
    
      // v3 = var x = d3.scale.linear()
      var x = d3.scaleLinear()
        .domain([0, 100])
        .range([0, width]);
    
      /*
      var brush = d3.svg.brush()
        .x(x)
        .extent([20, 50]);
      */
      var brush = d3.brushX()
        .extent([
          [0, 0],
          [width, height]
        ])
        .on("brush", brushed);
    
      var svg = d3.select("body").append("svg")
        .attr("width", width + margin * 2)
        .attr("height", 100);
    
      svg.append("g")
        .attr("transform", "translate(" + margin + "," + (margin + 10) + ")")
        .call(d3.axisBottom()
          .scale(x)
          .tickSize(0));
    
      var brushg = svg.append("g")
        .attr("transform", "translate(" + margin + "," + margin + ")")
        .attr("class", "brush")
        .call(brush)
    
      // left circle
    
    
      var left_text = brushg.append("text")
        .attr("class", "label")
        .attr("fill", "black")
        .attr("text-anchor", "middle")
        .text("hello world")
        .attr("transform", "translate(0," + (35) + ")")
    
      var right_text = brushg.append("text")
        .attr("class", "label")
        .attr("fill", "black")
        .attr("text-anchor", "middle")
        .text("hello world")
        .attr("transform", "translate(0," + (35) + ")")
    
    
      /* 
        Height of the brush's rect is now 
          generated by brush.extent():
      brushg.selectAll("rect")
          .attr("height", height);
      */
    
      function brushed() {
        /*
          The brush attributes are no longer stored 
          in the brush itself, but rather in the 
          element it is brushing. That's where much of
          the confusion around v4's brushes seems to be.
          The new method is a little difficult to adapt
          to, but seems more efficient. I think much of
          this confusion comes from the fact that 
          brush.extent() still exists, but means
          something completely different.
    
          Instead of calling brush.extent() to get the 
          range of the brush, call 
          d3.brushSelection(node) on what is being 
          brushed.
    
        d3.select('#start-number')
          .text(Math.round(brush.extent()[0]));
        d3.select('#end-number')
          .text(Math.round(brush.extent()[1]));
        */
    
    
        var range = d3.brushSelection(this)
          .map(x.invert);
    
        console.log('range->' + range)
        d3.selectAll("span")
          .text(function(d, i) {
            console.log(Math.round(range[i]))
            return Math.round(range[i])
          })
    
        left_text.attr("x", x(range[0]));
        left_text.text(Math.round(range[0]));
        right_text.attr("x", x(range[1]));
        right_text.text(Math.round(range[1]));
    
        d3.selectAll("rect").attr("dy", "-5em")
    
      }
    
    
      // v3:  brushed();
      brush.move(brushg, [20, 40].map(x));
    
    </script>

    【讨论】:

      【解决方案2】:

      轴上的路径是一个封闭的形状,并且会产生问题。此外,您不想要刻度线,所以为什么不自己绘制“轴”。那么圆边就会被正确绘制出来。

          var svg = d3.select("body").append("svg")
              .attr("width", width + margin * 2)
              .attr("height", 100)
            .append("g")
              .attr("transform", "translate(" + margin + "," + margin + ")")
              // .call(d3.axisBottom()
              //     .scale(x)
              //     .tickSize(0))
              ;
      
          svg.append("path")
             .attr("class", "domain")
             .attr("d", `M${x(0)},0 ${x(100)},0`);
      

      您必须将笔刷范围与描边路径表面相匹配

          var margin = 20,
              width = 400 - margin * 2,
              height = 10; // same as stroke width
      
          var brush = d3.brushX()
              .extent([[0,-height*0.5], [width,height*0.5]])
              .on("brush", brushed);
      

      dy 属性没有用处

            //d3.selectAll("rect").attr("dy", "-5em")
      

      设置选区的填充不透明度

      .selection {
        fill:red;
        fill-opacity: 1;
      }
      

      <!DOCTYPE html>
      <html>
      
      <head>
      <meta charset="utf-8">
        <script src="https://d3js.org/d3.v5.min.js"></script>
        <style>
      .tick{
        visibility:hidden;
      }
      
       .domain {
          stroke: grey;
          stroke-width:10;
          stroke-linecap: round;
      }
      
      .selection {
        fill:red;
        fill-opacity: 1;
      }
      
      </style>
      </head>
      
      <body>
        <div style="margin-left: 20px;margin-top: 20px;">
          <span></span> to <span></span>
      </div>
      
      <script>
          var margin = 20,
              width = 400 - margin * 2,
              height = 10; // same as stroke width
      
          // v3 = var x = d3.scale.linear()
          var x = d3.scaleLinear()
              .domain([0,100])
              .range([0, width]);
      
          /*
          var brush = d3.svg.brush()
            .x(x)
            .extent([20, 50]);
          */
          var brush = d3.brushX()
              .extent([[0,-height*0.5], [width,height*0.5]])
              .on("brush", brushed);
      
          var svg = d3.select("body").append("svg")
              .attr("width", width + margin * 2)
              .attr("height", 100)
            .append("g")
              .attr("transform", "translate(" + margin + "," + margin + ")")
              // .call(d3.axisBottom()
              //     .scale(x)
              //     .tickSize(0))
              ;
          svg.append("path")
             .attr("class", "domain")
             .attr("d", `M${x(0)},0 ${x(100)},0`);
      
          var brushg = svg.append("g")
              .attr("class", "brush")
              .call(brush)
      
           // left circle
      
      
          var left_text = brushg.append("text")
          .attr("class", "label")
          .attr("fill", "black")
          .attr("text-anchor", "middle")
          .text("hello world")
          .attr("transform", "translate(0," + (35) + ")")
      
          var right_text = brushg.append("text")
          .attr("class", "label")
          .attr("fill", "black")
          .attr("text-anchor", "middle")
          .text("hello world")
          .attr("transform", "translate(0," + (35) + ")")
      
      
          /*
            Height of the brush's rect is now
              generated by brush.extent():
          brushg.selectAll("rect")
              .attr("height", height);
          */
      
          function brushed() {
            /*
              The brush attributes are no longer stored
              in the brush itself, but rather in the
              element it is brushing. That's where much of
              the confusion around v4's brushes seems to be.
              The new method is a little difficult to adapt
              to, but seems more efficient. I think much of
              this confusion comes from the fact that
              brush.extent() still exists, but means
              something completely different.
      
              Instead of calling brush.extent() to get the
              range of the brush, call
              d3.brushSelection(node) on what is being
              brushed.
      
            d3.select('#start-number')
              .text(Math.round(brush.extent()[0]));
            d3.select('#end-number')
              .text(Math.round(brush.extent()[1]));
            */
      
            var range = d3.brushSelection(this)
                .map(x.invert);
      
            //console.log('range->'+range)
            d3.selectAll("span")
                .text(function(d, i) {
                  //console.log(Math.round(range[i]))
                  return Math.round(range[i])
                })
      
            left_text.attr("x", x(range[0]));
            left_text.text(Math.round(range[0]));
            right_text.attr("x", x(range[1]));
            right_text.text(Math.round(range[1]));
      
            //d3.selectAll("rect").attr("dy", "-5em")
      
          }
      
          // v3:  brushed();
          brush.move(brushg, [20, 40].map(x));
      
      </script>
      </body>
      </html>

      【讨论】:

        猜你喜欢
        • 2023-03-22
        • 2016-08-19
        • 2018-11-21
        • 2011-02-10
        • 2014-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多