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

SVG getScreenCTM

  1. const el = document.body.appendChild(
  2.   document.createElement`div`
  3. );
  4. el.innerHTML = `
  5. <svg width="200" height="200" viewBox="0 0 200 200">
  6.   <rect 
  7.     class="rect"
  8.     transform="translate(50, 50) scale(1.2) rotate(25)"
  9.     fill="purple"
  10.     x="0" y="0" width="50" height="50" />
  11. </svg>
  12. `;
  13.  
  14. const box = document.body.appendChild(
  15.   document.createElement`div`
  16. );
  17.  
  18. Object.assign(box.style, {
  19.   position: 'absolute',
  20.   left: 0, top: 0,
  21.   width: '50px',
  22.   height: '50px',
  23.   transformOrigin: '0 0',
  24.   outline: '5px solid red'
  25. });
  26.  
  27. const rect = document.querySelector('.rect');
  28. const {a, b, c, d, e, f} = rect.getScreenCTM()
  29.  
  30. box.style.transform = `
  31.   matrix(${[a, b, c, d, e, f]})
  32. `;

The transformation matrix of an SVG element can be obtained using getScreenCTM or getCTM. The latter of which will be relative to the SVG coordinate space, vs the coordinate space of the page.

Here we take the matrix data from getScreenCTM and use it on a div to place a border over an SVG rect node. This is great for layering HTML on top of SVG.

// dom // javascript // math // matrix // svg // tricks // ui

Wiggly Line on Canvas 2

  1. const canvas = document.body.appendChild(
  2.   document.createElement('canvas')
  3. );
  4. const c = canvas.getContext('2d');
  5. document.body.style.margin = 0;
  6.  
  7. function resize() {
  8.   canvas.width = innerWidth * 2;
  9.   canvas.height = innerHeight * 2;
  10.   canvas.style.width = innerWidth + 'px';
  11.   canvas.style.height = innerHeight + 'px';
  12. }
  13. addEventListener('resize', resize);
  14. resize();
  15.  
  16. const PAD = 50;
  17. const RAD = 2;
  18. const SPEED = 200;
  19. const TWO_PI = Math.PI * 2;
  20.  
  21. let mode = 'draw';
  22.  
  23. let t = Math.random() * TWO_PI, 
  24.     x = canvas.width / 2, 
  25.     y = canvas.height / 2,
  26.     vx = 0, vy = 0, ta = 0;
  27.  
  28. let solid = false;
  29. let dotMod = 3;
  30. function loop() {
  31.   if (Math.random() < .01) solid = !solid;
  32.   if (Math.random() < .01) dotMod = [2, 3, 6][Math.floor(Math.random() * 3)]
  33.  
  34.   for (var i = 0; i < SPEED; i++) {
  35.     t = Math.sin(ta) * TWO_PI;
  36.     vx = RAD * Math.cos(t);
  37.     vy = RAD * Math.sin(t);
  38.     ta += Math.random() * 0.1 - 0.05;
  39.     x += vx;
  40.     y += vy;
  41.  
  42.     if (Math.random() < 0.005) {
  43.       mode = 'no draw';
  44.     } else if (Math.random() < 0.005) {
  45.       mode = 'draw';
  46.     }
  47.  
  48.     if (mode === 'draw' && (solid || i % dotMod === 0)) {
  49.       c.fillStyle = 'black';
  50.       c.fillRect(x, y, 2, 2);
  51.     }
  52.  
  53.     if (x < -PAD) {
  54.       x = canvas.width + PAD;
  55.     } else if (x > canvas.width + PAD) {
  56.       x = -PAD;
  57.     }
  58.     if (y < -PAD) {
  59.       y = canvas.height + PAD;
  60.     } else if (y > canvas.height + PAD) {
  61.       y = -PAD;
  62.     }
  63.   }
  64.  
  65.   requestAnimationFrame(loop);
  66. }
  67. loop();

This is a variation on a post from awhile back. I was posting it over on dev.to and realized I wanted it to look a bit different.

Canvas Particle

  1. const canvas = document.createElement('canvas'),
  2.   c = canvas.getContext('2d');
  3.  
  4. canvas.width = 500;
  5. canvas.height = 500;
  6.  
  7. document.body.appendChild(canvas);
  8.  
  9. c.fillStyle = 'black';
  10. c.fillRect(0, 0, canvas.width, canvas.height);
  11.  
  12. let a = 0.29, b = 0.22;
  13.  
  14. function f(x, y) {
  15.   if (Math.random() < 0.001) b = Math.random();
  16.   return Math.cos((x + Math.sin(x) * 0.01 + Math.cos(x * a)) * b);
  17. }
  18.  
  19. let x = 1, y = 0;
  20.  
  21. setInterval(() => {
  22.   if (Math.random() < 0.03) {
  23.     x = 1;
  24.     y = 0;
  25.   }
  26.   if (Math.random() < 0.001) a = Math.random();
  27.   for (let i = 0; i < 1e3; i++) {
  28.     x = x + f(y);
  29.     y = y + f(x);
  30.     c.save();
  31.     c.translate(150, 250);
  32.     c.scale(0.5, 0.5);
  33.     c.fillStyle = 'rgba(255, 255, 255, 0.01)';
  34.     c.fillRect(x, y, 5, 5);
  35.     c.restore();
  36.   }
  37. }, 20);

A single particle moves around and leaves a trail

Elasticity With Trails

  1. let pointX = pointY = 0;
  2.  
  3. document.addEventListener('touchmove', 
  4.   e => e.preventDefault(), { passive: false });
  5.  
  6. document.addEventListener('mousemove', e => {
  7.   pointX = e.clientX;
  8.   pointY = e.clientY;
  9. });
  10.  
  11. document.addEventListener('touchmove', e => {
  12.   pointX = e.touches[0].clientX;
  13.   pointY = e.touches[0].clientY;
  14. });
  15.  
  16. let el = document.body.appendChild(
  17.   document.createElement`div`
  18. );
  19.  
  20. const size = 20;
  21. const halfSize = size / 2;
  22.  
  23. Object.assign(el.style, {
  24.   position: 'absolute',
  25.   width: `${size}px`,
  26.   height: `${size}px`,
  27.   background: 'red',
  28.   borderRadius: `${size}px`,
  29.   left: 0, top: 0
  30. });
  31.  
  32. let x = vx = y = vy = 0;
  33. const FADE_TIME = 800;
  34. const plotDot = (x, y) => {
  35.   const dot = document.body.appendChild(el.cloneNode());
  36.   const time = 
  37.   dot.style.transform += ' scale(.25)';
  38.   dot.style.transition = `opacity ${FADE_TIME}ms ease-out`;
  39.   window.requestAnimationFrame(() => {
  40.     dot.style.opacity = 0;
  41.     setTimeout(() => dot.parentNode.removeChild(dot), FADE_TIME);
  42.   })
  43. }
  44.  
  45. let ticks = 0;
  46. const loop = () => { 
  47.   vx = ((pointX - x) * .08 + vx) * .95;
  48.   vy = ((pointY - y) * .08 + vy) * .95;
  49.   x += vx;
  50.   y += vy;
  51.  
  52.   if (ticks++ % 2 === 0 && 
  53.     Math.abs(pointX - x) > 1 && 
  54.     Math.abs(pointY - y) > 1) {
  55.       plotDot();
  56.     }
  57.   el.style.transform = `translate(${x - halfSize}px, ${y - halfSize}px)`;
  58.   requestAnimationFrame(loop);
  59. }
  60. loop();
  61.  
  62. const info = document.body.appendChild(
  63.   document.createElement`div`
  64. );
  65. info.innerHTML = 'move mouse or finger left/right/up/down';

This is a variation on yesterdays post. This has elasticity on both axis and draws a trail of dots…

Elasticity

  1. let pointX = pointY = 0;
  2.  
  3. document.addEventListener('mousemove', e => {
  4.   pointX = e.clientX;
  5.   pointY = e.clientY;
  6. });
  7.  
  8. document.addEventListener('touchmove', e => {
  9.   pointX = e.touches[0].clientX
  10.   pointY = e.touches[0].clientY
  11. });
  12.  
  13. let el = document.body.appendChild(
  14.   document.createElement`div`
  15. );
  16.  
  17. const size = 20;
  18. const halfSize = size / 2;
  19.  
  20. Object.assign(el.style, {
  21.   position: 'absolute',
  22.   width: `${size}px`,
  23.   height: `${size}px`,
  24.   background: 'red',
  25.   left: 0, top: 0
  26. })
  27.  
  28. let x = vx = 0;
  29. const loop = () => { 
  30.   vx = ((pointX - x) * .2 + vx) * .79;
  31.   x += vx;
  32.   el.style.transform = `translate(${x - halfSize}px, 50px)`;
  33.   requestAnimationFrame(loop);
  34. }
  35. loop();
  36.  
  37. let info = document.body.appendChild(
  38.   document.createElement`div`
  39. );
  40. info.innerHTML = 'move mouse or finger left/right';

Basic interactive elasticity with mouse or touch

snippet.zone ~ 2021-24 /// {s/z}