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

Character Controls

  1. // Character Controls "clean version" with Trails...
  2.  
  3. // dynamically handle keys instead of explicitly
  4. // checking each one
  5. const keys = {}
  6. document.addEventListener('keydown', e => {
  7.   e.preventDefault();
  8.  
  9.   // store a boolean on the `keys` object
  10.   // to keep track of whick keys are down
  11.   keys[e.key] = true;
  12. });
  13.  
  14. document.addEventListener('keyup', e => {
  15.   e.preventDefault();
  16.    keys[e.key] = false;
  17. });
  18.  
  19. // setup motion
  20. let x = 100;
  21. let y = 300;
  22. let vx = 4;
  23. let vy = -10;
  24.  
  25. // put gravity in a variable
  26. const GRAVITY = 1;
  27. // replace "magic numbers" with constants
  28. const FLOOR_BOUNCE = -.33;
  29. const JUMP_POWER = 15;
  30. const ARROW_VEL = 3;
  31. const VX_DECAY = .8;
  32.  
  33. const TRAIL_NUM = 10;
  34. const chars = [];
  35. // store the size of the character
  36. const charWidth = 50;
  37. const charHeight = 80;
  38. for (let i = 0; i < TRAIL_NUM; i++) {
  39.   // create the character
  40.   const char = document.createElement('div');
  41.   chars.push({ char, x, y });
  42.   Object.assign(char.style, {
  43.     position: 'absolute',
  44.     width: `${charWidth}px`,
  45.     height: `${charHeight}px`,
  46.     background: 'black',
  47.     // add border radius for no reason
  48.     borderTopLeftRadius: '20px',
  49.     borderTopRightRadius: '20px',
  50.     opacity: i === 0 ? 1 : .25 - (.25 / TRAIL_NUM) * i
  51.   });
  52.  
  53.   document.body.appendChild(char);
  54. }
  55. const char = chars[0].char
  56.  
  57. function setLocs({ x, y }) {
  58.   for (let i = 0; i < TRAIL_NUM; i++) {
  59.     const char = chars[i];
  60.     if (x != null) char.x = x;
  61.     if (y != null) char.y = y;
  62.   }
  63. }
  64.  
  65.  
  66. // some bounds variables for the screen
  67. let floor;
  68. let screenRight;
  69. let screenLeft;
  70.  
  71. // main loop
  72. function loop() {
  73.   // we want to recalculate these
  74.   // so things work when the screen resizes
  75.   floor = innerHeight - 80;
  76.   screenRight = innerWidth + 50;
  77.   screenLeft = -charWidth;
  78.  
  79.   // update the characters velocity
  80.   x += vx;
  81.   y += vy;
  82.  
  83.  
  84.   // handle floor and screen sides
  85.   if (y > floor) {
  86.     vy *= FLOOR_BOUNCE;
  87.     y = floor;
  88.  
  89.     // @TODO set char xs
  90.   }
  91.  
  92.   if (x > screenRight) {
  93.     x = screenLeft
  94.     setLocs({ x })
  95.   }
  96.  
  97.   if (x < screenLeft) {
  98.     x = screenRight;
  99.     setLocs({ x })
  100.   }
  101.  
  102.  
  103.   // update the characters velocity with arrow keys
  104.   if (keys['ArrowRight']) {
  105.     vx += ARROW_VEL;
  106.   }
  107.   if (keys['ArrowLeft']) {
  108.     vx -= ARROW_VEL;
  109.   }
  110.  
  111.   // character can only jump up when it is on the floor
  112.   if (keys['ArrowUp'] && y >= floor) {
  113.     vy -= JUMP_POWER;
  114.   }
  115.  
  116.   if (keys['ArrowDown']) {
  117.     vy += ARROW_VEL;
  118.   }
  119.  
  120.   vy += GRAVITY;
  121.   vx *= VX_DECAY
  122.  
  123.   // update the characters styles
  124.   Object.assign(
  125.     char.style, {
  126.       // use template string instead of x + 'px'
  127.       left: `${x}px`,
  128.       top: `${y}px`
  129.     }
  130.   );
  131.  
  132.   // update trails stuff
  133.   chars[0].x = x;
  134.   chars[0].y = y;
  135.   for (let i = 1; i < TRAIL_NUM; i++) {
  136.     const char = chars[i]
  137.     char.x += (chars[i - 1].x - char.x) / 4;
  138.     char.y += (chars[i - 1].y - char.y) / 4;
  139.  
  140.     Object.assign(
  141.       chars[i].char.style, {
  142.         // use template string instead of x + 'px'
  143.         left: `${char.x}px`,
  144.         top: `${char.y}px`
  145.       }
  146.     );
  147.   }
  148.  
  149.   requestAnimationFrame(loop);
  150. }
  151. loop();

Click once on the preview area to give keyboard focus – then use arrow keys to move the character. I recently created a tutorial about this over on dev.to… check it out…

Canvas Ring on Image

  1. const uploadInput = document.body.appendChild(
  2.   Object.assign(document.createElement('input'), {
  3.     type: 'file',
  4.     accept: 'image/png, image/jpeg'
  5.   })
  6. )
  7. uploadInput.style.display = 'block'
  8.  
  9. const canvas = document.body.appendChild(document.createElement('canvas'))
  10. const c = canvas.getContext('2d')
  11.  
  12. canvas.style.width = '70%'
  13.  
  14. let sampleSize
  15. const steps = 100
  16. const step = (Math.PI * 2) / steps
  17.  
  18. let interval
  19.  
  20. const ring = () => {
  21.   clearInterval(interval)
  22.   let x = imageEl.width / 2,
  23.     y = imageEl.height / 2,
  24.     rad = imageEl.width * 0.3,
  25.     theta = 0,
  26.     px,
  27.     py,
  28.     pxs = [],
  29.     spy = []
  30.   ;(pys = []), (im = []), (rects = [])
  31.  
  32.   for (let i = 0; i < steps; i++) {
  33.     px = x + rad * Math.cos(theta)
  34.     py = y + (rad / 2) * Math.sin(theta)
  35.     theta += step
  36.     pxs[i] = px
  37.     pys[i] = spy[i] = py
  38.     im[i] = c.getImageData(px, py, sampleSize, sampleSize)
  39.     rects[i] = [px, py, sampleSize, sampleSize]
  40.   }
  41.  
  42.   interval = setInterval(() => {
  43.     for (let i = 0; i < steps; i++) {
  44.       pys[i] -= 1
  45.       c.putImageData(im[i], pxs[i], pys[i])
  46.       v = (y - spy[i]) / rad
  47.       c.fillStyle = 'rgba(0,0,0,' + v + ')'
  48.       c.fillRect(pxs[i] - 1, pys[i], sampleSize + 2, sampleSize - 1)
  49.     }
  50.   }, 16)
  51. }
  52.  
  53. const imageEl = new Image()
  54. imageEl.src = 'https://snippet.zone/wp-content/uploads/2022/01/taho-scaled.jpg'
  55. imageEl.onload = () => {
  56.   canvas.width = imageEl.width
  57.   canvas.height = imageEl.height
  58.   c.drawImage(imageEl, 0, 0)
  59.   sampleSize = imageEl.width / 25
  60.  
  61.   ring()
  62. }
  63.  
  64. const reader = new FileReader()
  65.  
  66. reader.addEventListener('load', () => {
  67.   imageEl.src = reader.result
  68. })
  69.  
  70. uploadInput.addEventListener('change', e => {
  71.   const file = e.target.files[0]
  72.   if (file != null) {
  73.     reader.readAsDataURL(file)
  74.   }
  75. })

Upload an image and it will have a distortion ring drawn on it

Speed-coded Eye

  1. d = document
  2. b = d.body
  3. b.style.margin = 0
  4. with(Math) {
  5.   S = min(innerHeight * 2, innerWidth * 2)
  6.   hs = S / 2
  7.  
  8.   with(
  9.     b.appendChild(Object.assign(
  10.       d.createElement`canvas`, {
  11.         width: S,
  12.         height: S
  13.       })).getContext`2d`) {
  14.  
  15.     // array of xy coords, closed boolean
  16.     function bezierSkin(bez, closed = true) {
  17.       const avg = calcAvgs(bez);
  18.       const leng = bez.length;
  19.       let i, n;
  20.  
  21.       if (closed) {
  22.         moveTo(avg[0], avg[1]);
  23.         for (i = 2; i < leng; i += 2) {
  24.           n = i + 1;
  25.           quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  26.         }
  27.         quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
  28.       } else {
  29.         moveTo(bez[0], bez[1]);
  30.         lineTo(avg[0], avg[1]);
  31.         for (i = 2; i < leng - 2; i += 2) {
  32.           n = i + 1;
  33.           quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  34.         }
  35.         lineTo(bez[leng - 2], bez[leng - 1]);
  36.       }
  37.     }
  38.  
  39.     // create anchor points by averaging the control points
  40.     function calcAvgs(p) {
  41.       const avg = [];
  42.       const leng = p.length;
  43.       let prev;
  44.       for (var i = 2; i < leng; i++) {
  45.         prev = i - 2;
  46.         avg.push((p[prev] + p[i]) / 2);
  47.       }
  48.       // close
  49.       avg.push((p[0] + p[leng - 2]) / 2);
  50.       avg.push((p[1] + p[leng - 1]) / 2);
  51.       return avg;
  52.     }
  53.  
  54.  
  55.     canvas.style.transformOrigin = '0 0'
  56.     canvas.style.transform = 'scale(.4)'
  57.  
  58.     rinit = _ => {
  59.       t = 0
  60.       tinc = .1
  61.       rad = hs * .8
  62.       pupil = random() * .25
  63.       radA = pupil + random() * .25
  64.  
  65.     }
  66.     dx = dy = hs
  67.     cx = cy = hs
  68.  
  69.     rinit()
  70.  
  71.     fillStyle = 'black'
  72.     fillRect(0, 0, S, S);
  73.     fillStyle = 'white'
  74.     beginPath()
  75.     moveTo(hs, hs)
  76.     arc(hs, hs, rad, 0, 7)
  77.     fill()
  78.  
  79.     outer = _ => {
  80.       dx = cx + rad * cos(t)
  81.       dy = cy + rad * sin(t)
  82.  
  83.       if (t > 7 && random() < .3) {
  84.         fnIdx++
  85.       }
  86.     }
  87.  
  88.     shutter = () => hs * radA + random() * hs * (.6 - pupil)
  89.  
  90.     innerA = _ => {
  91.       tinc = .05
  92.       rad = shutter()
  93.       dx = cx + rad * cos(t)
  94.       dy = cy + rad * sin(t)
  95.  
  96.       if (t > 21 && random() < .3) {
  97.         fnIdx++
  98.       }
  99.     }
  100.  
  101.     oa = 7 * 3
  102.  
  103.     innerB = _ => {
  104.       tinc = .05
  105.       T = t * random();
  106.       dx = cx + hs * radA * cos(T)
  107.       dy = cy + hs * radA * sin(T)
  108.       if (t > 28 + oa && random() < .3) {
  109.         rad = hs * .8
  110.         fnIdx++
  111.       }
  112.     }
  113.  
  114.     outerA = _ => {
  115.       R = (rad - hs * .1) + random() * hs * .1;
  116.       dx = cx + R * cos(t)
  117.       dy = cy + R * sin(t)
  118.       if (t > 35 + oa && random() < .3) {
  119.         fnIdx++
  120.       }
  121.     }
  122.  
  123.     outerB = _ => {
  124.       tinc = .01;
  125.       R = rad
  126.       if (random() < .5) R = shutter()
  127.       dx = cx + R * cos(t)
  128.       dy = cy + R * sin(t)
  129.  
  130.       if (t > 42 + oa && random() < .3) {
  131.         fnIdx++
  132.         ct = t
  133.       }
  134.     }
  135.  
  136.  
  137.     t2 = 0
  138.     outerC = _ => {
  139.       tinc = .1;
  140.       t2 += .01;
  141.       R = hs * .3
  142.       RR = (R + t2 + random() * 10);
  143.       dx = cx + R * .84 + RR * cos(t)
  144.       dy = cy - R * .84 + RR * sin(t)
  145.  
  146.       if (t > 70 + oa && random() < .3) {
  147.         fnIdx++
  148.       }
  149.     }
  150.  
  151.     outerD = _ => {
  152.       tinc = .1;
  153.       t2 += .01;
  154.       R = hs * .1
  155.       RR = (R + t2 + random() * 10);
  156.       dx = cx + hs * .3 + RR * cos(t)
  157.       dy = cy + R * .84 + RR * sin(t)
  158.  
  159.       if (t > 91 + oa && random() < .3) {
  160.         fnIdx++
  161.       }
  162.     }
  163.  
  164.     outerE = _ => {
  165.       tinc = .1;
  166.  
  167.       rad -= random() * .1;
  168.       dx = cx + rad * cos(t)
  169.       dy = cy + rad * sin(t)
  170.  
  171.       if (t > 112 + oa && random() < .3) {
  172.         fnIdx++
  173.       }
  174.     }
  175.     count = 0
  176.     last = _ => {
  177.       done = true;
  178.  
  179.       fillStyle = 'black'
  180.       fillRect(0, 0, S, S);
  181.       fillStyle = 'white'
  182.       beginPath()
  183.       moveTo(hs, hs)
  184.       arc(hs, hs, hs * .8, 0, 7)
  185.       fill()
  186.  
  187.       beginPath();
  188.       moveTo(0, 0);
  189.       bezierSkin(pnts, false)
  190.       stroke()
  191.  
  192.  
  193.       return
  194.       count++
  195.       if (count < 1) {
  196.         rinit()
  197.         setOff()
  198.         t = 0
  199.         fnIdx = 0
  200.       }
  201.     }
  202.  
  203.     fns = [outer, innerA, innerB, outerA, outerB, outerC, outerD, outerE, last]
  204.     fnIdx = 0
  205.  
  206.     outer()
  207.     drawX = dx
  208.     drawY = dy
  209.     pDrawX = 0
  210.     pDrawY = 0
  211.  
  212.     strokeStyle = 'rgba(0, 0, 0, 0.8)'
  213.     lineWidth = 1;
  214.     tt = 0
  215.  
  216.     ox = 0;
  217.     oy = 0;
  218.     setOff = _ => {
  219.       return
  220.       ox = S * 1.2 * random() - S / 2
  221.       oy = S * 1.2 * random() - S / 2
  222.       sl = .1 + random() * .9;
  223.     }
  224.  
  225.     sl = 1
  226.     pnts = []
  227.     done = false
  228.     loop = _ => {
  229.  
  230.       if (done) {
  231.         return;
  232.       }
  233.       shadowColor = 'rgba(155, 255, 255, .5)';
  234.       shadowBlur = 15;
  235.  
  236.       save()
  237.       scale(1, 1)
  238.       lineWidth = 2;
  239.       for (i = 0; i < 20; i++) {
  240.         t += tinc / 2
  241.         fns[fnIdx]()
  242.  
  243.         drawX += ((dx + ox) * sl - drawX) / 2;
  244.         drawY += ((dy + oy) * sl - drawY) / 2;
  245.  
  246.         if (drawX != 0 && pDrawX) {
  247.           beginPath()
  248.           moveTo(pDrawX, pDrawY);
  249.           lineTo(drawX, drawY);
  250.           pnts.push(drawX, drawY);
  251.           stroke()
  252.         }
  253.  
  254.         pDrawX = drawX
  255.         pDrawY = drawY
  256.       }
  257.  
  258.       restore()
  259.       requestAnimationFrame(loop)
  260.     }
  261.     loop()
  262.   }
  263. }

Another thing for #genuary2022… a single curve…

Raphaël Easing Equations How To

  1. const { pow, PI } = Math;
  2.  
  3. // mostly unedited code from Raphaël
  4. var ef = {
  5.   linear: function(n) {
  6.     return n;
  7.   },
  8.   '<': function(n) {
  9.     return pow(n, 1.7);
  10.   },
  11.   '>': function(n) {
  12.     return pow(n, 0.48);
  13.   },
  14.   '<>': function(n) {
  15.     var q = 0.48 - n / 1.04,
  16.       Q = Math.sqrt(0.1734 + q * q),
  17.       x = Q - q,
  18.       X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
  19.       y = -Q - q,
  20.       Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
  21.       t = X + Y + 0.5;
  22.     return (1 - t) * 3 * t * t + t * t * t;
  23.   },
  24.   backIn: function(n) {
  25.     var s = 1.70158;
  26.     return n * n * ((s + 1) * n - s);
  27.   },
  28.   backOut: function(n) {
  29.     n = n - 1;
  30.     var s = 1.70158;
  31.     return n * n * ((s + 1) * n + s) + 1;
  32.   },
  33.   elastic: function(n) {
  34.     if (n == !!n) {
  35.       return n;
  36.     }
  37.     return pow(2, -10 * n) * Math.sin(((n - 0.075) * (2 * PI)) / 0.3) + 1;
  38.   },
  39.   bounce: function(n) {
  40.     var s = 7.5625,
  41.       p = 2.75,
  42.       l;
  43.     if (n < 1 / p) {
  44.       l = s * n * n;
  45.     } else {
  46.       if (n < 2 / p) {
  47.         n -= 1.5 / p;
  48.         l = s * n * n + 0.75;
  49.       } else {
  50.         if (n < 2.5 / p) {
  51.           n -= 2.25 / p;
  52.           l = s * n * n + 0.9375;
  53.         } else {
  54.           n -= 2.625 / p;
  55.           l = s * n * n + 0.984375;
  56.         }
  57.       }
  58.     }
  59.     return l;
  60.   }
  61. };
  62. ef.easeIn = ef['ease-in'] = ef['<'];
  63. ef.easeOut = ef['ease-out'] = ef['>'];
  64. ef.easeInOut = ef['ease-in-out'] = ef['<>'];
  65. ef['back-in'] = ef.backIn;
  66. ef['back-out'] = ef.backOut;
  67.  
  68. // create a dot
  69. function dot(x, y, radius, color) {
  70.   const el = document.createElement('div');
  71.   const size = `${radius * 2}px`;
  72.   Object.assign(el.style, {
  73.     position: 'absolute',
  74.     left: `${x}px`,
  75.     top: `${y}px`,
  76.     width: size,
  77.     height: size,
  78.     transform: `translate(${-radius}px, ${-radius}px)`,
  79.     borderRadius: '50%',
  80.     background: color
  81.   });
  82.   el.classList.add('dot');
  83.   document.body.appendChild(el);
  84.   return el;
  85. }
  86.  
  87. const elA = dot(0, 40, 30, 'red');
  88. const elB = dot(0, 110, 30, 'blue');
  89. const elC = dot(0, 160, 20, 'green');
  90.  
  91. // how to use the easing equations:
  92. let t = 0;
  93.  
  94. let start = Date.now();
  95. let time = 0;
  96. let duration = 2; // 2 seconds
  97. function loop() {
  98.   // frame based
  99.   elA.style.left = `${ef.elastic(t) * 50}%`;
  100.   t += 0.005;
  101.  
  102.   // time based
  103.   if (time <= duration) {
  104.     time = (Date.now() - start) / 1000;
  105.     const param = time / duration;
  106.     elB.style.left = `${ef.elastic(param) * 50}%`;
  107.  
  108.     // green bounce example
  109.     elC.style.left = `${ef.bounce(param) * 50}%`;
  110.   }
  111.  
  112.   requestAnimationFrame(loop);
  113. }
  114. loop();

I realized it might not be obvious how to use Raphaël’s easing equations. So I speed coded this example.

If you’d like to learn more about this kind of thing gsap is a great place to start… it is amazing… I highly recommend browsing the source.

Raphaël Easing Equations

  1. var ef = R.easing_formulas = {
  2.     linear: function (n) {
  3.         return n;
  4.     },
  5.     "<": function (n) {
  6.         return pow(n, 1.7);
  7.     },
  8.     ">": function (n) {
  9.         return pow(n, .48);
  10.     },
  11.     "<>": function (n) {
  12.         var q = .48 - n / 1.04,
  13.             Q = math.sqrt(.1734 + q * q),
  14.             x = Q - q,
  15.             X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
  16.             y = -Q - q,
  17.             Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
  18.             t = X + Y + .5;
  19.         return (1 - t) * 3 * t * t + t * t * t;
  20.     },
  21.     backIn: function (n) {
  22.         var s = 1.70158;
  23.         return n * n * ((s + 1) * n - s);
  24.     },
  25.     backOut: function (n) {
  26.         n = n - 1;
  27.         var s = 1.70158;
  28.         return n * n * ((s + 1) * n + s) + 1;
  29.     },
  30.     elastic: function (n) {
  31.         if (n == !!n) {
  32.             return n;
  33.         }
  34.         return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
  35.     },
  36.     bounce: function (n) {
  37.         var s = 7.5625,
  38.             p = 2.75,
  39.             l;
  40.         if (n < (1 / p)) {
  41.             l = s * n * n;
  42.         } else {
  43.             if (n < (2 / p)) {
  44.                 n -= (1.5 / p);
  45.                 l = s * n * n + .75;
  46.             } else {
  47.                 if (n < (2.5 / p)) {
  48.                     n -= (2.25 / p);
  49.                     l = s * n * n + .9375;
  50.                 } else {
  51.                     n -= (2.625 / p);
  52.                     l = s * n * n + .984375;
  53.                 }
  54.             }
  55.         }
  56.         return l;
  57.     }
  58. };
  59. ef.easeIn = ef["ease-in"] = ef["<"];
  60. ef.easeOut = ef["ease-out"] = ef[">"];
  61. ef.easeInOut = ef["ease-in-out"] = ef["<>"];
  62. ef["back-in"] = ef.backIn;
  63. ef["back-out"] = ef.backOut;

Another fun chunk of code directly from the Raphaël source. Makes me think of the Penner easing equations.

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