Создайте масштаб, состоящий из нескольких доменов

#d3.js

#d3.js

Вопрос:

Как я могу определить шкалу D3 с несколькими доменами, составляющими один диапазон?

Пример

В этом примере у меня есть 3 домена разного размера, и я хочу, чтобы они составляли равные трети диапазона.

Что я хочу сделать:

 const yScale = linearScale()
  .domain([ [0, 10], [11, 50], [51, 500] ])
  .range([0, 100])
  

Что я сейчас делаю:

 const yScale1 = linearScale().domain([0, 10]).range([0, 33]);
const yScale2 = linearScale().domain([11, 50]).range([34, 66]);
const yScale3 = linearScale().domain([51, 500]).range([67, 100]);

if (val > 51) { 
  return yScale3(val);
} else if (val > 11) { 
  return yScale2(val);
} else {
  return yScale1(val);
}
  

Ответ №1:

Линейные шкалы D3 позволяют передавать несколько значений как для домена, так и для диапазона. Как описано в API:

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

В вашем случае это были бы:

 const scale = d3.scaleLinear()
    .domain([0,10,50,500])
    .range([0, 33.3, 66.6, 100]);
  

В качестве альтернативы, однострочный вариант с использованием сокращений D3 версии 5.8:

 const scale = d3.scaleLinear([0,10,50,500], [0, 33.3, 66.6, 100]);
  

Давайте посмотрим на это в действии:

 const svg = d3.select("svg");
const scale = d3.scaleLinear()
  .domain([0, 10, 50, 500])
  .range([0, 33.3, 66.6, 100]);
const circles = svg.selectAll(null)
  .data(d3.range(0, 500, 2))
  .enter()
  .append("circle")
  .attr("r", 2)
  .attr("cx", (_, i) => 5   i * 2   1)
  .attr("cy", d => 105 - scale(d));  
 svg {
  background-color: wheat;
}

circle {
  fill: none;
  stroke: gray;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="510" height="110"></svg>  

Конечно, лучшей практикой было бы создавать диапазон динамически, основываясь на количестве элементов в домене:

 const scale = d3.scaleLinear()
  .domain([0, 10, 50, 500]);

scale.range(d3.range(0, 100   100 / (scale.domain().length - 1), 100 / (scale.domain().length - 1)))

console.log(scale.range())  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>