// site-reactions.js // Copyright (C) 2022 DTP Technologies, LLC // License: Apache-2.0 'use strict'; const DTP_COMPONENT = { name: 'Site Reactions', slug: 'site-reactions' }; const dtp = window.dtp = window.dtp || { }; // jshint ignore:line import DtpLog from 'dtp/dtp-log'; class Reaction { constructor (container, reaction) { this.container = container; this.reaction = reaction; this.element = document.createElement('span'); this.element.classList.add('reaction-icon'); switch (this.reaction.reaction) { case 'clap': this.element.textContent = '👏'; break; case 'fire': this.element.textContent = '🔥'; break; case 'happy': this.element.textContent = '🤗'; break; case 'laugh': this.element.textContent = '🤣'; break; case 'angry': this.element.textContent = '🤬'; break; case 'honk': this.element.textContent = '🤡'; break; } this.position = { x: Math.random() * (this.container.offsetWidth - 32.0), y: this.container.clientHeight, }; this.opacity = 1.0; this.moveSpeed = 150.0 + (Math.random() * 50.0); this.rotation = 0.0; this.rotationDelta = 60.0 + (Math.random() * 15.0); this.container.appendChild(this.element); } update (elapsed) { const scale = elapsed / 1000.0; this.position.y -= this.moveSpeed * scale; this.rotation += this.rotationDelta * scale; if (this.rotation > 30 || this.rotation < -30) { this.rotationDelta = -this.rotationDelta; } const adjustedY = this.position.y + this.element.offsetHeight; if (adjustedY > 100) { return; } if (adjustedY === 0) { this.opacity = 0.0; return; } this.opacity = adjustedY / 100.0; } render ( ) { this.element.style.left = `${this.position.x}px`; this.element.style.top = `${this.position.y}px`; if (this.opacity > 0.8) { this.opacity = 0.8; } this.element.style.opacity = this.opacity; const transform = `rotate(${this.rotation}deg)`; this.element.style.transform = transform; } destroy ( ) { this.container.removeChild(this.element); } } export default class SiteReactions { constructor ( ) { this.log = new DtpLog(DTP_COMPONENT); this.container = document.querySelector('#chat-reactions'); this.reactions = [ ]; this.updateHandler = this.onUpdate.bind(this); } create (reaction) { const react = new Reaction(this.container, reaction); this.reactions.push(react); if (this.reactions.length === 1) { this.lastUpdate = new Date(); requestAnimationFrame(this.updateHandler); } } onUpdate ( ) { const NOW = new Date(); const elapsed = NOW - this.lastUpdate; const expired = [ ]; for (const reaction of this.reactions) { reaction.update(elapsed); if (reaction.position.y <= -(reaction.element.offsetHeight)) { expired.push(reaction); } else { reaction.render(); } } expired.forEach((react) => { const idx = this.reactions.indexOf(react); if (idx === -1) { return; } react.destroy(); this.reactions.splice(idx, 1); }); if (this.reactions.length > 0) { requestAnimationFrame(this.updateHandler); } this.lastUpdate = NOW; } } dtp.SiteReactions = SiteReactions;