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

Complementary HSL

  1. const a = document.body.appendChild(document.createElement('div')),
  2.       b = document.body.appendChild(document.createElement('div'));
  3. let degA = degB = 0;
  4.  
  5. const size = {
  6.   width: '100px',
  7.   height: '100px'
  8. };
  9. Object.assign(a.style, size);
  10. Object.assign(b.style, size);
  11.  
  12. function loop() {
  13.   degA += 1;
  14.   degB = degA + 180;
  15.   a.style.background = `hsl(${degA}deg, 100%, 50%)`;
  16.   b.style.background = `hsl(${degB}deg, 100%, 50%)`;
  17.   requestAnimationFrame(loop);
  18. }
  19. loop();

In HSL a hue difference of 180 degrees between two values will create a set of complimentary colors.

// animation // color // css // dom // javascript // tricks // ui

Wiggly Line Canvas

  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;
  9.   canvas.height = innerHeight;
  10. }
  11. addEventListener('resize', resize);
  12. resize();
  13.  
  14. const PAD = 50;
  15. const RAD = 2;
  16. const SPEED = 20;
  17. const TWO_PI = Math.PI * 2;
  18.  
  19. let mode = 'draw';
  20.  
  21. let t = Math.random() * TWO_PI, 
  22.     x = innerWidth / 2, 
  23.     y = innerHeight / 2,
  24.     vx = 0, vy = 0, ta = 0;
  25.  
  26. function loop() {
  27.   for (var i = 0; i < SPEED; i++) {
  28.     t = Math.sin(ta) * TWO_PI;
  29.     vx = RAD * Math.cos(t);
  30.     vy = RAD * Math.sin(t);
  31.     ta += Math.random() * 0.1 - 0.05;
  32.     x += vx;
  33.     y += vy;
  34.  
  35.     if (Math.random() < 0.005) {
  36.       mode = 'no draw';
  37.     } else if (Math.random() < 0.005) {
  38.       mode = 'draw';
  39.     }
  40.  
  41.     if (mode === 'draw') {
  42.       c.fillStyle = 'red';
  43.       c.fillRect(x, y, 2, 2);
  44.     }
  45.  
  46.     if (x < -PAD) {
  47.       x = innerWidth + PAD;
  48.     } else if (x > innerWidth + PAD) {
  49.       x = -PAD;
  50.     }
  51.     if (y < -PAD) {
  52.       y = innerHeight + PAD;
  53.     } else if (y > innerHeight + PAD) {
  54.       y = -PAD;
  55.     }
  56.   }
  57.  
  58.   requestAnimationFrame(loop);
  59. }
  60. loop();

Recently saw this in some very old code – cool trick for moving things in a wiggly way – or in this case, drawing a wiggly line.

ClassName Behaviors

  1. const ui = document.body.appendChild(
  2.   document.createElement('div')
  3. );
  4.  
  5. ui.innerHTML = `
  6.   <button class="blue circle redBorder moveDown">click</button>
  7.   <button class="red circle sayHi">click</button>  
  8.   <button class="green circle noBorder moveDown sayHi randomBgColor">click</button>
  9.  
  10.   <style>
  11.     * { -webkit-user-select: none; user-select: none; }
  12.     body, html {
  13.       width: 100%; height: 100%;
  14.     }
  15.     button { 
  16.       position: relative;
  17.       top: 0;
  18.       margin: .5em; 
  19.       cursor: pointer; 
  20.       color: white;
  21.       transition: all 200ms ease-out;
  22.       text-shadow: 1px 2px 1px black;
  23.     }
  24.     .circle {
  25.       width: 50px;
  26.       height: 50px;
  27.       border-radius: 500px;
  28.     }
  29.     .blue {
  30.       background: #275ba1;
  31.     }
  32.     .red {
  33.       background: red;
  34.     }
  35.     .green {
  36.       background: green;
  37.     }
  38.     .redBorder {
  39.       border: 2px solid red;
  40.     }
  41.     .noBorder {
  42.       border: none;
  43.     }
  44.   </style>
  45. `;
  46.  
  47. const actions = {
  48.   moveDown(e) {
  49.     e.target.style.top = `${parseFloat(e.target.style.top || 0) + 30}px`;
  50.   },
  51.   sayHi(e) {
  52.     e.target.innerHTML = [
  53.       'hi', 'hello', 'hey', 'aloha', 'what\'s up'
  54.     ][
  55.       Math.floor(Math.random() * 5)
  56.     ];
  57.     e.target.style.transform = `
  58.       rotate(${Math.random() * 40 - 20}deg) 
  59.       scale(${Math.random() * .3 + 1})`
  60.   },
  61.   randomBgColor(e) {
  62.     const col = `hsl(${Math.random() * 360}deg, 50%, 50%)`
  63.     e.target.style.background = col;
  64.   }
  65. };
  66.  
  67. document.body.addEventListener('click', e => {
  68.   // combine as many actions as we want
  69.   [...e.target.classList].forEach(cls => { 
  70.     const action = actions[cls];
  71.     if (action != null) action(e);
  72.   });
  73. });

This snippet takes the ideas from yesterdays post and goes one level further. This associates behavior with class names, so the class names can be combined to mix and match behavior.

In this case, combining classes like this green circle noBorder moveDown sayHi randomBgColor will cause the element in question to “move down”, “say hi” and randomize its background color when it is clicked. Click the “Try it out” to get a better idea.

// animation // css // dom // events // javascript // tricks // ui

Creative Coding Auto-Painting

  1. Object.getOwnPropertyNames(Math).map(i => (this[i] = Math[i]))
  2. ;((
  3.   width = innerWidth * 2,
  4.   height = innerHeight * 2,
  5.   cnv = document.body.appendChild(
  6.     Object.assign(document.createElement('canvas'), {
  7.       width,
  8.       height
  9.     })
  10.   ),
  11.   c = cnv.getContext('2d'),
  12.   r = (n = 1) => Math.random() * n,
  13.   NUM = 50,
  14.   f = () => ({
  15.     ax: r(width),
  16.     ay: r(height),
  17.     x: 0,
  18.     y: 0,
  19.     T: r(9),
  20.     R: r(innerWidth * 0.8) + 40,
  21.     t: r(6),
  22.     C: round(r(255)),
  23.     m: r(5) + 1
  24.   }),
  25.   cs,
  26.   sn,
  27.   dx,
  28.   dy,
  29.   ns = [...Array(NUM)].map(f)
  30. ) => {
  31.   Object.assign(cnv.style, {
  32.     transformOrigin: '0 0',
  33.     transform: 'scale(.5)'
  34.   })
  35.   Object.assign(document.body.style, {
  36.     margin: 0,
  37.     padding: 0
  38.   })
  39.  
  40.   const clear = () => {
  41.     c.fillStyle = '#666668'
  42.     c.fillRect(0, 0, width, height)
  43.     c.globalAlpha = 0.5
  44.   }
  45.  
  46.   onresize = () => {
  47.     width = cnv.width = innerWidth * 2
  48.     height = cnv.height = innerHeight * 2
  49.     clear()
  50.   }
  51.  
  52.   clear()
  53.  
  54.   setInterval(() => {
  55.     for (i = 0; i < 30; i++) {
  56.       ns.map((n, i) => {
  57.         with (n) {
  58.           x = ax + R * cos(t)
  59.           y = ay + R * sin(t) * pow(sin(t * 0.5), m)
  60.           c.fillStyle = `rgba(${C},${C},${C},.02)`
  61.           ;(cs = cos(T)), (sn = sin(T)), (dx = x - ax), (dy = y - ay)
  62.           c.fillRect(cs * dx - sn * dy + ax, sn * dx + cs * dy + ay, 50, 50)
  63.           t += 0.1
  64.           R -= 0.01
  65.           if (R < 5) ns[i] = f()
  66.         }
  67.       })
  68.     }
  69.   }, 16)
  70. })()

Speed coded semi-golfed canvas texture. Best if viewed in fullscreen.

Quick Touch Events 2 (easing)

  1. // no scrolling on mobile
  2. document.addEventListener('touchmove', e => e.preventDefault(), {
  3.   passive: false
  4. });
  5.  
  6. const hasTouch =
  7.   navigator.maxTouchPoints != null && navigator.maxTouchPoints > 0;
  8. // looking forward to `navigator?.maxTouchPoints > 0`
  9. // being better supported
  10.  
  11. function touchify(e) {
  12.   const touch = [];
  13.   touch.x = touch.y = 0;
  14.  
  15.   if (e.touches != null && e.touches.length > 0) {
  16.     touch.x = e.touches[0].clientX;
  17.     touch.y = e.touches[0].clientY;
  18.     for (let i = 0; i < e.touches.length; i++) {
  19.       touch[i] = e.touches[i];
  20.     }
  21.   } else {
  22.     touch.x = e.clientX;
  23.     touch.y = e.clientY;
  24.     touch[0] = { clientX: e.clientX, clientY: e.clientY };
  25.   }
  26.   return touch;
  27. }
  28.  
  29. let moveOrTouchMove;
  30.  
  31. if (hasTouch) {
  32.   moveOrTouchMove = 'touchmove';
  33. } else {
  34.   moveOrTouchMove = 'mousemove';
  35. }
  36.  
  37. function dot(x, y, radius, color) {
  38.   const el = document.createElement('div');
  39.   const size = `${radius * 2}px`;
  40.   Object.assign(el.style, {
  41.     position: 'absolute',
  42.     left: `${x}px`,
  43.     top: `${y}px`,
  44.     width: size,
  45.     height: size,
  46.     transform: `translate(${-radius}px, ${-radius}px)`,
  47.     borderRadius: '50%',
  48.     background: color
  49.   });
  50.   el.classList.add('dot');
  51.   document.body.appendChild(el);
  52.   return el;
  53. }
  54.  
  55. let dotX = 100,
  56.   dotY = 100,
  57.   touchX = 200,
  58.   touchY = 150,
  59.   damp = 12,
  60.   dotEl = dot(dotX, dotY, 20, 'red');
  61.  
  62. document.addEventListener(moveOrTouchMove, e => {
  63.   const { x, y } = touchify(e);
  64.   touchX = x;
  65.   touchY = y;
  66. });
  67.  
  68. function loop() {
  69.   dotX += (touchX - dotX) / damp;
  70.   dotY += (touchY - dotY) / damp;
  71.   Object.assign(dotEl.style, {
  72.     left: `${dotX}px`,
  73.     top: `${dotY}px`
  74.   });
  75.   window.requestAnimationFrame(loop);
  76. }
  77. loop();

Move your mouse on desktop or your finger on mobile – watch the red dot follow…

This is a simpler version of some of the things used in yesterdays post – just a quick way to normalize touch events – just one of many ways to do this – for simple stuff this is my goto.

If you want to try it out on its own page – take a look here (specifically good for trying it on mobile).

You’ll probably want a to use a meta tag like this – for the full effect.

  1. <meta name="viewport" content="width=device-width, initial-scale=1.0">
snippet.zone ~ 2021-24 /// {s/z}