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

Make a Grid

  1. const cellSize = 25;
  2. const cols = 10;
  3. const rows = 20;
  4.  
  5. function makeDot(x, y) {
  6.   const dot = document.body.appendChild(
  7.     document.createElement('div')
  8.   );
  9.  
  10.   dot.classList.add('cell');
  11.  
  12.   Object.assign(dot.style, {
  13.     position: 'absolute',
  14.     left: `${x}px`,
  15.     top: `${y}px`,
  16.     width: `${cellSize}px`,
  17.     height: `${cellSize}px`,
  18.     outline: '1px solid black',
  19.     cursor: 'pointer',
  20.     background: 'gray'
  21.   });
  22.  
  23.   return dot;
  24. }
  25.  
  26. for (let y = 0; y < rows; y++) {
  27.   for (let x = 0; x < cols; x++) {
  28.     makeDot(x * cellSize, y * cellSize);
  29.   }
  30. }
  31.  
  32. // make a cell red when it is rolled over
  33. document.addEventListener('mouseover', e => {
  34.   if (e.target.classList.contains('cell')) {
  35.     e.target.style.background = 'red';
  36.   }
  37. });

Here is a simple example for arranging divs in a grid.

// css // dom // javascript // math // tricks // ui

toString Hack Obfuscated

  1. x=''+self 
  2. j=''
  3. 'd1d7a1712345691a7512d427b1da7d9ab7519a4b721a961721d694'
  4. .split``
  5. .map(_=>j+=`x[0x${_}]+`)
  6. console.log(eval(j+'""'))

Yesterday’s snippet saying something else…

It’s simpler than it looks:

  1. x=''+self 
  2. // becomes "[object Window]"
  3.  
  4. j='' 
  5. // initialize `j` which will be javascript to pass to `eval`
  6.  
  7. 'd1d7a17123456...' 
  8. // this is a list of index values to 
  9. // look up in `x` in hexidecimal so that each 
  10. // index is a single character
  11.  
  12. .split``
  13. // split the index values into an array `[0xe, 0x2 ...`
  14.  
  15. .map(_=>j+=`x[0x${_}]+`)
  16. // map over the index values and write a string like
  17. // this `x[0xe]+x[0x2]+...` into `j`
  18.  
  19. console.log(eval(j+'""'))
  20. // evaluate `j` with an empty string at the end
  21. // `x[0xe]+x[0x2]+""` and log it out
`

toString hack

  1. x=self+''
  2. console.log(
  3.   x[8],x[9],x[6],' ',
  4.   x[3],x[1],x[2],' ',
  5.   x[10],x[4],x[8],' ',
  6.   x[5],x[1],x[11]
  7. )

Hit the “Try it out” and open the console….

Smooth Bezier on Canvas

  1. const canvas = document.createElement('canvas');
  2. const c = canvas.getContext('2d');
  3.  
  4. document.body.appendChild(canvas);
  5.  
  6. function draw() {
  7.   canvas.width = window.innerWidth;
  8.   canvas.height = window.innerHeight;
  9.   c.fillStyle = 'gray';
  10.   c.fillRect(0, 0, canvas.width, canvas.height);
  11.  
  12.   c.strokeStyle = 'white';
  13.   c.lineWidth = 2;
  14.  
  15.   c.beginPath();
  16.   c.moveTo(0, 0);
  17.   bezierSkin([10, 10, 210, 10, 10, 300, 210, 300], false);
  18.   bezierSkin([200, 10, 330, 10, 250, 300]);
  19.   bezierSkin(
  20.     Array(30)
  21.       .fill(0)
  22.       .map((a, b) => (b % 2 == 0) * 300 + Math.random() * 300)
  23.   );
  24.   c.stroke();
  25. }
  26. window.addEventListener('resize', draw);
  27. draw();
  28.  
  29. // array of xy coords, closed boolean
  30. function bezierSkin(bez, closed = true) {
  31.   const avg = calcAvgs(bez);
  32.   const leng = bez.length;
  33.   let i, n;
  34.  
  35.   if (closed) {
  36.     c.moveTo(avg[0], avg[1]);
  37.     for (i = 2; i < leng; i += 2) {
  38.       n = i + 1;
  39.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  40.     }
  41.     c.quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
  42.   } else {
  43.     c.moveTo(bez[0], bez[1]);
  44.     c.lineTo(avg[0], avg[1]);
  45.     for (i = 2; i < leng - 2; i += 2) {
  46.       n = i + 1;
  47.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  48.     }
  49.     c.lineTo(bez[leng - 2], bez[leng - 1]);
  50.   }
  51. }
  52.  
  53. // create anchor points by averaging the control points
  54. function calcAvgs(p) {
  55.   const avg = [];
  56.   const leng = p.length;
  57.   let prev;
  58.   for (var i = 2; i < leng; i++) {
  59.     prev = i - 2;
  60.     avg.push((p[prev] + p[i]) / 2);
  61.   }
  62.   // close
  63.   avg.push((p[0] + p[leng - 2]) / 2);
  64.   avg.push((p[1] + p[leng - 1]) / 2);
  65.   return avg;
  66. }

Being able to draw smooth lines that connect arbitrary points is something that I find myself needing very frequently. This is a port of an old old snippet of mine that does just that. By averaging control points of a quadratic bezier curve we ensure that our resulting Bezier curves are always smooth.

It would be very cool if html5 canvas implemented the Catmull Rom Spline but it unfortunately does not. The wonderful Raphael library used to have support for it.

Obfuscated Canvas Commands

  1. const oCan = (
  2.   d = document,
  3.   b = d.body,
  4.   canvas = b.appendChild(document.createElement('canvas')),
  5.   c = canvas.getContext('2d'),
  6.   props = [],
  7.   o = {},
  8.   docs = {},
  9.   cmds = [],
  10.   L,
  11.   k,
  12.   du,
  13.   j,
  14.   draw,
  15.   id
  16. ) => {
  17.   ;(onresize = () => {
  18.     canvas.width = innerWidth
  19.     canvas.height = innerHeight
  20.     if (draw) {
  21.       clearTimeout(id)
  22.       id = setTimeout(() => cmds.forEach(v => draw(v)), 500)
  23.     }
  24.   })()
  25.  
  26.   Object.assign(b.style, { margin: 0, height: '100%' })
  27.  
  28.   // longer way: console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(c)));
  29.   for (let i in c) props.push(i)
  30.  
  31.   // make alphabetical since object keys have
  32.   // no order
  33.   props.sort().map(i => {
  34.     L = i.match(/[A-Z]/)
  35.     k = i[0]
  36.     if (L) k += L[0]
  37.     du = 0
  38.     if (o[k]) {
  39.       j = 0
  40.       while (o[k]) k += i[++j]
  41.     }
  42.  
  43.     o[k] =
  44.       (typeof c[i])[0] == 'f'
  45.         ? (...args) => c[i].apply(c, args)
  46.         : v => (c[i] = v)
  47.     docs[i] = k
  48.   })
  49.  
  50.   console.log('docs:', JSON.stringify(docs, null, 2))
  51.  
  52.   return (draw = (s, cmd, currFn, args = [], part, fn, num) => {
  53.     cmd = s.split(/\s+/)
  54.     cmds.push(s)
  55.     c.save()
  56.     for (let i = 0; i < cmd.length; i++) {
  57.       part = cmd[i]
  58.       fn = o[part]
  59.       if (fn && currFn != fn) {
  60.         currFn && currFn.apply(c, args)
  61.         currFn = fn
  62.         args = []
  63.       } else {
  64.         num = parseFloat(part)
  65.         args.push(!isNaN(num) ? num : part)
  66.       }
  67.     }
  68.     currFn && currFn.apply(c, args)
  69.     c.restore()
  70.   })
  71. }
  72.  
  73. const c = oCan()
  74. // `font` & text not suppoted
  75. // make str a function so resize works?
  76. c(`
  77.   fS #ccc
  78.   fR 0 0 400 ${innerHeight}
  79.   fS blue
  80.   fR 40 0 20 20
  81.   gCl difference
  82.   ro .25
  83.   fR 50 0 30 30
  84.   gCl source-over
  85.   fS rgba(200,100,9)
  86.   fR 100 100 40 40
  87.   `)

I’ve had this idea for a long time, never bothered doing it until now. I wrote it in a semi-golfed style for no reason… Anyway, this lets you write canvas code in a strange obfuscated syntax that looks like this:

  1. c(`
  2.   fS #ccc
  3.   fR 0 0 400 ${innerHeight}
  4.   fS blue
  5.   fR 40 0 20 20
  6.   gCl difference
  7.   ro .25
  8.   fR 50 0 30 30
  9.   gCl source-over
  10.   fS rgba(200,100,9)
  11.   fR 100 100 40 40
  12.   `)

This snippet logs out some JSON that shows all the method aliases for the canvas context.

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