JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<body>
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="Square" viewbox="0 0 50 50" fill="currentColor">
<rect x="0" y="0" width="50" height="50"/>
</symbol>
</svg>
<div class="window">
<svg id="parent" viewBox="0 0 200 200" width="100%" height="100%">
<use class="background" href="#Square" width="200" height="200" x="0" y="0"></use>
<use class="red_square draggable" href="#Square" width="25" height="25" x="0" y="0" transform="translate(0, 0)"></use>
<use class="blue_square draggable" href="#Square" width="25" height="25" x="100" y="100" transform="translate(0, 0)"></use>
</svg>
</div>
</body>
CSS
.red_square {
color: red;
}
.blue_square {
color: blue;
}
.background {
color: grey;
}
.draggable {
cursor: grab;
}
.window {
float: left;
width: 50%;
}
JavaScript
//When this is false, the blue square will always be drawn in front of the red square.
//When this is true, the dragged square will be on top, but touchmove won't work
let append_child = true;
let mid_drag = false, screen_transform, parent, dragged_element = null, mouse_piece_center_offset;
//Drag and drop functionality adapted from:
//Peter Collingridge
//http://www.petercollingridge.co.uk/tutorials/svg/interactive/dragging/
function Initialize()
{
parent = document.getElementById('parent');
screen_transform = parent.getScreenCTM();
parent.addEventListener('mousedown', Handle_DragStart);
parent.addEventListener('touchstart', Handle_DragStart);
parent.addEventListener('mousemove', Handle_Drag);
parent.addEventListener('touchmove', Handle_Drag);
parent.addEventListener('mouseup', Handle_DragEnd);
parent.addEventListener('touchend', Handle_DragEnd);
}
function Get_MousePosition(event)
{
if (event.touches) // if multiple touches on mobile, use the first one
event = event.touches[0];
let mouse_svg_x = (event.clientX - screen_transform.e) / screen_transform.a;
let mouse_svg_y = (event.clientY - screen_transform.f) / screen_transform.d;
return { x: mouse_svg_x, y: mouse_svg_y };
}
function Handle_DragStart(event)
{
if (mid_drag)
return;
if (!event.target.classList.contains("draggable"))
return;
mid_drag = true;
dragged_element = event.target;
let transform = dragged_element.transform.baseVal.getItem(0);
mouse_piece_center_offset = Get_MousePosition(event);
mouse_piece_center_offset.x -= transform.matrix.e;
mouse_piece_center_offset.y -= transform.matrix.f;
if (append_child)
parent.appendChild(event.target);
}
function Handle_Drag(event)
{
if (!mid_drag)
return;
//this cancels the offset between the mouse position and the center of the SVG
let mouse_drag_svg_positon = Get_MousePosition(event);
let newTranslateX = mouse_drag_svg_positon.x - mouse_piece_center_offset.x;
let newTranslateY =...