import { gsap } from 'gsap'; import { map } from './utils'; /** * Class representing a preview map element (.preview__map) */ export class HoverMap { // DOM elements DOM = { // Main element el: null, // Main element children (.preview__map-item) mapItems: null, // The current little dot that moves inside the map as the user moves the cursor (.dot) dotEl: null, // The Thumb that gets hovered thumb: null, } // Custom cursor (Cursor obj) cursor; // Size/Position of one of the map items (all the same) hoverMapItemBounds; // Size/Position of one of the dots (all the same) dotBounds; // Size/Position of the current hovered thumbs thumbBounds; // requestAnimationFrame id requestId; /** * Constructor. * @param {Element} DOM_el - * @param {Cursor} cursor - */ constructor(DOM_el, cursor) { this.DOM = {el: DOM_el}; this.DOM.mapItems = this.DOM.el.querySelectorAll('.preview__map-item'); this.cursor = cursor; } /** * Shows the hover map element and the current map item's dot element. * @param {Thumb} thumb - The Thumb obj representing the .thumb element that is getting hovered at the moment. * @param {Number} itemPosition - The position of the thumb which aloow us to get to the right item (this.mapItems). There are as many map items as there are thumbs. */ show(thumb, itemPosition) { this.DOM.thumb = thumb.DOM.img; this.DOM.dotEl = [...this.DOM.mapItems][itemPosition].querySelector('.dot'); this.dotBounds = this.DOM.dotEl.getBoundingClientRect(); this.hoverMapItemBounds = this.DOM.mapItems[0].getBoundingClientRect(); this.thumbBounds = this.DOM.thumb.getBoundingClientRect(); // Hide the cursor if ( this.DOM.thumb.classList.contains('thumb__img--nocursor') ) { this.cursor.hide(); } // Animation gsap.killTweensOf([this.DOM.el, this.DOM.dotEl]); gsap.to([this.DOM.el, this.DOM.dotEl], { duration: 1, ease: 'expo', opacity: 1 }); // Add a click feedback animation to the dot this.onClickDotEv = () => { gsap.timeline() .to(this.DOM.dotEl, { duration: 0.1, ease: 'power1.in', scale: 3 }) .to(this.DOM.dotEl, { duration: 1, ease: 'expo', scale: 1 }); } this.DOM.thumb.addEventListener('click', this.onClickDotEv); // Start loop this.loopRender(); } /** * Hides the hover map element and the current map item's dot element. */ hide() { // Show the cursor if ( this.DOM.thumb.classList.contains('thumb__img--nocursor') ) { this.cursor.show(); } gsap.killTweensOf([this.DOM.el, this.DOM.dotEl]); gsap.set([this.DOM.el, this.DOM.dotEl], { opacity: 0 }); // Remove click event this.DOM.thumb.removeEventListener('click', this.onClickDotEv); // Stop the rAF loop. this.stopRendering(); } /** * Start the render loop animation (rAF) */ loopRender() { if ( !this.requestId ) { this.requestId = requestAnimationFrame(() => this.render()); } } /** * Stop the render loop animation (rAF) */ stopRendering() { if ( this.requestId ) { window.cancelAnimationFrame(this.requestId); this.requestId = undefined; } } /** * rAF loop - move the small dot as the cursor moves hover the thumb */ render() { this.requestId = undefined; // Set up the translation values of the current dot element. // These will map to the current translation values of the cursor position inside the current hovered thumb gsap.set(this.DOM.dotEl, { x: map(this.cursor.renderedStyles['tx'].previous - this.thumbBounds.left + this.cursor.bounds.width/2, 0, this.thumbBounds.width, 0, this.hoverMapItemBounds.width) - this.dotBounds.width/2 - 1, y: map(this.cursor.renderedStyles['ty'].previous - this.thumbBounds.top + this.cursor.bounds.height/2, 0, this.thumbBounds.height, 0, this.hoverMapItemBounds.height) - this.dotBounds.height/2 - 1 }); // Keep rolling this.loopRender(); } }