Beispiel:SVG-10-arcs.html
<!DOCTYPE html> <html lang="de" > <head>
<meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" media="screen" href="./Beispiel:Grundlayout.css"> <title>Arc Bogen-Generator</title>
<style>
- code {
display: block; font: bold 2em monospace; padding: 1em 2em; color: #333;
}
- code::selection {
background: steelblue; color: #fff;
} .variant { fill:none;
stroke: #337599; stroke-width: 1; stroke-dasharray: 1 1;
}
.current {
stroke-width: 5; stroke: #c82f04; stroke-linecap: round; fill: none;
}
- grid path {
stroke:steelblue; stroke-dasharray:1 1; stroke-width:0.5; }
circle {
stroke-width: 1; stroke: steelblue; fill: hsl(201 50% 75%);
} circle:hover {
fill: steelblue; cursor: move;
}
- p1 {
fill:gold; }
- p1:hover {fill:orange;}
output { color:steelBlue; }
text {font-family:sans-serif;text-anchor:middle;font-size:12px;}
.inputs { display: grid; grid-template-columns: 30em 15em; gap: 1em; } fieldset { display: grid; grid-template-columns: 8em 1fr; gap: 1em; }
label {
vertical-align:middle;
display: inline-block;
}
label::after { content: ":"; }
</style>
</head>
<body>
<path d="<output id="path" contenteditable>M100,100 A100,100 0 1,1 200,200</output>"/>
<form class="inputs">
<fieldset>
<label for="rx">Radius X</label><input id="rx" type="range" min="0" max="150"/>
<label for="ry">Radius Y</label><input id="ry" type="range" min="0" max="150"/>
</fieldset>
<fieldset>
<label for="laf">Large arc flag</label><input id="laf" type="checkbox"/>
<label for="sf">Sweep flag</label><input id="sf" type="checkbox"/>
</fieldset>
</form>
<label>
<input type="checkbox" id="relative">
Relative Pfad-Kommandos
</label>
<svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 999 599" > <defs> <pattern id="grid" patternUnits="userSpaceOnUse" width="10" height="10" x="0" y="0"> <path d="M0,0 v10 h10" fill="none" /> </pattern> </defs> <rect id="background" width="100%" height="100%" fill="url(#grid)"></rect> <g id="main"> <g class="handle-group" data-label="P1" transform="translate(100 100)">
<circle id="p1" class="handle" r="9" /> <text dx="-2em" dy="-1.5em">Startpunkt</text>
</g> <g class="handle-group" data-label="P2" transform="translate(200 200)">
<circle id="p2" class="handle" r="9" /> <text dx="3em" dy="1.5em">Endpunkt</text>
</g>
<g class="arc-variants" aria-hidden="true"> <path class="arc variant" data-laf="0" data-sf="0" /> <path class="arc variant" data-laf="0" data-sf="1" /> <path class="arc variant" data-laf="1" data-sf="0" /> <path class="arc variant" data-laf="1" data-sf="1" /> </g> <path id="arc" class="arc current" d="M100,100 A100,100 0 1,1 200,200" />
</g> </svg>
<script> const svg = document.getElementById('svg');
const output = document.getElementById('path'); const relativeToggle = document.getElementById('relative');
const rxInput = document.getElementById('rx'); const ryInput = document.getElementById('ry'); const lafInput = document.getElementById('laf'); const sfInput = document.getElementById('sf');
const p1 = document.querySelector('#p1').parentElement; const p2 = document.querySelector('#p2').parentElement;
const arc = document.getElementById('arc'); const variants = document.querySelectorAll('.arc.variant');
let dragging = null;
/* --------------------------------------------------
Helpers
*/
function getPos(g) {
const t = g.transform.baseVal.consolidate().matrix;
return { x: Math.round(t.e), y: Math.round(t.f) };
}
function setPos(g, x, y) {
g.setAttribute('transform', `translate(${x} ${y})`);
}
function buildArc({ p1, p2, rx, ry, laf, sf, relative }) {
if (relative) {
return `m${p1.x},${p1.y} a${rx},${ry} 0 ${laf},${sf} ${p2.x - p1.x},${p2.y - p1.y}`;
}
return `M${p1.x},${p1.y} A${rx},${ry} 0 ${laf},${sf} ${p2.x},${p2.y}`;
}
/* --------------------------------------------------
Update everything
*/
function update() {
const start = getPos(p1); const end = getPos(p2);
const rx = +rxInput.value; const ry = +ryInput.value; const laf = lafInput.checked ? 1 : 0; const sf = sfInput.checked ? 1 : 0; const rel = relativeToggle.checked;
// main arc
const d = buildArc({ p1: start, p2: end, rx, ry, laf, sf, relative: rel });
arc.setAttribute('d', d);
output.textContent = d;
// variants
variants.forEach(v => {
const vLaf = v.dataset.laf;
const vSf = v.dataset.sf;
v.setAttribute(
'd',
buildArc({
p1: start,
p2: end,
rx, ry,
laf: vLaf,
sf: vSf,
relative: false
})
);
});
}
/* --------------------------------------------------
Drag handling
*/
svg.addEventListener('pointerdown', e => {
const g = e.target.closest('.handle-group');
if (!g) return;
dragging = g;
svg.setPointerCapture(e.pointerId);
});
svg.addEventListener('pointermove', e => {
if (!dragging) return; const pt = svg.createSVGPoint(); pt.x = e.clientX; pt.y = e.clientY; const svgPt = pt.matrixTransform(svg.getScreenCTM().inverse()); setPos(dragging, Math.round(svgPt.x), Math.round(svgPt.y)); update();
});
svg.addEventListener('pointerup', () => dragging = null); svg.addEventListener('pointerleave', () => dragging = null);
/* --------------------------------------------------
Inputs
*/
[rxInput, ryInput, lafInput, sfInput, relativeToggle]
.forEach(el => el.addEventListener('input', update));
/* --------------------------------------------------
Output editing
*/
output.addEventListener('input', () => {
const d = output.textContent.trim();
arc.setAttribute('d', d);
const match = d.match( /[Mm]\s*([\d.-]+),([\d.-]+)\s*[Aa]\s*([\d.-]+),([\d.-]+)\s*0\s*([01]),([01])\s*([\d.-]+),([\d.-]+)/ ); if (!match) return;
const [, x1, y1, rx, ry, laf, sf, x2, y2] = match.map(Number);
setPos(p1, x1, y1); setPos(p2, x2, y2);
rxInput.value = rx; ryInput.value = ry; lafInput.checked = !!laf; sfInput.checked = !!sf;
update();
});
update(); </script>
</body></html>