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

Divide Rectangle Into Smaller Rectangles

  1. const rand = num => ~~(Math.random() * num)
  2.  
  3. let rectNum = 2 + rand(10)
  4. let rectCount = 0
  5.  
  6. document.body.appendChild(document.createElement('div')).innerText =
  7.   'click anywhere to regenerate'
  8.  
  9. function reset() {
  10.   ;[...document.querySelectorAll('.rect')].forEach(rect => rect.remove())
  11.   rectNum = 2 + rand(10)
  12.   rectCount = 0
  13.   newRect(300, 300, 50, 50)
  14. }
  15. reset()
  16. onpointerup = reset
  17.  
  18. function newRect(w, h, xp, yp) {
  19.   const rect = document.body.appendChild(document.createElement('div'))
  20.  
  21.   rect.classList.add('rect')
  22.   rectCount++
  23.  
  24.   Object.assign(rect.style, {
  25.     position: 'absolute',
  26.     left: `${xp}px`,
  27.     top: `${yp}px`,
  28.     width: `${w}px`,
  29.     height: `${h}px`,
  30.     outline: `1px solid black`,
  31.   })
  32.  
  33.   const props = {
  34.     x: xp,
  35.     y: yp,
  36.     height: h,
  37.     width: w,
  38.     seed: rand(3),
  39.     divide() {
  40.       const div = 2 + rand(5 * Math.random() * Math.random())
  41.       if (rand(2) == rand(2)) {
  42.         const newHeight = this.height / div
  43.  
  44.         newRect(this.width, this.height - newHeight, this.x, this.y)
  45.         newRect(this.width, newHeight, this.x, this.y + this.height - newHeight)
  46.       } else {
  47.         const newWidth = w / div
  48.         newRect(this.width - newWidth, this.height, this.x, this.y)
  49.         newRect(newWidth, this.height, this.x + this.width - newWidth, this.y)
  50.       }
  51.       rect.remove()
  52.     },
  53.   }
  54.   window.requestAnimationFrame(() => {
  55.     if (rectCount < rectNum) {
  56.       props.divide()
  57.     } else {
  58.       console.log('DONE!')
  59.     }
  60.   })
  61. }

This snippet comes to mind from time to time – one easy way to divide a rectangle into smaller rectangles- I actually went back and looked it up as it was an answer to a student question from 2006. The original one was written in ActionScript 2. Have a look:

  1. var wormNum:Number = 123;
  2. var wormCount:Number = 0;
  3. newWorm(400, 400, 0, 0);
  4. this.onEnterFrame = function() {
  5. 	if (wormCount < wormNum) {
  6. 		for (var props:String in this) {
  7. 			if (this[props]._x != undefined) {
  8. 				this[props].divide();
  9. 			}
  10. 		}
  11. 	}
  12. };
  13. function newWorm(w, h, xp, yp) {
  14. 	var currWorm:MovieClip = this.createEmptyMovieClip("box"+wormCount, this.getNextHighestDepth());
  15. 	wormCount++;
  16. 	box(w, h, currWorm, random(0xFFFFFF));
  17. 	currWorm._x = xp;
  18. 	currWorm._y = yp;
  19. 	currWorm.seed = random(3);
  20. 	currWorm.divide = function() {
  21. 		var div = random(4)+(1+Math.random()*1);
  22. 		if (random(2) == random(2)) {
  23. 			// divide vertically
  24. 			var nh:Number = this._height/div;
  25. 			newWorm(this._width, this._height-nh, this._x, this._y);
  26. 			newWorm(this._width, nh, this._x, this._y+this._height-nh);
  27. 		} else {
  28. 			// divide horizonatlly
  29. 			var nw:Number = this._width/div;
  30. 			newWorm(this._width-nw, this._height, this._x, this._y);
  31. 			newWorm(nw, this._height, this._x+this._width-nw, this._y);
  32. 		}
  33. 		this.removeMovieClip();
  34. 	};
  35. }
  36. function box(w:Number, h:Number, mc:MovieClip, col:Number):Void {
  37. 	with (mc) {
  38. 		lineStyle(0, 0, 20);
  39. 		beginFill(col, 10);
  40. 		moveTo(0, 0);
  41. 		lineTo(w, 0);
  42. 		lineTo(w, h);
  43. 		lineTo(0, h);
  44. 		endFill();
  45. 	}
  46. }

Don’t remember why I called them worms instead of rectangles, some AS2 types floating around…

Pass a Class

  1. function callMethods(evt) {
  2.   const e = new evt
  3.   e.one()
  4.   e.two()
  5. }
  6.  
  7. callMethods(class {
  8.   one() {
  9.     console.log('one')
  10.   }
  11.  
  12.   two() {
  13.     console.log('two')
  14.   }
  15. })

This is so tempting for something I want to do… but too strange to use probably… maybe…

Destructure in Every Function

  1. // read through the comments of this snippet...
  2.  
  3. function dist1(x1, y1, x2, y2) {
  4.   const dx = x1 - x2
  5.   const dy = y1 - y2
  6.   return Math.sqrt(dx**2 + dy**2)
  7. }
  8.  
  9. function dist2({x1, y1, x2, y2}) {
  10.   const dx = x1 - x2
  11.   const dy = y1 - y2
  12.   return Math.sqrt(dx**2 + dy**2)
  13. }
  14.  
  15. // What's the difference here... well
  16. dist1(50, 50, 100, 100)
  17.  
  18. // vs
  19.  
  20. dist2({ x1: 50, y1: 50, x2: 100, y2: 100 })
  21.  
  22. // so what?
  23.  
  24. // With `dist2` the order of the arguments doesn't matter
  25. // and the arguments are named now as a result of being keys
  26. // in an object
  27.  
  28. // How many times have you changed a core function or method as you're
  29. // working on a project?
  30.  
  31. // Let's see another example:
  32.  
  33. // circle(100, 200, 300, 'red', 'blue', 0, 0)
  34.  
  35. // Can you guess what those arguments are? It's not really a big deal
  36. // and editors help with this, typescript helps with this... but what about:
  37.  
  38. circle({ 
  39.   x: 10, 
  40.   y: 110, 
  41.   radius: 120, 
  42.   fill: 'red', 
  43.   stroke: 'blue', 
  44.   velocityX: 0, 
  45.   velocitY: 0
  46. })
  47.  
  48. // how about...
  49. circle({ radius: 50, fill: 'blue' })
  50.  
  51. // or...
  52. circle({ stroke: 'green', x: 40, velocityX: 1 })
  53.  
  54. // etc...
  55. circle({ 
  56.   radius: 50,
  57.   stroke: 'black', x: 200, 
  58.   fill: 'teal',
  59.   velocityY: 1, velocityX: -1 })
  60.  
  61. // In combination with default arguments we end up with a very easy pattern for functions/methods
  62. // with a complex argument signature. gsap (aka TweenLite/TweenMax) has used this pattern for many
  63. // years. I've seen similar things in many languages...
  64.  
  65. // How does the circle function look?
  66.  
  67. function circle({ 
  68.   x = 0, y = 0, 
  69.   radius = 30, 
  70.   fill = 'black', 
  71.   stroke = 'transparent', 
  72.   velocityX = 0, velocityY = 0}) {
  73.  
  74.   const diam = radius * 2;
  75.  
  76.   const circle = document.body.appendChild(
  77.     Object.assign(
  78.       document.createElement('div'), 
  79.       { style: `
  80.         position: absolute;
  81.         left: ${x}px;
  82.         top: ${y}px;
  83.         width: ${diam}px;
  84.         height: ${diam}px;
  85.         background: ${fill};
  86.         border: 3px solid ${stroke};
  87.         border-radius: 100%;
  88.       `
  89.       }
  90.     )
  91.   )
  92.   if (velocityX != 0 || velocityY != 0) {
  93.     setInterval(() => {
  94.       x += velocityX
  95.       y += velocityY
  96.       circle.style.left = `${x}px`
  97.       circle.style.top = `${y}px`
  98.     }, 16)
  99.   }
  100.   return circle
  101. }
  102.  
  103.  
  104. // here is a golfed distance function - for no reason
  105. d=(a,b,c,d,e=a-c,f=b-d)=>Math.sqrt(e*e+f*f)
  106. console.log(
  107.   dist1(0, 0, 140, 140) ===
  108.   d(0, 0, 140, 140)
  109. )

Object Key Order and Reflect.ownKeys

  1. const obj = { 11: 'eleven', 23: 'twenty-three', 1: 'one', 2: 'two', '-1': 'negative 1' };
  2.  
  3. console.log(Reflect.ownKeys(obj))

I noticed that for in.. over an object was giving me weirdly consistent results across all browsers the other day and stumbled upon this.

Great news, but it’s Reflect.ownKeys for me… now that IE11 is dying/dead.

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.

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