Beispiel:Pfad-Konvertierer.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

<!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>