Beispiel:Pfad-Konvertierer.html
<!DOCTYPE html> <html lang="de"> <head>
<meta charset="UTF-8" />
<title>SVG-Pfad Konvertierer und Generator</title>
<style>
textarea {
width: 100%;
height: 10em;
font-family: monospace;
font-size: 1em;
margin-bottom: 1rem;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
margin-bottom: 1rem;
}
button {
padding: 0.5rem 1rem;
cursor: pointer;
}
label {
display: flex;
align-items: center;
gap: 0.5rem;
}
output {
min-width: 1.5rem;
text-align: center;
font-weight: bold;
}
body {
max-width: 60em;
margin: 2rem auto;
padding: 0 1rem;
display: grid; grid-template-columns: 1fr 1fr; gap:1em;
}
h1,.controls {grid-column: 1/ -1;}
#col label {font: bold 1.25em sans-serif;}
svg {border:thin dotted;}
</style>
</head> <body>
<fieldset>
<button id="toRelative">Absolut → Relativ</button>
</fieldset>
<fieldset>
<button id="round">Runden</button>
<label>
Genauigkeit
<input type="range" id="decimals" min="0" max="6" value="2" />
<output id="decOut">2</output>
</label>
</fieldset>
<label for="input">SVG Input</label>
<textarea id="input" placeholder="Füge deinen SVG-path (d-Attribut) ein!"></textarea> <label for="svginput">So sieht's aus</label>
<svg id="svginput" viewBox="0 0 500 500">
<path id="pathInput" fill="steelblue" fill-opacity="0.4" stroke="steelblue"/>
</svg>
<label for="output">Ergebnis</label>
<textarea id="output" placeholder="Ergebnis"></textarea> <label for="svgoutput">So sieht's aus</label> <svg id="svgoutput" viewBox="0 0 500 500"> <path id="pathOutput" fill="green" fill-opacity="0.4" stroke="green"/>
</svg>
<script>
function parsePath(d) {
const tokens = d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/g) || []; const result = []; let i = 0;
while (i < tokens.length) {
const cmd = tokens[i++];
const params = [];
while (i < tokens.length && !isNaN(tokens[i])) {
params.push(Number(tokens[i++]));
}
result.push({ cmd, params });
}
return result;
}
function roundNumbers(arr, decimals) {
const f = Math.pow(10, decimals); return arr.map(n => Math.round(n * f) / f);
}
function roundOnly(d, decimals) {
const f = Math.pow(10, decimals); return d.replace(/-?\d*\.?\d+(?:e[-+]?\d+)?/g, n => Math.round(Number(n) * f) / f );
}
// ========================= // Absolute → Relative // =========================
function toRelative(parsed, decimals) {
let x = 0, y = 0; let sx = 0, sy = 0;
const out = [];
parsed.forEach(seg => {
const { cmd, params } = seg;
const isAbs = cmd === cmd.toUpperCase();
const lc = cmd.toLowerCase();
if (lc === 'z') {
x = sx; y = sy;
out.push('z');
return;
}
// already relative → just round
if (!isAbs) {
out.push(cmd + roundNumbers(params, decimals).join(' '));
// update cursor for relative commands
if (lc === 'm' || lc === 'l') {
x += params[params.length - 2];
y += params[params.length - 1];
}
return;
}
let p = params.slice(); let res = [];
switch (cmd) {
case 'M':
for (let i = 0; i < p.length; i += 2) {
const dx = p[i] - x;
const dy = p[i + 1] - y;
res.push(dx, dy);
x = p[i]; y = p[i + 1];
if (i === 0) { sx = x; sy = y; }
}
out.push('m' + roundNumbers(res, decimals).join(' '));
break;
case 'L':
for (let i = 0; i < p.length; i += 2) {
res.push(p[i] - x, p[i + 1] - y);
x = p[i]; y = p[i + 1];
}
out.push('l' + roundNumbers(res, decimals).join(' '));
break;
case 'H':
res.push(p[0] - x);
x = p[0];
out.push('h' + roundNumbers(res, decimals));
break;
case 'V':
res.push(p[0] - y);
y = p[0];
out.push('v' + roundNumbers(res, decimals));
break;
case 'C':
for (let i = 0; i < p.length; i += 6) {
res.push(
p[i] - x, p[i + 1] - y,
p[i + 2] - x, p[i + 3] - y,
p[i + 4] - x, p[i + 5] - y
);
x = p[i + 4]; y = p[i + 5];
}
out.push('c' + roundNumbers(res, decimals).join(' '));
break;
default:
out.push(cmd + roundNumbers(params, decimals).join(' '));
}
});
return out.join(' ');
}
// ========================= // SVG rendering helpers // =========================
function renderPath(pathEl, d) {
if (!d || !d.trim()) {
pathEl.removeAttribute('d');
return;
}
pathEl.setAttribute('d', d);
}
function fitViewBox(svg, path) {
if (!path.hasAttribute('d')) return;
const box = path.getBBox();
svg.setAttribute(
'viewBox',
`${box.x - 10} ${box.y - 10} ${box.width + 20} ${box.height + 20}`
);
}
// ========================= // UI wiring // =========================
const input = document.getElementById('input'); const output = document.getElementById('output'); const decimals = document.getElementById('decimals'); const decOut = document.getElementById('decOut');
const svgIn = document.getElementById('svginput'); const svgOut = document.getElementById('svgoutput');
const pathIn = document.createElementNS('http://www.w3.org/2000/svg', 'path'); const pathOut = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathIn.setAttribute('fill', 'steelblue'); pathIn.setAttribute('fill-opacity', '0.4'); pathIn.setAttribute('stroke', 'steelblue');
pathOut.setAttribute('fill', 'green'); pathOut.setAttribute('fill-opacity', '0.4'); pathOut.setAttribute('stroke', 'green');
svgIn.appendChild(pathIn); svgOut.appendChild(pathOut);
// live input preview + clearing input.addEventListener('input', () => {
if (!input.value.trim()) {
output.value = ;
renderPath(pathIn, );
renderPath(pathOut, );
return;
}
renderPath(pathIn, input.value);
fitViewBox(svgIn, pathIn);
});
// decimals slider decimals.addEventListener('input', () => {
decOut.textContent = decimals.value;
});
// buttons document.getElementById('toRelative').addEventListener('click', () => {
if (!input.value.trim()) return; const parsed = parsePath(input.value); const d = toRelative(parsed, Number(decimals.value)); output.value = d; renderPath(pathOut, d); fitViewBox(svgOut, pathOut);
});
document.getElementById('round').addEventListener('click', () => {
if (!input.value.trim()) return; const d = roundOnly(input.value, Number(decimals.value)); output.value = d; renderPath(pathOut, d); fitViewBox(svgOut, pathOut);
}); </script>
</body> </html>