class Circle { constructor({ origin, speed, color, angle, context }) { this.origin = origin; this.position = { ...this.origin }; this.color = color; this.speed = speed; this.angle = angle; this.context = context; this.renderCount = 0; } draw() { this.context.fillStyle = this.color; this.context.beginPath(); this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2); this.context.fill(); } move() { this.position.x = Math.sin(this.angle) * this.speed + this.position.x; this.position.y = Math.cos(this.angle) * this.speed + this.position.y + this.renderCount * 0.3; this.renderCount++; } } class Boom { constructor({ origin, context, circleCount = 10, area }) { this.origin = origin; this.context = context; this.circleCount = circleCount; this.area = area; this.stop = false; this.circles = []; } randomArray(range) { const length = range.length; const randomIndex = Math.floor(length * Math.random()); return range[randomIndex]; } randomColor() { const range = ["8", "9", "A", "B", "C", "D", "E", "F"]; return ( "#" + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) + this.randomArray(range) ); } randomRange(start, end) { return (end - start) * Math.random() + start; } init() { for (let i = 0; i < this.circleCount; i++) { const circle = new Circle({ context: this.context, origin: this.origin, color: this.randomColor(), angle: this.randomRange(Math.PI - 1, Math.PI + 1), speed: this.randomRange(1, 6), }); this.circles.push(circle); } } move() { this.circles.forEach((circle, index) => { if ( circle.position.x > this.area.width || circle.position.y > this.area.height ) { return this.circles.splice(index, 1); } circle.move(); }); if (this.circles.length == 0) { this.stop = true; } } draw() { this.circles.forEach((circle) => circle.draw()); } } class CursorSpecialEffects { constructor() { this.computerCanvas = document.createElement("canvas"); this.renderCanvas = document.createElement("canvas"); this.computerContext = this.computerCanvas.getContext("2d"); this.renderContext = this.renderCanvas.getContext("2d"); this.globalWidth = window.innerWidth; this.globalHeight = window.innerHeight; this.booms = []; this.running = false; } handleMouseDown(e) { const boom = new Boom({ origin: { x: e.clientX, y: e.clientY }, context: this.computerContext, area: { width: this.globalWidth, height: this.globalHeight, }, }); boom.init(); this.booms.push(boom); this.running || this.run(); } handlePageHide() { this.booms = []; this.running = false; } init() { const style = this.renderCanvas.style; style.position = "fixed"; style.top = style.left = 0; style.zIndex = "99999"; style.pointerEvents = "none"; style.width = this.renderCanvas.width = this.computerCanvas.width = this.globalWidth; style.height = this.renderCanvas.height = this.computerCanvas.height = this.globalHeight; document.body.append(this.renderCanvas); window.addEventListener("mousedown", this.handleMouseDown.bind(this)); window.addEventListener("pagehide", this.handlePageHide.bind(this)); } run() { this.running = true; if (this.booms.length == 0) { return (this.running = false); } requestAnimationFrame(this.run.bind(this)); this.computerContext.clearRect(0, 0, this.globalWidth, this.globalHeight); this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight); this.booms.forEach((boom, index) => { if (boom.stop) { return this.booms.splice(index, 1); } boom.move(); boom.draw(); }); this.renderContext.drawImage( this.computerCanvas, 0, 0, this.globalWidth, this.globalHeight ); } } const cursorSpecialEffects = new CursorSpecialEffects(); cursorSpecialEffects.init();