1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| class HandwritingDrawing { constructor(svgElement) { this.svg = svgElement; this.isDrawing = false; this.isErasing = false; this.lastX = 0; this.lastY = 0; this.currentPath = null; this.eraseMode = false; this.eraserRect = null; this.initEvents(); }
initEvents() { this.svg.addEventListener("mousedown", (e) => this.startDrawing(e)); this.svg.addEventListener("mousemove", (e) => this.draw(e)); this.svg.addEventListener("mouseup", () => this.stopDrawing()); this.svg.addEventListener("mouseleave", () => this.stopDrawing());
this.svg.addEventListener("touchstart", (e) => { this.startDrawing(e); e.preventDefault(); }); this.svg.addEventListener("touchmove", (e) => { this.draw(e); e.preventDefault(); }); this.svg.addEventListener("touchend", () => this.stopDrawing()); }
startDrawing(e) { this.isDrawing = true; const { offsetX, offsetY } = this.getCoordinates(e); this.lastX = offsetX; this.lastY = offsetY; if (this.eraseMode) { this.isErasing = true; this.createEraserRectangle(offsetX, offsetY); } else { this.createNewPath(offsetX, offsetY); } }
draw(e) { if (!this.isDrawing) return; const { offsetX, offsetY } = this.getCoordinates(e); if (this.eraseMode && this.isErasing) { this.updateEraserRectangle(offsetX, offsetY); } else if (!this.eraseMode && this.currentPath) { this.updatePath(offsetX, offsetY); } }
stopDrawing() { this.isDrawing = false; if (this.eraseMode && this.isErasing && this.eraserRect) { this.eraseIntersectingPaths(); this.svg.removeChild(this.eraserRect); this.eraserRect = null; this.isErasing = false; } this.currentPath = null; }
createNewPath(x, y) { this.currentPath = document.createElementNS( "<http://www.w3.org/2000/svg>", "path" ); this.currentPath.setAttribute("stroke", "#000"); this.currentPath.setAttribute("stroke-width", "3"); this.currentPath.setAttribute("fill", "none"); this.currentPath.setAttribute("stroke-linecap", "round"); this.currentPath.setAttribute("stroke-linejoin", "round"); this.currentPath.setAttribute("d", `M${x},${y}`); this.svg.appendChild(this.currentPath); }
updatePath(x, y) { const newD = this.currentPath.getAttribute("d") + ` L${x},${y}`; this.currentPath.setAttribute("d", newD); this.lastX = x; this.lastY = y; }
createEraserRectangle(x, y) { this.eraserRect = document.createElementNS( "<http://www.w3.org/2000/svg>", "rect" ); this.eraserRect.setAttribute("x", x); this.eraserRect.setAttribute("y", y); this.eraserRect.setAttribute("width", 0); this.eraserRect.setAttribute("height", 0); this.eraserRect.classList.add("eraser-rectangle"); this.svg.appendChild(this.eraserRect); }
updateEraserRectangle(x, y) { const width = x - this.lastX; const height = y - this.lastY; this.eraserRect.setAttribute("width", Math.abs(width)); this.eraserRect.setAttribute("height", Math.abs(height)); this.eraserRect.setAttribute("x", Math.min(x, this.lastX)); this.eraserRect.setAttribute("y", Math.min(y, this.lastY)); }
eraseIntersectingPaths() { const rectX = parseFloat(this.eraserRect.getAttribute("x")); const rectY = parseFloat(this.eraserRect.getAttribute("y")); const rectWidth = parseFloat(this.eraserRect.getAttribute("width")); const rectHeight = parseFloat(this.eraserRect.getAttribute("height")); const elements = Array.from(this.svg.querySelectorAll("path")); elements.forEach((element) => { const bbox = element.getBBox(); if ( bbox.x < rectX + rectWidth && bbox.x + bbox.width > rectX && bbox.y < rectY + rectHeight && bbox.y + bbox.height > rectY ) { this.svg.removeChild(element); } }); }
getCoordinates(e) { if (e.touches) { const touch = e.touches[0]; const rect = this.svg.getBoundingClientRect(); return { offsetX: touch.clientX - rect.left, offsetY: touch.clientY - rect.top, }; } else { return { offsetX: e.offsetX, offsetY: e.offsetY }; } }
toggleEraseMode() { this.eraseMode = !this.eraseMode; } }
const svgElement = document.getElementById("drawing-svg"); const drawing = new HandwritingDrawing(svgElement);
|