codepens/bubblegum-on-hover/dist/script.js

159 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class Animation {
cols = 16;
rows = 9;
circleSize = 30;
scaleMin = 0.15;
scaleMax = 2.5;
range = 175;
spacingHorizontal = 60;
spacingVertical = 80;
fill = getComputedStyle(document.documentElement).getPropertyValue('--circles');
svgMargin = 40;
svgHeight = 0;
svgWidth = 0;
circles = [];
screen = {
width: window.innerWidth,
height: window.innerHeight
};
mouse = {
x: window.innerWidth / 2,
y: window.innerHeight / 2
};
mouseStored = Object.assign({}, this.mouse);
constructor(selector) {
this.svg = document.querySelector(selector);
this.g = this.svg.querySelector("g");
this.svgWidth = this.cols * this.circleSize + 2 * this.svgMargin + (this.cols - 1) * this.spacingHorizontal;
this.svgHeight = this.rows + 2 * this.svgMargin + (this.rows - 1) * this.spacingVertical;
this.svg.setAttribute("viewBox", `0 0 ${this.svgWidth} ${this.svgHeight}`);
this.addEventListeners();
// Draw all the circles
this.draw();
// And animate them if the user is fine with it
window.matchMedia('(prefers-reduced-motion: no-preference)').matches ? this.animate() : null;
}
addEventListeners() {
let self = this;
// Don't redraw everything, only recalculate the centers of all arrows
window.addEventListener("resize", function() {
self.screen.width = window.innerWidth;
self.screen.height = window.innerHeight;
self.setCircleCenters();
});
}
draw() {
for(var i = 0; i < this.rows; i++) {
const offset = i % 2;
for(var j = 0; j < this.cols + 2 * offset; j++) {
// We're drawing the initial lines horizontally
let c = new Circle(
this.svgMargin + j*this.circleSize + j*this.spacingHorizontal - (offset * (this.spacingHorizontal + this.circleSize)/2),
this.svgMargin + i*this.spacingVertical,
this.circleSize,
this.fill
);
// Set a transform origin and add the HTML element to the SVG
const cElement = c.getElement();
gsap.set(cElement, {transformOrigin: "50% 50%"});
this.g.appendChild(cElement);
this.circles.push(c);
}
}
this.setCircleCenters();
}
clamp(num, min, max) {
return Math.min(Math.max(num, min), max);
}
setMouseCoords(event) {
this.mouse.x = event.clientX;
this.mouse.y = event.clientY;
}
setCircleCenters() {
this.circles.forEach(circle => {
// Get the center of the line
// Instead of mapping svg coords to the screen position, get the position on the actual screen using boundingRect
const boundingRect = circle.getElement().getBoundingClientRect();
circle.center = {
x: boundingRect.x + boundingRect.width / 2,
y: boundingRect.y + boundingRect.height / 2
};
})
}
animate() {
// Listen for the mouse movements
window.addEventListener("mousemove", this.setMouseCoords.bind(this));
// And use the ticker to update the rotation accordingly
gsap.ticker.add(this.setCircleScale.bind(this));
}
setCircleScale() {
// Don't do anything if the cursor's position is the same as in the previous tick
if (this.mouseStored.x === this.mouse.x && this.mouseStored.y === this.mouse.y) return;
this.circles.forEach(circle => {
// d = √[(x2 x1)2 + (y2 y1)2]
const distance = Math.sqrt((Math.pow((circle.center.x - this.mouse.x), 2) + Math.pow((circle.center.y - this.mouse.y), 2)));
const scale = this.range / (distance + this.circleSize);
gsap.to(
circle.getElement(),
{
scale: this.clamp(scale, this.scaleMin, this.scaleMax)
}
);
});
// Store the mouse position for the next tick
this.mouseStored.x = this.mouse.x;
this.mouseStored.y = this.mouse.y;
}
}
class Circle {
cx = 0;
cy = 0;
r = 0;
fill = "white";
element = null;
constructor(cx, cy, r, fill) {
this.cx = cx;
this.cy = cy;
this.r = r;
this.fill = fill;
this.element = document.createElementNS("http://www.w3.org/2000/svg", 'circle');
this.setElement();
}
getElement() {
return this.element;
}
setElement(cx, cy, r, fill) {
this.element.setAttribute("cx", cx ? cx : this.cx);
this.element.setAttribute("cy", cy ? cy : this.cy);
this.element.setAttribute("r", r ? r : this.r);
this.element.setAttribute("fill", fill ? fill : this.fill);
}
}
const animation = new Animation("#animation");