Использование ползунка для изменения размера массива и отображения в виде динамической svg-гистограммы?

javascript #html #css #d3.js

#javascript #HTML #css #d3.js

Вопрос:

Итак, я пытаюсь создать визуализатор сортировки, подобный тому, который видел здесь Клемент Михайлеску: https://clementmihailescu.github.io/Sorting-Visualizer /

Я использую не react, а чистый javascript, css, html и d3.js библиотека.

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

Моя другая функция — это функция обновления, которая привязана к прослушивателю событий, который обнаруживает всякий раз, когда пользователь перемещает ползунок, и должен динамически изменять размер массива (и, следовательно, изменять количество столбцов / прямоугольников, отображаемых на экране). Моя главная проблема в том, что мой код работает нормально, когда я перемещаю ползунок ниже максимального значения 100, и он соответственно удаляет полосы с экрана вплоть до 1. Однако, когда я перемещаю ползунок обратно вверх, столбцы не отображаются повторно. Я добавил циклы for в консоль.запишите выходные данные значений массива, и я вижу, что они все еще существуют и изменяются, но я не могу понять, как заставить столбцы svg отображаться. Кнопка «создать массив» отлично работает и при изменении размера, но она также изменяет весь массив, а не только диапазон, определяемый ползунком. Код размещен ниже:

 const DUMMY_DATA = [{
    id: 'd1',
    value: 10
  },
  {
    id: 'd2',
    value: 11
  },
  {
    id: 'd3',
    value: 12
  },
  {
    id: 'd4',
    value: 6
  },
  {
    id: 'd5',
    value: 7
  },
  {
    id: 'd6',
    value: 13
  },
  {
    id: 'd7',
    value: 4
  },
  {
    id: 'd8',
    value: 2
  },
  {
    id: 'd9',
    value: 14
  },
  {
    id: 'd10',
    value: 3
  },
  {
    id: 'd11',
    value: 7
  },
  {
    id: 'd12',
    value: 6
  },
  {
    id: 'd13',
    value: 7
  },
  {
    id: 'd14',
    value: 9
  },
  {
    id: 'd15',
    value: 9
  },
  {
    id: 'd16',
    value: 2
  },
  {
    id: 'd17',
    value: 1
  },
  {
    id: 'd18',
    value: 15
  },
  {
    id: 'd19',
    value: 12
  },
  {
    id: 'd20',
    value: 7
  },
  {
    id: 'd21',
    value: 7
  },
  {
    id: 'd22',
    value: 8
  },
  {
    id: 'd23',
    value: 5
  },
  {
    id: 'd24',
    value: 5
  },
  {
    id: 'd25',
    value: 11
  },
  {
    id: 'd26',
    value: 11
  },
  {
    id: 'd27',
    value: 12
  },
  {
    id: 'd28',
    value: 10
  },
  {
    id: 'd29',
    value: 6
  },
  {
    id: 'd30',
    value: 13
  },
  {
    id: 'd31',
    value: 14
  },
  {
    id: 'd32',
    value: 2
  },
  {
    id: 'd33',
    value: 12
  },
  {
    id: 'd34',
    value: 11
  },
  {
    id: 'd35',
    value: 10
  },
  {
    id: 'd36',
    value: 5
  },
  {
    id: 'd37',
    value: 6
  },
  {
    id: 'd38',
    value: 7
  },
  {
    id: 'd39',
    value: 14
  },
  {
    id: 'd40',
    value: 14
  },
  {
    id: 'd41',
    value: 1
  },
  {
    id: 'd42',
    value: 0
  },
  {
    id: 'd43',
    value: 11
  },
  {
    id: 'd44',
    value: 15
  },
  {
    id: 'd45',
    value: 7
  },
  {
    id: 'd46',
    value: 13
  },
  {
    id: 'd47',
    value: 4
  },
  {
    id: 'd48',
    value: 4
  },
  {
    id: 'd49',
    value: 4
  },
  {
    id: 'd50',
    value: 11
  },
  {
    id: 'd51',
    value: 10
  },
  {
    id: 'd52',
    value: 5
  },
  {
    id: 'd53',
    value: 1
  },
  {
    id: 'd54',
    value: 6
  },
  {
    id: 'd55',
    value: 7
  },
  {
    id: 'd56',
    value: 0
  },
  {
    id: 'd57',
    value: 0
  },
  {
    id: 'd58',
    value: 2
  },
  {
    id: 'd59',
    value: 10
  },
  {
    id: 'd60',
    value: 11
  },
  {
    id: 'd61',
    value: 3
  },
  {
    id: 'd62',
    value: 13
  },
  {
    id: 'd63',
    value: 7
  },
  {
    id: 'd64',
    value: 13
  },
  {
    id: 'd65',
    value: 2
  },
  {
    id: 'd66',
    value: 2
  },
  {
    id: 'd67',
    value: 1
  },
  {
    id: 'd68',
    value: 2
  },
  {
    id: 'd69',
    value: 3
  },
  {
    id: 'd70',
    value: 7
  },
  {
    id: 'd71',
    value: 6
  },
  {
    id: 'd72',
    value: 10
  },
  {
    id: 'd73',
    value: 12
  },
  {
    id: 'd74',
    value: 2
  },
  {
    id: 'd75',
    value: 10
  },
  {
    id: 'd76',
    value: 11
  },
  {
    id: 'd77',
    value: 12
  },
  {
    id: 'd78',
    value: 2
  },
  {
    id: 'd79',
    value: 3
  },
  {
    id: 'd80',
    value: 13
  },
  {
    id: 'd81',
    value: 14
  },
  {
    id: 'd82',
    value: 12
  },
  {
    id: 'd83',
    value: 0
  },
  {
    id: 'd84',
    value: 11
  },
  {
    id: 'd85',
    value: 2
  },
  {
    id: 'd86',
    value: 5
  },
  {
    id: 'd87',
    value: 7
  },
  {
    id: 'd88',
    value: 3
  },
  {
    id: 'd89',
    value: 14
  },
  {
    id: 'd90',
    value: 12
  },
  {
    id: 'd91',
    value: 10
  },
  {
    id: 'd92',
    value: 10
  },
  {
    id: 'd93',
    value: 10
  },
  {
    id: 'd94',
    value: 6
  },
  {
    id: 'd95',
    value: 7
  },
  {
    id: 'd96',
    value: 13
  },
  {
    id: 'd97',
    value: 0
  },
  {
    id: 'd98',
    value: 2
  },
  {
    id: 'd99',
    value: 1
  },
  {
    id: 'd100',
    value: 11
  },
];

const xScale = d3
  .scaleBand() //Gives all bars/items the same width
  .domain(DUMMY_DATA.map((dataPoint) => dataPoint.id)) //Tells scaleBand() how many data points there are based on the number of ids
  .rangeRound([0, 1000]) //Sets the range of the area the bars are generated in, i.e. the width of the container
  .padding(0.1); //Puts padding between the bars
const yScale = d3
  .scaleLinear() //Gives y coordinates function to position the heights of the bars
  .domain([0, 15]) //Specifies the min and max range of height values of the bars
  .range([600, 0]); //Sets range of the height of the container that the bars will sit in

const container = d3.select('svg')
  .classed('container', true);

const bars = container
  .selectAll('.bar')
  .data(DUMMY_DATA)
  .enter()
  .append('rect')
  .classed('bar', true)
  .attr('width', xScale.bandwidth()) //Takes available width and divides by the number of data points (ids) to give equal widths
  .attr('height', (data) => 600 - yScale(data.value))
  .attr('x', data => xScale(data.id))
  .attr('y', data => yScale(data.value));


/////////////////////////////////////////////   Generate button functions    ///////////////////////////////////////////////////////
// Grabs the "Generate Array" button
var newarr = document.getElementById("generate-array");
newarr.onclick = function() {
  //Find index of specific object using findIndex method. 
  objIndex = DUMMY_DATA.findIndex((obj => obj.id));

  //Log object to Console.
  for (objIndex in DUMMY_DATA) {
    console.log("Before update: ", DUMMY_DATA[objIndex])
  }

  //Update object's value properties.
  DUMMY_DATA.forEach(obj => {
    for (var i = 0; i < DUMMY_DATA.length; i  ) {
      DUMMY_DATA[i].value = Math.floor(Math.random() * 16);
    }
  });

  //Updates the bars with newly generated values
  container
    .selectAll('.bar')
    .data(DUMMY_DATA)
    .transition()
    .attr('width', xScale.bandwidth())
    .attr('height', (data) => 600 - yScale(data.value))
    .attr('x', data => xScale(data.id))
    .attr('y', data => yScale(data.value));

  //Log object to console again.
  for (objIndex in DUMMY_DATA) {
    console.log("After update: ", DUMMY_DATA[objIndex])
  }

  container.exit()
    .remove()
};

/////////////////////////////////////////////   Slider functions    ///////////////////////////////////////////////////////
var slider = document.getElementById("myRange");
var output = document.getElementById("demo");

// Display the default slider value and print to console
output.innerHTML = slider.value;
console.log(slider.value)

update = () => {

  //Output currently updated slider value and print to console
  output.innerHTML = slider.value;
  console.log(slider.value)

  //Take portion of original dataset
  SLIDER_DATA = DUMMY_DATA.splice(0, slider.value)

  // Create the u variable
  var u = container.selectAll(".bar")
    .data(SLIDER_DATA)

  //Update object's value properties.
  SLIDER_DATA.forEach(obj => {
    for (var i = 0; i < SLIDER_DATA.length; i  ) {
      // console.log(`${key}: ${obj[key]}`);
      SLIDER_DATA[i].value = Math.floor(Math.random() * 16);

    }
  });

  //Updates the bars with newly generated values
  u
    .transition()
    .attr('width', xScale.bandwidth())
    .attr('height', (data) => 600 - yScale(data.value))
    .attr('x', data => xScale(data.id))
    .attr('y', data => yScale(data.value))

  // If less group in the new dataset, I delete the ones not in use anymore
  u
    .exit()
    .transition() // and apply changes to all of them
    .duration(1000)
    .style("opacity", 0)
    .remove()

  DUMMY_DATA.splice(0, 0, ...SLIDER_DATA);

}
//Event listener to detect when slider is being used and invoke update
slider.addEventListener('input', update); 
 .container {
  display: block;
  margin: auto;
  width: 1000px;
  height: 600px;
  border: 1px solid #720570;
}

.bar {
  fill: #720570;
} 
 <!-- Load d3.js -->
<script src="https://d3js.org/d3.v7.min.js" charset="utf-8"></script>
<script src="https://d3js.org/d3-scale.v3.min.js"></script>

<div id="toolbar">
  <button type="button" class="algorithmButton" id="generate-array">Generate Array</button>
  <div class="slidecontainer">
    <input type="range" min="1" max="100" value="50" class="slider" id="myRange">
    <p style="padding-left: 8px">Value:<span id="demo" style="padding-left: 5px"></span></p>
  </div>
</div>

<!-- Create a div where the graph will take place -->
<svg></svg> 

Я новичок как в javascript, так и d3.js и это первая программа, которую я действительно пытался написать с помощью любого из них, но раньше я использовал python и c . Я не уверен, связана ли проблема с моим javascript или моими манипуляциями с svg с d3.js или что. Любая помощь очень ценится, поскольку я пытаюсь разобраться в этом уже около недели!

Комментарии:

1. Манипулирование массивом или объектами массива на месте обычно считается источником ошибок, и его следует избегать. Это несколько абстрактно и выходит за рамки, но взгляните, например, на глубокое и мелкое копирование.

2. Как уже указывалось, вы, вероятно, могли бы легче достичь желаемых результатов, не меняя массив на месте. Столбцы не добавляются, потому что у вас нет .enter().append('rect') событий изменения.

3. Большое спасибо вам обоим, я заставил это работать! @SmokeyShakers Я все еще привыкаю d3.js поэтому я забыл снова добавить прямоугольники, и теперь я использую slice вместо splice, чтобы исходный массив не был изменен!

4. Это здорово. По моему опыту, чем больше вы можете охватить весь ввод, выход, соединение и т. Д., Тем Проще использовать D3.