Выравнивание оси-диапазона-ползунка

Мне нужно разработать компонент d3, подобный показанному на рисунке ниже.

Выравнивание оси-диапазона-ползунка

Я сослался на существующий образец кода из эта ссылка и изменил его, чтобы создать что-то вроде этого.

Выравнивание оси-диапазона-ползунка

Слева изменялась ширина оси, которую я пытался изменить, изменив свойство ширина штриха для класс домена. Однако я закончил примерно так.

Выравнивание оси-диапазона-ползунка

Проблемы:

  1. Ручка ползунка не совмещена с осью.
  2. Цвет оси отпечатывается на слайдере.
  3. Концы оси не идеально круглые.

Вопросов:

  1. Я не могу понять, что мне переводить / преобразовывать, чтобы выровнять ползунки и ось.
  2. Я попробовал повозиться со значениями непрозрачности, но это не помогло.
  3. Я установил штрих-линия на круглый, но он все еще не полностью круглый.

Я использую для этого d3 v4. И 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.info('range->'+range)
      d3.selectAll("span")
          .text(function(d, i) {
            console.info(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>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
437
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ось и кисть на самом деле идеально выровнены!

Вы можете увидеть это, если установите для 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.info('range->'+range)
      d3.selectAll("span")
          .text(function(d, i) {
            console.info(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.info('range->' + range)
    d3.selectAll("span")
      .text(function(d, i) {
        console.info(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>

Путь на оси представляет собой замкнутую форму, и обводка создает проблемы. Также вам не нужны галочки, так почему бы не нарисовать «ось» самостоятельно. Тогда округлый край будет нарисован правильно.

    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.info('range->'+range)
      d3.selectAll("span")
          .text(function(d, i) {
            //console.info(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>

Другие вопросы по теме