'use strict'; class HoverIntent { constructor({ sensitivity = 0.1, // speed less than 0.1px/ms means "hovering over an element" interval = 100, // measure mouse speed once per 100ms elem, over, out }) { this.sensitivity = sensitivity; this.interval = interval; this.elem = elem; this.over = over; this.out = out; // make sure "this" is the object in event handlers. this.onMouseMove = this.onMouseMove.bind(this); this.onMouseOver = this.onMouseOver.bind(this); this.onMouseOut = this.onMouseOut.bind(this); // and in time-measuring function (called from setInterval) this.trackSpeed = this.trackSpeed.bind(this); elem.addEventListener("mouseover", this.onMouseOver); elem.addEventListener("mouseout", this.onMouseOut); } onMouseOver(event) { if (this.isOverElement) { // if we're over the element, then ignore the event // we are already measuring the speed return; } this.isOverElement = true; // after every mousemove we'll be check the distance // between the previous and the current mouse coordinates // if it's less than sensivity, then the speed is slow this.prevX = event.pageX; this.prevY = event.pageY; this.prevTime = Date.now(); elem.addEventListener('mousemove', this.onMouseMove); this.checkSpeedInterval = setInterval(this.trackSpeed, this.interval); } onMouseOut(event) { // if left the element if (!event.relatedTarget || !elem.contains(event.relatedTarget)) { this.isOverElement = false; this.elem.removeEventListener('mousemove', this.onMouseMove); clearInterval(this.checkSpeedInterval); if (this.isHover) { // if there was a stop over the element this.out.call(this.elem, event); this.isHover = false; } } } onMouseMove(event) { this.lastX = event.pageX; this.lastY = event.pageY; this.lastTime = Date.now(); } trackSpeed() { let speed; if (!this.lastTime || this.lastTime == this.prevTime) { // cursor didn't move speed = 0; } else { speed = Math.sqrt( Math.pow(this.prevX - this.lastX, 2) + Math.pow(this.prevY - this.lastY, 2) ) / (this.lastTime - this.prevTime); } if (speed < this.sensitivity) { clearInterval(this.checkSpeedInterval); this.isHover = true; this.over.call(this.elem, event); } else { // speed fast, remember new coordinates as the previous ones this.prevX = this.lastX; this.prevY = this.lastY; this.prevTime = this.lastTime; } } destroy() { elem.removeEventListener('mousemove', this.onMouseMove); elem.removeEventListener('mouseover', this.onMouseOver); elem.removeEventListener('mouseout', this.onMouseOut); } }