Sure! Here’s the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plotly Draggable Rectangle</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
#custom-plot {
width: 800px;
height: 800px;
}
</style>
</head>
<body>
<div id="custom-plot"></div>
<script>
// Initial rect coordinates in figure
const initialRect = { x0: 1, y0: 1, x1: 2, y1: 2 };
// Current rect coordinates in figure
let currentRect = { ...initialRect };
const data = [
{
x: [1, 2, 3, 4],
y: [1, 2, 3, 4],
mode: 'markers',
type: 'scatter'
}
];
const layout = {
dragmode: 'zoom',
xaxis: {
scaleanchor: 'y',
scaleratio: 1
},
yaxis: {
scaleanchor: 'x',
scaleratio: 1
}
};
// Create the Plotly figure
Plotly.newPlot('custom-plot', data, layout).then(plot => {
// Create the rect
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('fill', 'none');
rect.setAttribute('stroke', 'red');
rect.setAttribute('stroke-width', 2);
rect.style.cursor = 'move';
rect.style.pointerEvents = 'all';
// Add rect to the plot
plot.querySelector('svg').appendChild(rect);
let isDragging = false;
let offsetX, offsetY;
function updateRect(x0, y0, x1, y1) {
const xScale = plot._fullLayout.xaxis._length / (plot._fullLayout.xaxis.range[1] - plot._fullLayout.xaxis.range[0]);
const yScale = plot._fullLayout.yaxis._length / (plot._fullLayout.yaxis.range[1] - plot._fullLayout.yaxis.range[0]);
const x = (x0 - plot._fullLayout.xaxis.range[0]) * xScale + plot._fullLayout.margin.l;
const y = plot._fullLayout.height - ((y1 - plot._fullLayout.yaxis.range[0]) * yScale + plot._fullLayout.margin.b);
const width = (x1 - x0) * xScale;
const height = (y1 - y0) * yScale;
rect.setAttribute('x', x);
rect.setAttribute('y', y);
rect.setAttribute('width', width);
rect.setAttribute('height', height);
}
updateRect(currentRect.x0, currentRect.y0, currentRect.x1, currentRect.y1);
rect.addEventListener('mousedown', function (e) {
isDragging = true;
offsetX = e.clientX - parseFloat(rect.getAttribute('x'));
offsetY = e.clientY - parseFloat(rect.getAttribute('y'));
});
document.addEventListener('mousemove', function (e) {
if (isDragging) {
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
const x0 = plot._fullLayout.xaxis.range[0] + (x - plot._fullLayout.margin.l) / plot._fullLayout.xaxis._length * (plot._fullLayout.xaxis.range[1] - plot._fullLayout.xaxis.range[0]);
const y1 = plot._fullLayout.yaxis.range[0] + (plot._fullLayout.height - y - plot._fullLayout.margin.b) / plot._fullLayout.yaxis._length * (plot._fullLayout.yaxis.range[1] - plot._fullLayout.yaxis.range[0]);
const width = parseFloat(rect.getAttribute('width'));
const height = parseFloat(rect.getAttribute('height'));
currentRect = {
x0: x0,
y0: y1 - height / plot._fullLayout.yaxis._length * (plot._fullLayout.yaxis.range[1] - plot._fullLayout.yaxis.range[0]),
x1: x0 + width / plot._fullLayout.xaxis._length * (plot._fullLayout.xaxis.range[1] - plot._fullLayout.xaxis.range[0]),
y1: y1
};
updateRect(currentRect.x0, currentRect.y0, currentRect.x1, currentRect.y1);
}
});
document.addEventListener('mouseup', function () {
isDragging = false;
});
plot.on('plotly_relayout', function () {
updateRect(currentRect.x0, currentRect.y0, currentRect.x1, currentRect.y1);
});
});
</script>
</body>
</html>