d3 js масштабирование в одном направлении, панорамирование в обоих направлениях - PullRequest
2 голосов
/ 19 января 2020

Я пытаюсь построить диаграмму d3 js, которая масштабируется только по оси X, но позволяет выполнять панорамирование по обеим осям. Пример ниже дает желаемый эффект:

https://jsfiddle.net/xpr364uo/

Однако у меня возникают проблемы с переводом этого кода в мой собственный код. Во-первых, я рендеринг на холст, поэтому у меня нет возможности установить атрибут «трансформировать» в некотором элементе. Кроме того, мое масштабирование использует rescaleX / rescaleY на копиях весов, как и «новый способ» масштабирования с помощью d3-zoom, насколько я понимаю:

const zoomBehavior = zoom().on('zoom', () => {
    const xDomain = event.transform.rescaleX(x2).domain();
    const yDomain = event.transform.rescaleY(y2).domain();          
    xScale.domain(xDomain);
    yScale.domain(yDomain);
    render();
});

Это работает для масштабирования / панорамирования на обоих оси. Как я могу изменить его, чтобы получить такой же эффект, как в скрипке? Что я должен делать с deltaPanY (из скрипки) в моем коде?

1 Ответ

1 голос
/ 19 января 2020

Вы можете отслеживать второе преобразование масштаба (я назову это yTransform) и использовать его для изменения масштаба оси y. Поскольку вы хотите, чтобы x масштабировался нормально, вы все равно можете использовать d3.event.transform.rescaleX() для изменения масштаба по оси X, в то время как yTransform можно использовать для изменения масштаба по оси Y.

При панорамировании значение перевода y yTransform должно быть обновлено в соответствии с текущим состоянием масштабирования. И наоборот, при масштабировании следует использовать yTransform для отмены изменения y-преобразования состояния масштабирования.

Возможно что-то вроде:

var yTransform = d3.zoomIdentity; // initial state for the y transform

var zoom = d3.zoom()
  .on("zoom", function() {
    var t = d3.event.transform;  // zoom state
    x2 = t.rescaleX(x);          // rescale x as normal (t.y is irrelevant)

    // for a pan event, update the y translate
    if (d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;  

    // for a scroll, use the  current y translate
    else t.y = yTransform.y;        

    y2 = yTransform.rescaleY(y);   // rescale y.

    render();

  })

Значения k и x для yTranslate значения не имеют: масштаб всегда равен 1, так как мы не увеличиваем масштаб, а преобразование x не имеет значения для изменения масштаба по оси y. Выше не учитываются события двойного щелчка, но я добавлю это ниже .

var dots = d3.range(100)
  .map(function() {
     return {x: Math.random(), y: Math.random()}
  })
  
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();

var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");

// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
  g.call(d3.axisRight(y2))
  
  

render();

var yTransform = d3.zoomIdentity;

var zoom = d3.zoom()
  .on("zoom", function() {
    var t = d3.event.transform;
    x2 = t.rescaleX(x);

   // For dbl clicks, d3.event.sourceEvent is null.
   if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;  
   else t.y = yTransform.y;      

    y2 = yTransform.rescaleY(y);
   
    render();
    
  })

canvas.call(zoom);


  
function render() {
   context.clearRect(0,0,500,300);
   dots.forEach(function(d) {
      context.beginPath();
      context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
      context.stroke(); 
   })
   
   g.call(d3.axisRight(y2));
   
}
canvas, svg {
  position: absolute;
  top: 0;
  left: 0;
}
svg {
  pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>

Поскольку я изменяю свойства напрямую - это не самое идеальное.

В качестве альтернативы, мы могли бы отслеживать смещение перевода на y (разница между ay переводится с dbl щелчками / событиями колеса и без этих событий). Оба смещения y и y translate могут быть использованы для создания соответствующего преобразования масштабирования:

var yOffset = 0;
var lastY = 0;

var zoom = d3.zoom()
  .on("zoom", function() {
    var t = d3.event.transform;
    x2 = t.rescaleX(x);

   // For dbl clicks, d3.event.sourceEvent is null.
   if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
     lastY = t.y - yOffset;
     y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
   }
   else {
     yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
   }
    render();
})

var dots = d3.range(100)
  .map(function() {
     return {x: Math.random(), y: Math.random()}
  })
  
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();

var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");

// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
  g.call(d3.axisRight(y2))
  
  

render();

var yOffset = 0;
var lastY = 0;

var zoom = d3.zoom()
  .on("zoom", function() {
    var t = d3.event.transform;
    x2 = t.rescaleX(x);
    
   // For dbl clicks, d3.event.sourceEvent is null.
   if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
     lastY = t.y - yOffset;
     y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
   }
   else {
     yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
   }

    render();
    
  })

canvas.call(zoom);


  
function render() {
   context.clearRect(0,0,500,300);
   dots.forEach(function(d) {
      context.beginPath();
      context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
      context.stroke(); 
   })
   
   g.call(d3.axisRight(y2));
   
}
canvas, svg {
  position: absolute;
  top: 0;
  left: 0;
}
svg {
  pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...