)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Animation Along Path SVG

  1. const el = document.body.appendChild(
  2.   document.createElement('div'));
  3.  
  4. el.innerHTML = `
  5.   <svg width="100%" height="100%" viewBox="0 0 550 496">
  6.     <path 
  7.       d="M 20 10 C 100 100 300 00 300 100 200 200 150 300 20 10" 
  8.       stroke="black" fill='none' vector-effect="non-scaling-stroke"/>
  9.     <circle cx="20" cy="10" r="6" fill="red" />
  10.   </svg>
  11.   <style>
  12.     svg, div, body, html {
  13.       overflow: visible; 
  14.       height: 100%; 
  15.       width: 100%;
  16.       margin: 0; padding: 0;
  17.     }
  18.   </style>
  19.  `;
  20.  
  21. const circle = el.querySelector('circle');
  22. const path = el.querySelector('path');
  23. const length = path.getTotalLength();
  24. let pos = 0;
  25.  
  26. function loop() {
  27.   pos += 3;
  28.   if (pos > length) {
  29.     pos = 0;
  30.   }
  31.   const point = path.getPointAtLength(pos);
  32.   circle.cx.baseVal.value = point.x;
  33.   circle.cy.baseVal.value = point.y;
  34.   requestAnimationFrame(loop);
  35. }
  36. loop();

SVG has an easy way to animate something along an arbitrary path… getPointAtLength

Object Getter Setter

  1. const book = {
  2.   get name() { 
  3.     return this.title ?? 'no title'; 
  4.   },
  5.   set name(val) { 
  6.     document.
  7.       body.
  8.       innerHTML += `name changed to "${val}"<br>`;
  9.     this.title = val; 
  10.   }
  11. };
  12.  
  13. console.log(book.name);
  14. book.name = 'Math Book';
  15. book.name = 'JavaScript Book';

Getter setter syntax for Object literal.

Ellipse Path SVG

  1. const ellipse = (cx, cy, rx, ry) => { 
  2.   if (ry == null) ry = rx;
  3.   return `
  4.     M${cx-rx} ${cy}a${rx} ${ry} 
  5.     0 1 0 ${rx*2} 0a${rx},${ry} 
  6.     0 1 0 -${rx*2},0`
  7. }
  8.  
  9. const svg = `
  10. <svg style="overflow:hidden;stroke:black;fill:none">
  11.   <path d="${ellipse(80, 50, 30)}" />
  12.   <path d="${ellipse(80, 120, 60, 20)}" />
  13.   <path d="${ellipse(150, 60, 20, 40)}" />
  14. </svg>`
  15.  
  16. document.body.innerHTML = svg;

Draw an ellipse inside an svg path. I saw this method over on this stackoverflow thread at some point and tweaked it a little bit. Very nice simple solution from user yogi

Catmull Rom to Bezier SVG

  1. // from: 
  2. //   https://github.com/DmitryBaranovskiy/raphael/blob/b5fdbdc9850827eb49622cd0bc81b26025f8128a/dev/raphael.core.js#L1019
  3. // and: 
  4. //   http://schepers.cc/getting-to-the-point
  5.  
  6. function catmullRom2bezier(crp, close) {
  7.   const d = [];
  8.   for (let i = 0, iLen = crp.length; iLen - 2 * !close > i; i += 2) {
  9.     const p = [
  10.       { x: crp[i - 2], y: crp[i - 1] },
  11.       { x: crp[i], y: crp[i + 1] },
  12.       { x: crp[i + 2], y: crp[i + 3] },
  13.       { x: crp[i + 4], y: crp[i + 5] }
  14.     ];
  15.     if (close) {
  16.       if (!i) {
  17.         p[0] = { x: crp[iLen - 2], y: crp[iLen - 1] };
  18.       } else if (iLen - 4 == i) {
  19.         p[3] = { x: crp[0], y: crp[1] };
  20.       } else if (iLen - 2 == i) {
  21.         p[2] = { x: crp[0], y: crp[1] };
  22.         p[3] = { x: crp[2], y: crp[3] };
  23.       }
  24.     } else {
  25.       if (iLen - 4 == i) {
  26.         p[3] = p[2];
  27.       } else if (!i) {
  28.         p[0] = { x: crp[i], y: crp[i + 1] };
  29.       }
  30.     }
  31.     d.push([
  32.       'C',
  33.       (-p[0].x + 6 * p[1].x + p[2].x) / 6,
  34.       (-p[0].y + 6 * p[1].y + p[2].y) / 6,
  35.       (p[1].x + 6 * p[2].x - p[3].x) / 6,
  36.       (p[1].y + 6 * p[2].y - p[3].y) / 6,
  37.       p[2].x,
  38.       p[2].y
  39.     ]);
  40.   }
  41.  
  42.   // really for demo purposes:
  43.  
  44.   // draw a tiny rect if less than 2 points
  45.   console.log(d.length);
  46.   if (d.length < 2) {
  47.     const x = crp[0];
  48.     const y = crp[1];
  49.     return `M ${x} ${y} h 1 v 1 h -1 v-1`;
  50.  
  51.     // draw a line if 2 points
  52.   } else if (d.length === 2) {
  53.     return `M ${crp[0]} ${crp[1]} L ` + crp.flat().join` `;
  54.   }
  55.   // draw curves if 3 or more (raphael only ever did this, other numbers of points
  56.   // were considered invalid)
  57.   return  `M ${crp[0]} ${crp[1]}` + d.flat().join` `;
  58. }
  59.  
  60. // try it out:
  61.  
  62. const pathCommands = catmullRom2bezier([
  63.   100, 100, 200, 100, 200, 300, 170, 150, 120, 180, 120, 120
  64.   ], true)
  65.  
  66. const svg = `
  67.   click 3 or more times anywhere<br>
  68.   <button>clear</button>
  69.   <svg>
  70.     <path d="M 100 100 ${pathCommands}" stroke="blue" fill="none" />
  71.   </svg>
  72.   <style>
  73.   svg {
  74.     position:absolute;
  75.     left:0;top:0;
  76.     overflow:visible;
  77.     pointer-events:none;
  78.   }
  79.   </style>
  80. `;
  81.  
  82. const div = document.body.appendChild(
  83.   document.createElement('div')
  84. );
  85. div.innerHTML = svg;
  86. const path = div.querySelector('path')
  87.  
  88. let pnts = [];
  89.  
  90. function isButton(e) {
  91.   if (e.target.tagName === 'BUTTON') {
  92.     pnts = []
  93.     path.setAttribute('d', 'M 0 0L0 0');
  94.     return true;
  95.   }
  96. }
  97.  
  98. document.addEventListener('click', e => {
  99.   if (isButton(e)) return;
  100.   pnts.push(e.clientX, e.clientY);
  101.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  102. });
  103.  
  104. document.addEventListener('touchend', e => {
  105.   if (isButton(e)) return;
  106.   pnts.push(e.touchs[0].clientX, e.touchs[0].e.clientY);
  107.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  108. });

Raphaël was my first deep dive into SVG. Back then, only VML was supported in older IE versions, so using Raphaël was key for tons of commercial work I did (Raphaël generated VML as well as SVG). This snippet is the Catmull Rom code from Raphaël. I’m pretty sure I didn’t know what a Catmull Rom spline was until I found out that using `R` in a path wasn’t supported natively in SVG… and was actually some magic thing that existed in Raphaël alone.

// curves // javascript // math // svg

John Wallis Pi (Wallis Product)

  1. /*
  2. It was always my affectation even from a child, in all pieces
  3. of Learning or Knowledge, not merely to learn by rote, which 
  4. is soon forgotten, but to know the grounds or reasons of what 
  5. I learn; to inform my Judgement, as well as furnish my Memory; 
  6. and thereby, make a better Impression on both.
  7.  
  8. - John Wallis
  9.  
  10. */
  11.  
  12. i = c = 1
  13. setInterval(_ => {
  14.   for (j = 0; j < 1000; j++, i += .5)
  15.     c *= (~~i * 2) / (~~(i - .5) * 2 + 1)
  16.  
  17.   console.log(c * 2 + '\n' + 3.141592653589793)
  18. }, 16);
π = 2
2 / 1
·
2 / 3
·
4 / 3
·
4 / 5
·
6 / 5
·
6 / 7
·
8 / 7
·
8 / 9
·   · · ·

I was at an old book store a few days ago and randomly picked up History of Mathematics Vol. I by David Eugene Smith 1923 for $7.50. After enjoying flipping around and reading different parts, I pulled up Wikipedia to read more about the author… From there I found some interesting quotes from talking about John Wallis – when I saw the Wallis Product I was inspired to write a quick snippet.

// golfed // javascript // math // pi
snippet.zone ~ 2021-24 /// {s/z}