hypr angle logo dynamic wave

by phloe

HTML

<svg viewBox="-50 -50 691 395">
  <g>
    <g>
      <path d="M0 0H79V79L214.861 215H103.139L79 190.866V215H0V0Z"/>
      <path d="M295 79V294H216V214.866L80.1385 79H191.861L216 103.143V79L295 79Z"/>
    </g>
    <g>
      <path d="M510.861 79L375 214.866V294H296V182.143L320.138 158H296V79L510.861 79Z"/>
      <path d="M512.143 79L376.139 215H487.861L547.861 156H591V79H512.143Z"/>
    </g>
    <path d="M512 192.627L548.2 157H591V215H512V192.627ZM512 0H591V78H512V0Z" fill-opacity="0.5"/>
  </g>
</svg>

CSS

path {
  transform: translate(var(--x, 0px), var(--y, 0px));
}

JavaScript

class Waves {
	constructor(coords, callback) {
  	this.coords = coords;
  	this.waves = [];
    this.checking = false;
    this.callback = callback;
  }
  add(wave) {
  	this.waves.push(wave);
    if (!this.checking) {
    	this.checking = true;
      this.check();
    }
  }
  check() {
  	const retire = [];
    const heights = this.coords.map(() => 0);
    const now = +Date.now();
    this.waves.forEach((wave, index) => {
      if (wave.start + wave.falloff > now) {
				this.coords.forEach((x, i) => {
        	heights[i] += wave.getY(x, now);
        });
      }
      else {
				retire.unshift(index);
      }
    });
    
    retire.forEach((index) => {
    	this.waves[index] = null;
    	this.waves.splice(index, 1);
    });
    
    this.callback(heights);
    
  	if (this.waves.length) {
      requestAnimationFrame(() => this.check());
    }
    else {
    	this.checking = false;
    }
  }
}

class Wave {
	constructor(center, amp, freq, size) {
  	this.center = center;
  	this.amp = amp;
    this.freq = freq;
    this.size = size;
    this.start = +Date.now();
    this.falloff = 5000;
  }
  
  getY(x, now) {
    const elapsed = now - this.start;
    if (elapsed > this.falloff) {
    	return 0;
    }
    const sinDelta = elapsed;
  	const dist = Math.abs(x - this.center);
    const sinPos = (dist % this.freq) / this.freq;
    return Math.sin(sinPos + ((sinDelta / 1000 - 1) * Math.PI / 2)) * this.amp * (1 - (elapsed / this.falloff));
  }
}

const svg = document.querySelector("svg");
const paths = [...document.querySelectorAll("path")];

let path, startX, startY, scale;

const box = svg.getAttribute("viewBox")
	.split(" ")
  .map((string) => parseFloat(string, 10));

const boxWidth = box[2] - box[0];

const pathCoords = paths.map((path) => {
	const { left, width } = path.getBoundingClientRect();
  return left + (width / 2);
});

const waves = new Waves(pathCoords, (heights) => {
  heights.forEach((height, index) => {
  	let y = height;
    if (index === 0) {
   ...