javascript - 轴-范围- slider 对齐

标签 javascript html d3.js svg

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

Desired Visualization

我引用了 this link 中的现有代码示例,并修改它以创建类似的东西。

My Intermediate working version

Left 正在更改轴的宽度,我通过更改域类笔画宽度属性进行了尝试。然而,我以这样的方式结束了。

Not working version

问题:

  1. slider handle 未与轴对齐。
  2. 轴颜色印记在 slider 上。
  3. 轴的末端不是完美的圆形。

问题:

  1. 我不知道如何平移/变换才能对齐 slider 和轴。
  2. 我尝试调整不透明度值,但没有帮助。
  3. 我将行程线帽设置为圆形,但它仍然不是完全圆形。

我为此使用 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>

最佳答案

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

如果将描边宽度设置为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 描边宽度,让我们中断选择并将轴向下移动其 描边宽度 的一半(这里我将宽度增加到 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>

关于javascript - 轴-范围- slider 对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51782973/

相关文章:

javascript - D3 js 正交标记旋转

javascript - d3js v5 调整分组条形图大小

javascript - 用于多个 div 的 jQuery slideToggle

javascript - Angular 显示带有额外零的数字

javascript - 带反向索引的子串

javascript - 提交按钮 .submit 恢复为默认行为

html - 在边框折叠表中使边框长度相同

html - 为移动端改编一些代码使用 CSS/HTML

html - 你怎么能在滚动的固定导航标题后面对文本/div 进行 z-index?

javascript - 在 d3 中动态更新适用于圆形,但不适用于外部 SVG