Replaced canvas by SVG objects allowing direct DOM manipulation
This commit is contained in:
parent
834e19d9b8
commit
dcd471e2be
@ -17,7 +17,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Canvas, auf dem wir die Formen zeichnen -->
|
<!-- Canvas, auf dem wir die Formen zeichnen -->
|
||||||
<canvas id="shapesCanvas" width="600" height="400"></canvas>
|
<svg id="shapesCanvas" width="800" height="600"
|
||||||
|
style="border: 1px solid black;"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.1">
|
||||||
|
</svg>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@ -1,50 +1,68 @@
|
|||||||
// js/canvasManager.js
|
// js/canvasManager.js
|
||||||
|
|
||||||
|
let backgroundCanvas;
|
||||||
|
let lastShapesArray = [];
|
||||||
|
|
||||||
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet alle Shapes auf dem Canvas neu
|
* Renders all shapes into an SVG container
|
||||||
* @param {HTMLCanvasElement} canvas
|
* @param {SVGElement} container
|
||||||
* @param {Array} shapesArray - Array der Shapes aus dem Dokument
|
* @param {Array} shapesArray
|
||||||
|
* @param {string|null} movingShapeId
|
||||||
*/
|
*/
|
||||||
export function drawAllShapesOnCanvas(canvas, shapesArray) {
|
export function drawAllShapesOnCanvas(container, shapesArray, movingShapeId = null) {
|
||||||
const ctx = canvas.getContext("2d");
|
if (!container || !(container instanceof SVGElement)) {
|
||||||
|
return;
|
||||||
// Canvas leerräumen
|
}
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
// Create or update shapes
|
||||||
shapesArray.forEach(shape => {
|
shapesArray.forEach(shape => {
|
||||||
if (shape.type === "circle") {
|
let element = document.getElementById(shape.id);
|
||||||
drawCircle(ctx, shape);
|
|
||||||
} else if (shape.type === "triangle") {
|
if (!element) {
|
||||||
drawTriangle(ctx, shape);
|
if (shape.type === "circle") {
|
||||||
}
|
element = document.createElementNS(SVG_NS, "circle");
|
||||||
|
element.id = shape.id;
|
||||||
|
container.appendChild(element);
|
||||||
|
} else if (shape.type === "triangle") {
|
||||||
|
element = document.createElementNS(SVG_NS, "polygon");
|
||||||
|
element.id = shape.id;
|
||||||
|
container.appendChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update element attributes using setAttributeNS for SVG
|
||||||
|
if (shape.type === "circle") {
|
||||||
|
element.setAttributeNS(null, "cx", shape.x);
|
||||||
|
element.setAttributeNS(null, "cy", shape.y);
|
||||||
|
element.setAttributeNS(null, "r", shape.radius);
|
||||||
|
element.setAttributeNS(null, "fill", shape.color || "black");
|
||||||
|
element.setAttributeNS(null, "stroke", "black");
|
||||||
|
element.setAttributeNS(null, "stroke-width", "1");
|
||||||
|
} else if (shape.type === "triangle") {
|
||||||
|
const points = shape.coordinates
|
||||||
|
.map(p => `${p.x},${p.y}`)
|
||||||
|
.join(" ");
|
||||||
|
element.setAttributeNS(null, "points", points);
|
||||||
|
element.setAttributeNS(null, "fill", shape.color || "black");
|
||||||
|
element.setAttributeNS(null, "stroke", "black");
|
||||||
|
element.setAttributeNS(null, "stroke-width", "1");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// Only clean up if we have shapes to compare against
|
||||||
/**
|
if (shapesArray && Array.isArray(shapesArray)) {
|
||||||
* Zeichnet einen Kreis
|
const currentIds = shapesArray.map(shape => String(shape.id));
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
Array.from(container.children).forEach(element => {
|
||||||
* @param {Object} circle
|
if (!currentIds.includes(element.id)) {
|
||||||
*/
|
container.removeChild(element);
|
||||||
function drawCircle(ctx, circle) {
|
}
|
||||||
ctx.beginPath();
|
});
|
||||||
ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI);
|
}
|
||||||
ctx.fillStyle = circle.color || "black";
|
}
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zeichnet ein Dreieck
|
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
|
||||||
* @param {Object} triangle
|
|
||||||
*/
|
|
||||||
function drawTriangle(ctx, triangle) {
|
|
||||||
const [p1, p2, p3] = triangle.coordinates;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(p1.x, p1.y);
|
|
||||||
ctx.lineTo(p2.x, p2.y);
|
|
||||||
ctx.lineTo(p3.x, p3.y);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fillStyle = triangle.color || "black";
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
50
src/main.js
50
src/main.js
@ -7,7 +7,7 @@ let selectedShapeId = null;
|
|||||||
let offsetX = 0;
|
let offsetX = 0;
|
||||||
let offsetY = 0;
|
let offsetY = 0;
|
||||||
|
|
||||||
const canvas = document.getElementById("shapesCanvas")
|
const container = document.getElementById("shapesCanvas");
|
||||||
|
|
||||||
// Add throttle function at the top
|
// Add throttle function at the top
|
||||||
function throttle(func, limit) {
|
function throttle(func, limit) {
|
||||||
@ -26,13 +26,16 @@ const handle = initRepo()
|
|||||||
await handle.whenReady()
|
await handle.whenReady()
|
||||||
|
|
||||||
// draw current state
|
// draw current state
|
||||||
drawAllShapesOnCanvas(canvas, handle.docSync().shapes)
|
drawAllShapesOnCanvas(container, handle.docSync().shapes)
|
||||||
|
|
||||||
// 2. Subscribe so we can re-render whenever doc changes
|
// 2. Subscribe so we can re-render whenever doc changes
|
||||||
// Listen for 'change' events
|
// Listen for 'change' events
|
||||||
handle.on("change", ({doc}) => {
|
handle.on("change", ({doc}) => {
|
||||||
console.log("Document changed! Drawallshapes", doc)
|
console.log("Document changed! Drawallshapes", doc);
|
||||||
drawAllShapesOnCanvas(canvas, doc.shapes)
|
// Only redraw all if we're not currently dragging
|
||||||
|
if (!isDragging) {
|
||||||
|
drawAllShapesOnCanvas(container, doc.shapes);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 3. Buttons holen
|
// 3. Buttons holen
|
||||||
@ -51,44 +54,47 @@ addTriangleButton.addEventListener("click", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Mousedown: Prüfen, ob wir auf ein Shape klicken
|
// Mousedown: Prüfen, ob wir auf ein Shape klicken
|
||||||
canvas.addEventListener("mousedown", (e) => {
|
container.addEventListener("mousedown", (e) => {
|
||||||
const { offsetX: mouseX, offsetY: mouseY } = e;
|
// Get SVG coordinates
|
||||||
|
const pt = container.createSVGPoint();
|
||||||
// 1. Shape unter Maus suchen
|
pt.x = e.clientX;
|
||||||
const clickedShape = getShapeAtCoordinates(mouseX, mouseY);
|
pt.y = e.clientY;
|
||||||
console.log("Clicked shape:",clickedShape)
|
const svgP = pt.matrixTransform(container.getScreenCTM().inverse());
|
||||||
|
|
||||||
|
const clickedShape = getShapeAtCoordinates(svgP.x, svgP.y);
|
||||||
|
|
||||||
if (clickedShape) {
|
if (clickedShape) {
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
selectedShapeId = clickedShape.id;
|
selectedShapeId = clickedShape.id;
|
||||||
|
offsetX = svgP.x - clickedShape.x;
|
||||||
// Damit das Shape unter dem Mauszeiger bleibt, merken wir uns den "Offset"
|
offsetY = svgP.y - clickedShape.y;
|
||||||
offsetX = mouseX - clickedShape.x;
|
|
||||||
offsetY = mouseY - clickedShape.y;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Throttled version of moveShape
|
// Throttled version of moveShape
|
||||||
const throttledMoveShape = throttle((shapeId, x, y) => {
|
const throttledMoveShape = throttle((shapeId, x, y) => {
|
||||||
moveShape(shapeId, x, y);
|
moveShape(shapeId, x, y);
|
||||||
|
// Pass the moving shape ID to drawAllShapesOnCanvas
|
||||||
|
drawAllShapesOnCanvas(container, handle.docSync().shapes, shapeId);
|
||||||
}, 16); // 50ms throttle time (20 updates per second)
|
}, 16); // 50ms throttle time (20 updates per second)
|
||||||
|
|
||||||
// Mousemove: Shape verschieben, sofern dragging aktiv ist
|
// Mousemove: Shape verschieben, sofern dragging aktiv ist
|
||||||
canvas.addEventListener("mousemove", (e) => {
|
container.addEventListener("mousemove", (e) => {
|
||||||
if (!isDragging || selectedShapeId === null) return;
|
if (!isDragging || selectedShapeId === null) return;
|
||||||
|
|
||||||
const { offsetX: mouseX, offsetY: mouseY } = e;
|
const pt = container.createSVGPoint();
|
||||||
|
pt.x = e.clientX;
|
||||||
|
pt.y = e.clientY;
|
||||||
|
const svgP = pt.matrixTransform(container.getScreenCTM().inverse());
|
||||||
|
|
||||||
// Neue Koordinaten berechnen
|
const newX = svgP.x - offsetX;
|
||||||
const newX = mouseX - offsetX;
|
const newY = svgP.y - offsetY;
|
||||||
const newY = mouseY - offsetY;
|
|
||||||
|
|
||||||
// Use throttled version instead
|
|
||||||
throttledMoveShape(selectedShapeId, newX, newY);
|
throttledMoveShape(selectedShapeId, newX, newY);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mouseup: Dragging beenden
|
// Mouseup: Dragging beenden
|
||||||
canvas.addEventListener("mouseup", () => {
|
container.addEventListener("mouseup", () => {
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
selectedShapeId = null;
|
selectedShapeId = null;
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user