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