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

Thinking About Colors

  1. const { assign } = Object
  2. const { min, max, floor, round, abs } = Math
  3.  
  4. // `hslToRgb` and `rgbToHsl` functions from the amazing Raphael library
  5. // by Dmitry Baranovskiy
  6. // https://github.com/DmitryBaranovskiy/raphael/blob/master/raphael.js
  7.  
  8. export function hslToRgb({ h, s, l, a }) {
  9.   let H = h
  10.   let S = s
  11.   let L = l
  12.  
  13.   s /= 100
  14.   l /= 100
  15.  
  16.   let r, g, b, x, c
  17.   h = (h % 360) / 60
  18.   c = 2 * s * (l < .5 ? l : 1 - l)
  19.   x = c * (1 - abs(h % 2 - 1))
  20.   r = g = b = l - c / 2;
  21.   h = floor(h)
  22.   r += [c, x, 0, 0, x, c][h]
  23.   g += [x, c, c, x, 0, 0][h]
  24.   b += [0, 0, x, c, c, x][h]
  25.   r *= 255; g *= 255; b *= 255
  26.   r = round(r)
  27.   g = round(g)
  28.   b = round(b)
  29.   return { r, g, b, h: H, s: S, l: L, a }
  30. }
  31.  
  32. export function rgbToHsl({ r, g, b, a }) {
  33.   let R = r
  34.   let G = g
  35.   let B = b
  36.  
  37.   r /= 255
  38.   g /= 255
  39.   b /= 255
  40.  
  41.   let h, s, l, w, m, c;
  42.   w = max(r, g, b);
  43.   m = min(r, g, b);
  44.   c = w - m;
  45.   h = (c == 0 ? null :
  46.         w == r ? (g - b) / c :
  47.         w == g ? (b - r) / c + 2 :
  48.                  (r - g) / c + 4)
  49.   h = ((h + 360) % 6) * 60 / 360;
  50.  
  51.   l = (w + m) / 2;
  52.   s = (c == 0 ? 0 : l < .5 ?
  53.     c / (2 * l) :
  54.     c / (2 - 2 * l));
  55.   h *= 360
  56.   s *= 100
  57.   l *= 100
  58.   h = round(h)
  59.   s = round(s)
  60.   l = round(l);
  61.   return { h, s, l, r: R, g: G, b: B, a }
  62. }
  63.  
  64. const fromDec = dec => ({
  65.   r: dec >> 16,
  66.   g: dec >> 8 & 255,
  67.   b: dec & 255
  68. })
  69.  
  70. const fromHex = hex =>
  71.   fromDec(Number('0x' + hex.slice(1)))
  72.  
  73. const components = def =>
  74.   def.match(/[0-9\.]+/g).map(parseFloat)
  75.  
  76. const fromRgb = rgb => {
  77.   const [r, g, b, a] = components(rgb)
  78.   return { r, g, b, a: a ?? 1 }
  79. }
  80.  
  81. const fromHsl = hsl => {
  82.   const [h, s, l, a] = components(hsl)
  83.   return { h, s, l, a: a ?? 1 }
  84. }
  85.  
  86. const utils = {
  87.   hex: (def, o) => assign(o, rgbToHsl(
  88.     fromHex(def)), { a: o.a ?? 1 }
  89.   ),
  90.   rgb: (def, o) => assign(o, rgbToHsl(fromRgb(def))),
  91.   hsl: (def, o) => assign(o, hslToRgb(fromHsl(def))),
  92.   prop: (def, o) => {
  93.     const name = Object.keys(def)[0]
  94.     o[name] = def[name]
  95.     if (name.match(/[rgb]/)) assign(o, rgbToHsl(o))
  96.     if (name.match(/[hsl]/)) assign(o, hslToRgb(o))
  97.  
  98.     return o
  99.   }
  100. }
  101.  
  102. const types = { '#': 'hex', r: 'rgb', h: 'hsl', '[': 'prop' }
  103. const type = str => types[str[0]]
  104.  
  105. const hx = c => {
  106.   const chan = (+c).toString(16)
  107.   if (chan.length === 0) return '00'
  108.   if (chan.length === 1) return '0' + chan
  109.   return chan
  110. };
  111.  
  112. export function shade(def) {
  113.   const o = {
  114.     set(def) {
  115.       const method = utils[type(String(def))]
  116.       if (method != null) method(def, o)
  117.     },
  118.     hsl: _ => `hsla(${o.h}, ${o.s}%, ${o.l}%, ${o.a})`,
  119.     rgb: _ => `rgba(${o.r}, ${o.g}, ${o.b}, ${o.a})`,
  120.     hex: _ => `#${hx(o.r)}${hx(o.g)}${hx(o.b)}`
  121.   }
  122.   o.set(def)
  123.   return o;
  124. }

Wrote this today for something at work… it’s not done yet but figured it was worth a post… will probably post finished version tomorrow…

This can be a little golfed since it is just a demo for work and not code on a real product 😀

charAt

  1. '0123'.charAt('1') // -> '1'
  2. '0123'.charAt(1.45) // -> '1'
  3. '0123'.charAt(true) // -> '1'
  4. '0123'.charAt() // -> '0'
  5. '0123'.charAt(NaN) // -> '0'
  6. '0123'.charAt(-1) // -> ''
  7.  
  8. Number.prototype.charAt = String.prototype.charAt
  9. NaN.charAt() // -> 'N'
  10.  
  11. Boolean.prototype.charAt = Boolean.prototype.charAt
  12. true.charAt() // -> t

Funny snippet from Räphael’s creator Dmitry Baranovskiy’s talk Zen of JavaScript

Here is the part where he talks about this:

HWB Colors

  1. // Safari and Firefox only... no Chrome support at time of this post
  2. Object.assign(document.body.style, { 
  3.   margin: 0,
  4.   display: 'grid',
  5.   'grid-template-columns': '1fr 1fr 1fr',
  6. });
  7.  
  8. const hwbBlock = () => {
  9.   const hue = Math.round(Math.random() * 360)
  10.   const white = Math.round(
  11.     Math.random() * Math.random() * 100)
  12.   const black = Math.round(
  13.     Math.random() * Math.random() * 100)
  14.   const col = `hwb(${hue} ${white}% ${black}%)`
  15.   document.body.innerHTML += `
  16.     <div 
  17.       style="
  18.         width: 100%;
  19.         height: 4em;
  20.         font-family: sans-serif;
  21.         text-align: center;
  22.         line-height: 2.3;
  23.         color: white;
  24.         text-shadow: 1px 1px 5px black;
  25.         background:${col}">${col}</div>
  26.   `
  27. }
  28.  
  29. Array(102).fill(0).map(hwbBlock);

HWB color format… Looking forward to better support on this CSS color format…

// color // css

Step Between Two Numbers

  1. const stepBetweenA = (min, max, steps) => 
  2.   Array(steps).fill(0).reduce((prev, curr, i) => {
  3.     prev.push(min + ((max - min) / (steps - 1)) * i)
  4.     return prev
  5.   }, [])
  6.  
  7.  
  8. const stepBetweenB = (min, max, steps) => {
  9.   steps -= 1
  10.   const diff = (max - min) / steps
  11.   const result = [min]
  12.   for (let i = 0; i < steps; i++) {
  13.     result.push(min += diff)
  14.   }
  15.   return result
  16. }
  17.  
  18. console.log('a', stepBetweenA(10, 110, 4))
  19. console.log('b', stepBetweenB(10, 110, 4))
  20.  
  21. const ITER = 10000
  22.  
  23. console.time('a')
  24. for (let i = 0; i < ITER; i++) {
  25.   stepBetweenA(10, 110, 4)
  26. }
  27. console.timeEnd('a')
  28.  
  29.   console.time('b')
  30. for (let i = 0; i < ITER; i++) {
  31.   stepBetweenB(10, 110, 4)
  32. }
  33. console.timeEnd('b')

Two messy implementations for stepping between two numbers… I am not sure it’s possible to make console.time work well with the snippet zone quick editor – so if you want to see the times… open your console.

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…

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