Как я могу (в D3) изменить размер прямоугольника, оставаясь фиксированным в одной точке? - PullRequest
0 голосов
/ 28 мая 2020

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

Прямоугольник меняет свою форму слева направо, но я также хочу, чтобы он стал go справа налево, как только текущее значение x станет меньше, чем значение x фиксированной точки. Может кто-нибудь помочь мне с этой проблемой? Как я могу этого добиться? Большое спасибо!

const margin = {top: 10, right: 30, bottom: 100, left: 60},
        chartWidth = width  - margin.left - margin.right,
        chartHeight = height - margin.top - margin.bottom;
      let shouldAppear = false;

      const svg = d3.select('svg')
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform","translate(" + left + "," + top + ")");

        svg.append('rect')
        .attr('class', 'canvas')
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        
      svg.on('mousedown', function(){
        shouldAppear = true;
        const mouse = d3.mouse(this);
        rectangle.attr('y', mouse[1])
          .attr('x', mouse[0])
        
      })
      svg.on('mousemove', function(){
        const mouse = d3.mouse(this);
        
        if (shouldAppear){ 
          rectangle
              .attr('width', Math.abs(mouse[0] - rectangle.attr('x')))
              .attr('height', Math.abs(mouse[1] - rectangle.attr('y')))
          }
      }) 
      svg.on('mouseup', function(){
        shouldAppear = false;
        rectangle
        .attr('width', 0)
        .attr('height', 0)
      });
      let rectangle = svg
          .append('rect')
          .attr('class', 'rectangle')
          .attr('width', 0)
          .attr('height', 0)
          .style('fill','red')  
    }  

1 Ответ

1 голос
/ 28 мая 2020

Проблема в том, что когда указатель перемещается влево или вверх от начальной точки, ваш расчет ширины и высоты становится отрицательным, и svg не допускает отрицательных значений для width или height для rect элементов.

Решение состоит в том, чтобы сохранить начальную позицию мыши в функции mousedown как mousestart:

let mousestart; 
svg.on("mousedown", function () {
  shouldAppear = true;
  const mouse = d3.mouse(this);
  mousestart = mouse;
  rectangle.attr("y", mouse[1]).attr("x", mouse[0]);
});

Затем используйте положение mousestart, чтобы оценить, находится ли курсор на слева, справа, сверху или снизу от начальной позиции и создайте определенные c корректировки прямоугольника по мере необходимости в вашей mousemove функции.

svg.on("mousemove", function () {
  const mouse = d3.mouse(this);
  if (shouldAppear) {
    let x = mouse[0] - mousestart[0]; //negative values indicate cursor moved left
    let y = mouse[1] - mousestart[1]; //negative values indicate cursor moved up
    if (x >= 0 && y >= 0) { // if the cursor moved right and down from starting position
      rectangle
        .attr("width", Math.abs(mouse[0] - rectangle.attr("x")))
        .attr("height", Math.abs(mouse[1] - rectangle.attr("y")));
    } 
    if (x <= 0){ // if the cursor moved left
      rectangle
        .attr('x', mouse[0]) // move the rectangle to the new cursor x position
        .attr('width', mousestart[0] - mouse[0] ) // the width of the rectangle is now the difference between the starting point and current x point
        .attr("height", Math.abs(mouse[1] - rectangle.attr("y"))); // the height is calculated based on the difference between the current position and the rectangle y
    }
    if (y <= 0) { // if the cursor moved up similar calculations as above but in the y direction
      rectangle
        .attr('y', mouse[1])
        .attr('height', mousestart[1] - mouse[1] )
        .attr("width", Math.abs(mouse[0] - rectangle.attr("x")));
    }
    if (x <= 0 && y <= 0 ) { // if the cursor moved left and up similar calculations as above but in both x and y direction
      rectangle
        .attr('x', mouse[0])
        .attr('y', mouse[1])
        .attr('width', mousestart[0] - mouse[0] )
        .attr('height', mousestart[1] - mouse[1] )
    }
  }
});

Проверьте полный рабочий фрагмент:

let width = 400,
  height = 400;

const margin = { top: 10, right: 30, bottom: 100, left: 60 },
  chartWidth = width - margin.left - margin.right,
  chartHeight = height - margin.top - margin.bottom;
let shouldAppear = false;

const svg = d3
  .select("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg
  .append("rect")
  .attr("class", "canvas")
  .attr("width", chartWidth)
  .attr("height", chartHeight - 10);


let mousestart; 

svg.on("mousedown", function () {
  shouldAppear = true;
  const mouse = d3.mouse(this);
  mousestart = mouse;
  rectangle.attr("y", mouse[1]).attr("x", mouse[0]);
});
svg.on("mousemove", function () {
  const mouse = d3.mouse(this);
  if (shouldAppear) {
    let x = mouse[0] - mousestart[0]; //negative values indicate cursor moved left
    let y = mouse[1] - mousestart[1]; //negative values indicate cursor moved up
    if (x >= 0 && y >= 0) { // if the cursor moved right and down from starting position
      rectangle
        .attr("width", Math.abs(mouse[0] - rectangle.attr("x")))
        .attr("height", Math.abs(mouse[1] - rectangle.attr("y")));
    } 
    if (x <= 0){ // if the cursor moved left
      rectangle
        .attr('x', mouse[0]) // move the rectangle to the new cursor x position
        .attr('width', mousestart[0] - mouse[0] ) // the width of the rectangle is now the difference between the starting point and current x point
        .attr("height", Math.abs(mouse[1] - rectangle.attr("y"))); // the height is calculated based on the difference between the current position and the rectangle y
    }
    if (y <= 0) { // if the cursor moved up similar calculations as above but in the y direction
      rectangle
        .attr('y', mouse[1])
        .attr('height', mousestart[1] - mouse[1] )
        .attr("width", Math.abs(mouse[0] - rectangle.attr("x")));
    }
    if (x <= 0 && y <= 0 ) { // if the cursor moved left and up similar calculations as above but in both x and y direction
      rectangle
        .attr('x', mouse[0])
        .attr('y', mouse[1])
        .attr('width', mousestart[0] - mouse[0] )
        .attr('height', mousestart[1] - mouse[1] )
    }
  }
});
svg.on("mouseup", function () {
  shouldAppear = false;
  rectangle.attr("width", 0).attr("height", 0);
});
let rectangle = svg
  .append("rect")
  .attr("class", "rectangle")
  .attr("width", 0)
  .attr("height", 0)
  .style("fill", "red");
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

Обновление

Улучшение функции mousemove, которую легче читать, вычисляет элементы rect атрибуты rectx, recty, rectwidth, rectheight и используйте его для установки атрибутов. Обратите внимание, что вам все равно потребуется захватить координаты mousestart в функции mousedown, как показано выше:

svg.on("mousemove", function () {
  const mouse = d3.mouse(this);
  if (shouldAppear) {
    let rectx, recty, rectwidth, rectheight;
    if (mouse[0] < mousestart[0]) { // if cursor moved left
      rectx = mouse[0];
      rectwidth = mousestart[0] - mouse[0];
    } else {  // if cursor moved right
      rectx = mousestart[0];
      rectwidth = mouse[0] - mousestart[0];
    }

    if (mouse[1] < mousestart[1]) { // if cursor moved up
      recty = mouse[1];
      rectheight = mousestart[1] - mouse[1];
    } else {  // if cursor moved down
      recty = mousestart[1];
      rectheight = mouse[1] - mousestart[1];
    }

    rectangle
      .attr('x', rectx)
      .attr('y', recty)
      .attr('width', rectwidth)
      .attr('height', rectheight)
  }
});

См. Полный фрагмент:

let width = 400,
  height = 400;

const margin = { top: 10, right: 30, bottom: 100, left: 60 },
  chartWidth = width - margin.left - margin.right,
  chartHeight = height - margin.top - margin.bottom;
let shouldAppear = false;

const svg = d3
  .select("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg
  .append("rect")
  .attr("class", "canvas")
  .attr("width", chartWidth)
  .attr("height", chartHeight - 10);

let mousestart;

svg.on("mousedown", function () {
  shouldAppear = true;
  const mouse = d3.mouse(this);
  mousestart = mouse;
  rectangle.attr("y", mouse[1]).attr("x", mouse[0]);
});
svg.on("mousemove", function () {
  const mouse = d3.mouse(this);
  if (shouldAppear) {
    let rectx, recty, rectwidth, rectheight;
    if (mouse[0] < mousestart[0]) { // if cursor moved left
      rectx = mouse[0];
      rectwidth = mousestart[0] - mouse[0];
    } else {  // if cursor moved right
      rectx = mousestart[0];
      rectwidth = mouse[0] - mousestart[0];
    }

    if (mouse[1] < mousestart[1]) { // if cursor moved up
      recty = mouse[1];
      rectheight = mousestart[1] - mouse[1];
    } else {  // if cursor moved down
      recty = mousestart[1];
      rectheight = mouse[1] - mousestart[1];
    }
    
    rectangle
      .attr('x', rectx)
      .attr('y', recty)
      .attr('width', rectwidth)
      .attr('height', rectheight)
  }
});
svg.on("mouseup", function () {
  shouldAppear = false;
  rectangle.attr("width", 0).attr("height", 0);
});

let rectangle = svg
  .append("rect")
  .attr("class", "rectangle")
  .attr("width", 0)
  .attr("height", 0)
  .style("fill", "red");
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...