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

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

Wobbling Ball With Canvas

  1. // same as yesterday but with canvas instead of svg
  2. const canvas = document.createElement('canvas'),
  3.       c = canvas.getContext('2d');
  4. document.body.appendChild(canvas);
  5. document.body.style.margin = 0;
  6.  
  7. let w = window.innerWidth,
  8.     h = window.innerHeight,
  9.     x = w / 2,
  10.     y = h / 2,
  11.     vx = vy = dx = dy = 0,
  12.     damp = 0.99, div = 8, ticks = 0, 
  13.     wobbleChance = 0.03,
  14.     startTick = 50;
  15.  
  16. function loop() {
  17.  
  18.   w = window.innerWidth;
  19.   h = window.innerHeight;
  20.   radius = w * 0.05;
  21.   diam = radius * 2;
  22.   diam2x = diam * 2;
  23.  
  24.   if (x > w){
  25.     vx *= -1;
  26.     dx *= -1;
  27.     x = w;
  28.   } else if (x < 0){
  29.     vx *= -1;
  30.     dx *= -1;
  31.     x = 0;
  32.   }
  33.  
  34.   if (y > h) {
  35.     vy *= -1;
  36.     dy *= -1;
  37.     y = h;
  38.   } else if (y < 0) {
  39.     vy *= -1;
  40.     dy *= -1;
  41.     y = 0
  42.   } 
  43.  
  44.   if (
  45.     Math.random() < wobbleChance || 
  46.     ticks === startTick) {
  47.       dx += Math.random() * 10 - 5;
  48.       dy += Math.random() * 10 - 5;
  49.   }
  50.  
  51.   dx *= damp;
  52.   dy *= damp;
  53.  
  54.   vx += (dx - vx) / div;
  55.   vy += (dy - vy) / div;
  56.  
  57.   x += vx;
  58.   y += vy;
  59.  
  60.   // in most cases these days you
  61.   // just clear the whole canvas, but for
  62.   // this example we clear a rectangle around 
  63.   // the circle 
  64.   c.clearRect(
  65.     x - diam, 
  66.     y - diam, 
  67.     diam2x, 
  68.     diam2x
  69.   );
  70.  
  71.   // draw the path
  72.   c.fillStyle = 'red'
  73.   c.beginPath();
  74.   c.arc(x, y, radius, 0, Math.PI * 2, false);
  75.   c.fill();
  76.  
  77.   ticks++;
  78.   window.requestAnimationFrame(loop);
  79. }
  80. loop();
  81.  
  82. function resize() {
  83.   canvas.width = window.innerWidth;
  84.   canvas.height = window.innerHeight;
  85. }
  86. resize();
  87. window.addEventListener('resize', resize);

A wobbling ball with canvas.

Wobbling Ball with SVG

  1. const el = document.body.appendChild(
  2.   document.createElement('div'));
  3.  
  4. el.innerHTML = `
  5.   <svg width="100%" height="100%">
  6.     <circle 
  7.      id="circ" 
  8.      cx="0" cy="0" r="50"
  9.      fill="red" style="will-change: transform;"/>
  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. let w = window.innerWidth,
  22.     h = window.innerHeight,
  23.     x = w / 2,
  24.     y = h / 2,
  25.     vx = vy = dx = dy = 0,
  26.     damp = 0.99, div = 8, ticks = 0, 
  27.     wobbleChance = 0.03,
  28.     startTick = 50;
  29.  
  30. function loop() {
  31.   w = window.innerWidth;
  32.   h = window.innerHeight;
  33.  
  34.   if (x > w){
  35.     vx *= -1;
  36.     dx *= -1;
  37.     x = w;
  38.   } else if (x < 0){
  39.     vx *= -1;
  40.     dx *= -1;
  41.     x = 0;
  42.   }
  43.  
  44.   if (y > h) {
  45.     vy *= -1;
  46.     dy *= -1;
  47.     y = h;
  48.   } else if (y < 0) {
  49.     vy *= -1;
  50.     dy *= -1;
  51.     y = 0
  52.   } 
  53.  
  54.   if (
  55.     Math.random() < wobbleChance || 
  56.     ticks === startTick) {
  57.       dx += Math.random() * 10 - 5;
  58.       dy += Math.random() * 10 - 5;
  59.   }
  60.  
  61.   dx *= damp;
  62.   dy *= damp;
  63.  
  64.   vx += (dx - vx) / div;
  65.   vy += (dy - vy) / div;
  66.  
  67.   x += vx;
  68.   y += vy;
  69.  
  70.   circ.setAttribute('transform', `translate(${x} ${y})`);
  71.  
  72.   ticks++;
  73.   window.requestAnimationFrame(loop);
  74. }
  75. loop();
  76.  
  77. function resize() {
  78.   const radius = Math.min(w, h) * .05;
  79.  
  80.   // `window.circ` is the global id (⌐■_■)
  81.   circ.r.baseVal.value = radius;
  82. }
  83. resize();
  84. window.addEventListener('resize', resize);

A wobbling ball with svg.

snippet.zone /// {s/z}