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

Gravity Dots 2

  1. d = document
  2. b = d.body
  3. b.style.margin = 0
  4. b.style.background = 'black'
  5. r = (v = 1) => Math.random() * v
  6.  
  7. with(
  8.   b.appendChild(
  9.     d.createElement`canvas`
  10.   ).getContext`2d`) {
  11.  
  12.   onresize = () => {
  13.     canvas.width = innerWidth
  14.     canvas.height = innerHeight
  15.   }
  16.   onresize()
  17.  
  18.   fillStyle = 'red'
  19.  
  20.   dot = (
  21.     x = r(innerWidth),
  22.     y = 0,
  23.     mass = 20, sr = r(2) + 2, 
  24.     R = sr,
  25.     dx, dy,
  26.     col = '#005eb0',
  27.     dist, vx = .2, vy = 0) => { 
  28.  
  29.     return () => { 
  30.       fillStyle = col
  31.       R = sr
  32.       if (r() < .001) {
  33.         vx = r() * 4 - 2
  34.         R = sr * 2
  35.         fillStyle = 'red'
  36.         col = `hsl(${r(360)}deg, 50%, 50%)`
  37.       }
  38.  
  39.       dx = innerWidth / 2 - x
  40.       dy = innerHeight / 2 - y
  41.       dist = Math.sqrt(dx * dx + dy * dy)
  42.       dist *= dist
  43.       vx += dx / dist * mass
  44.       vy += dy / dist * mass
  45.  
  46.       x += vx
  47.       y += vy
  48.  
  49.       beginPath()
  50.       arc(x, y, R, 0, 6.29)
  51.       fill()
  52.     }
  53.   }
  54.  
  55.   const dots = []
  56.   for (let i = 0; i < 100; i++) dots.push(dot())
  57.   loop = () => {
  58.     fillStyle = 'rgba(0, 0, 0, 0.05)'
  59.     fillRect(0, 0, canvas.width, canvas.height)
  60.     dots.map(d => d())
  61.   }
  62.   setInterval(loop, 16)
  63. }

A variation on this recent post.

Gravity Dots

  1. d = document
  2. b = d.body
  3. b.style.margin = 0
  4. b.style.background = 'black'
  5. r = (v = 1) => Math.random() * v
  6.  
  7. with(
  8.   b.appendChild(
  9.     d.createElement`canvas`
  10.   ).getContext`2d`) {
  11.  
  12.   onresize = () => {
  13.     canvas.width = innerWidth
  14.     canvas.height = innerHeight
  15.   }
  16.   onresize()
  17.  
  18.   fillStyle = 'red'
  19.  
  20.   dot = (
  21.     x = r(innerWidth),
  22.     y = r(innerHeight),
  23.     mass = 10, sr = r(8) + 4, 
  24.     R = sr,
  25.     dx, dy,
  26.     dist, vx = .2, vy = 0) => { 
  27.  
  28.     return () => { 
  29.       fillStyle = '#005eb0'
  30.       R = sr
  31.       if (r() < .005) {
  32.         vx = r() * 4 - 2
  33.         R = sr * 2
  34.         fillStyle = 'white'
  35.       }
  36.  
  37.       dx = innerWidth / 2 - x
  38.       dy = innerHeight / 2 - y
  39.       dist = Math.sqrt(dx * dx + dy * dy)
  40.       dist *= dist
  41.       vx += dx / dist * mass
  42.       vy += dy / dist * mass
  43.  
  44.       x += vx
  45.       y += vy
  46.  
  47.       beginPath()
  48.       arc(x, y, R, 0, 6.29)
  49.       fill()
  50.     }
  51.   }
  52.  
  53.   const dots = []
  54.   for (let i = 0; i < 10; i++) dots.push(dot())
  55.   loop = () => {
  56.     fillStyle = 'rgba(0, 0, 0, 0.2)'
  57.     fillRect(0, 0, canvas.width, canvas.height)
  58.     dots.map(d => d())
  59.   }
  60.   setInterval(loop, 16)
  61. }

Some speed coded dots that gravitate to the center of the screen and occasionally change direction.

Fermat’s Spiral

  1. const canvas = document.body.appendChild(document.createElement('canvas'));
  2. const c = canvas.getContext('2d');
  3.  
  4. function resize() {
  5.   canvas.width = window.innerWidth;
  6.   canvas.height = window.innerHeight;
  7.   draw();
  8. }
  9.  
  10. function draw() {
  11.   c.clearRect(0, 0, canvas.width, canvas.height);
  12.   c.fillStyle = 'blue';
  13.  
  14.   const iter = 300;
  15.   const halfWidth = window.innerWidth / 2;
  16.   const halfHeight = window.innerHeight / 2;
  17.   let rad = 0,
  18.     theta = 0,
  19.     scale = 20 * Math.min(window.innerWidth, window.innerHeight) * 0.006,
  20.     x,
  21.     y;
  22.  
  23.   c.save();
  24.   c.translate(halfWidth, halfHeight);
  25.  
  26.   for (let i = 0; i < iter; i++) {
  27.     rad = Math.sqrt(theta) * scale;
  28.     x = rad * Math.cos(theta);
  29.     y = rad * Math.sin(theta);
  30.     c.fillRect(x, y, 2, 2);
  31.     c.fillRect(-x, -y, 5, 5);
  32.  
  33.     theta += 0.05;
  34.   }
  35.   c.restore();
  36. }
  37.  
  38. resize();
  39. window.addEventListener('resize', resize);

Draw Fermat’s spiral…

Wobbling Discord Blob

  1. const SCALE = 0.25;
  2. const TWO_PI = Math.PI * 2;
  3. const HALF_PI = Math.PI / 2;
  4. const canvas = document.createElement("canvas");
  5. const c = canvas.getContext("2d");
  6.  
  7. canvas.width = window.innerWidth;
  8. canvas.height = window.innerHeight;
  9. document.body.appendChild(canvas);
  10.  
  11. class Blob {
  12.   constructor() {
  13.     this.wobbleIncrement = 0;
  14.     // use this to change the size of the blob
  15.     // use this to change the size of the blob
  16.     this.radius = 1100;
  17.     // think of this as detail level
  18.     // number of conections in the `bezierSkin`
  19.     this.segments = 14;
  20.     this.step = HALF_PI / this.segments;
  21.     this.anchors = [];
  22.     this.radii = [];
  23.     this.thetaOff = [];
  24.  
  25.     const bumpRadius = 200;
  26.     const halfBumpRadius = bumpRadius / 2;
  27.  
  28.     for (let i = 0; i < this.segments + 2; i++) {
  29.       this.anchors.push(0, 0);
  30.       this.radii.push(Math.random() * bumpRadius - halfBumpRadius);
  31.       this.thetaOff.push(Math.random() * TWO_PI);
  32.     }
  33.  
  34.     this.theta = 0;
  35.     this.thetaRamp = 0;
  36.     this.thetaRampDest = 12;
  37.     this.rampDamp = 25;
  38.   }
  39.   update() {
  40.     this.thetaRamp += (this.thetaRampDest - this.thetaRamp) / this.rampDamp;
  41.     this.theta += 0.03;
  42.  
  43.     this.anchors = [0, this.radius];
  44.     for (let i = 0; i <= this.segments + 2; i++) {
  45.       const sine = Math.sin(this.thetaOff[i] + this.theta + this.thetaRamp);
  46.       const rad = this.radius + this.radii[i] * sine;
  47.       const theta = this.step * i;
  48.       const x = rad * Math.sin(theta);
  49.       const y = rad * Math.cos(theta);
  50.       this.anchors.push(x, y);
  51.     }
  52.  
  53.     c.save();
  54.     c.translate(-10, -10);
  55.     c.scale(SCALE, SCALE);
  56.     c.fillStyle = "blue";
  57.     c.beginPath();
  58.     c.moveTo(0, 0);
  59.     bezierSkin(this.anchors, false);
  60.     c.lineTo(0, 0);
  61.     c.fill();
  62.     c.restore();
  63.   }
  64. }
  65.  
  66. const blob = new Blob();
  67.  
  68. function loop() {
  69.   c.clearRect(0, 0, canvas.width, canvas.height);
  70.   blob.update();
  71.   window.requestAnimationFrame(loop);
  72. }
  73. loop();
  74.  
  75. // array of xy coords, closed boolean
  76. function bezierSkin(bez, closed = true) {
  77.   const avg = calcAvgs(bez);
  78.   const leng = bez.length;
  79.  
  80.   if (closed) {
  81.     c.moveTo(avg[0], avg[1]);
  82.     for (let i = 2; i < leng; i += 2) {
  83.       let n = i + 1;
  84.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  85.     }
  86.     c.quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
  87.   } else {
  88.     c.moveTo(bez[0], bez[1]);
  89.     c.lineTo(avg[0], avg[1]);
  90.     for (let i = 2; i < leng - 2; i += 2) {
  91.       let n = i + 1;
  92.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  93.     }
  94.     c.lineTo(bez[leng - 2], bez[leng - 1]);
  95.   }
  96. }
  97.  
  98. // create anchor points by averaging the control points
  99. function calcAvgs(p) {
  100.   const avg = [];
  101.   const leng = p.length;
  102.   let prev;
  103.  
  104.   for (let i = 2; i < leng; i++) {
  105.     prev = i - 2;
  106.     avg.push((p[prev] + p[i]) / 2);
  107.   }
  108.   // close
  109.   avg.push((p[0] + p[leng - 2]) / 2, (p[1] + p[leng - 1]) / 2);
  110.   return avg;
  111. }

This is a stackoverflow answer of mine. The question was asking how to create a wobbling blob like the one in the background of the discord login page. Take a look at the answer here.

User AmooHesam wrapped it up in a github repo here.

// animation // canvas // javascript // math // paths // ui

Local Mouse Click

  1. const RIGHT_BOUND = 200;
  2. const measureEl = document.createElement('div');
  3.  
  4. Object.assign(measureEl.style, {
  5.   position: 'absolute',
  6.   left: 0,
  7.   top: 0,
  8.   zIndex: 999
  9. });
  10.  
  11. function localPosition(e, element, w = 1, h = 1) {
  12.  
  13.   // normalize desktop and mobile
  14.   const touches = e.touches;
  15.   let x;
  16.   let y;
  17.   if (touches != null && touches.length > 0) {
  18.     x = touches[0].clientX;
  19.     y = touches[0].clientY;
  20.   } else {
  21.     x = e.clientX;
  22.     y = e.clientY;
  23.   }
  24.  
  25.   function location(width) {
  26.     let left = 0;
  27.     let right = RIGHT_BOUND;
  28.     let mid;
  29.  
  30.     while (right - left > 0.0001) {
  31.       mid = (right + left) / 2;
  32.  
  33.       measureEl.style[width ? 'width' : 'height'] = `${mid}%`;
  34.       measureEl.style[width ? 'height' : 'width'] = '100%';
  35.       element.appendChild(measureEl);
  36.       const el = document.elementFromPoint(x, y);
  37.  
  38.       element.removeChild(measureEl);
  39.       if (el === measureEl) {
  40.         right = mid;
  41.       } else {
  42.         if (right === RIGHT_BOUND) {
  43.           return null;
  44.         }
  45.         left = mid;
  46.       }
  47.     }
  48.  
  49.     return mid;
  50.   }
  51.  
  52.   const left = location(1);
  53.   const top = location(0);
  54.   return left != null && top != null
  55.     ? {
  56.         // percentage values
  57.         left,
  58.         top,
  59.         // pixel values
  60.         x: left * w * 0.01,
  61.         y: top * h * 0.01
  62.       }
  63.     : null;
  64. }
  65.  
  66. const div = document.body.appendChild(document.createElement('div'));
  67. div.innerHTML = 'click me';
  68. Object.assign(div.style, {
  69.   postition: 'absolute',
  70.   transform: 'translate(20px, 20px) rotate(45deg) scale(0.8)',
  71.   width: '120px',
  72.   height: '90px',
  73.   color: 'white',
  74.   fontFamily: 'sans-serif',
  75.   background: 'gray'
  76. });
  77.  
  78. document.addEventListener('touchstart', onClick);
  79. document.addEventListener('mousedown', onClick);
  80.  
  81. function onClick(e) {
  82.   const info = localPosition(e, e.target, 120, 90);
  83.   console.log(info);
  84. }

Find the local percentage and pixel values of the mouse/touch location.

I found this one on stackoverflow here by user 4esn0k.

This is an interesting alternative to semi-broken native browser solutions and custom matrix math 😉

// css // dom // html // javascript // math
snippet.zone ~ 2021-24 /// {s/z}