Проблема в том, что когда указатель перемещается влево или вверх от начальной точки, ваш расчет ширины и высоты становится отрицательным, и 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>