140 lines
4.3 KiB
JavaScript
140 lines
4.3 KiB
JavaScript
// Exit intent detection based on mouse movement patterns
|
|
let lastMousePosition = { x: 0, y: 0 };
|
|
let lastMouseMoveTime = 0;
|
|
let lastActivityTime = Date.now();
|
|
let exitIntentDetected = false;
|
|
let cornerDetectionTimeout = null;
|
|
let afkTimeout = null;
|
|
|
|
// Configuration
|
|
const CORNER_THRESHOLD = 50; // pixels from corner to consider it a corner
|
|
const MOVEMENT_THRESHOLD = 3; // minimum pixels to consider it a movement
|
|
const TIME_THRESHOLD = 3000; // 3 seconds to detect slow movement
|
|
const SLOW_MOVEMENT_THRESHOLD = 100; // pixels per second to consider it slow movement
|
|
const AFK_THRESHOLD = 240000; // 4 minutes in milliseconds
|
|
|
|
// Check if device is a touch-only mobile device
|
|
function isTouchOnlyDevice() {
|
|
// Check if device has touch capability
|
|
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
|
|
// Check if device is a mobile device (excluding tablets and laptops with touch)
|
|
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
|
|
// Check if device has a keyboard (laptops/desktops)
|
|
const hasKeyboard = navigator.keyboard !== undefined;
|
|
|
|
// Device is touch-only if it has touch, is mobile, and doesn't have a keyboard
|
|
return hasTouch && isMobile && !hasKeyboard;
|
|
}
|
|
|
|
function sendExitIntentToUnity() {
|
|
if (typeof unityInstance !== 'undefined') {
|
|
unityInstance.SendMessage('WebUtils', 'OnExitIntent');
|
|
}
|
|
}
|
|
|
|
function initExitIntentDetection() {
|
|
// Only initialize if not a touch-only device
|
|
if (isTouchOnlyDevice()) {
|
|
return;
|
|
}
|
|
|
|
document.addEventListener('mousemove', handleMouseMove);
|
|
document.addEventListener('mouseleave', handleMouseLeave);
|
|
document.addEventListener('keydown', handleActivity);
|
|
document.addEventListener('click', handleActivity);
|
|
|
|
// Start AFK timer
|
|
resetAFKTimer();
|
|
}
|
|
|
|
function handleActivity() {
|
|
lastActivityTime = Date.now();
|
|
resetAFKTimer();
|
|
}
|
|
|
|
function resetAFKTimer() {
|
|
// Clear existing AFK timeout if any
|
|
if (afkTimeout) {
|
|
clearTimeout(afkTimeout);
|
|
}
|
|
|
|
// Set new AFK timeout
|
|
afkTimeout = setTimeout(() => {
|
|
if (!exitIntentDetected) {
|
|
exitIntentDetected = true;
|
|
sendExitIntentToUnity();
|
|
}
|
|
}, AFK_THRESHOLD);
|
|
}
|
|
|
|
function handleMouseMove(e) {
|
|
const currentTime = Date.now();
|
|
const timeDiff = currentTime - lastMouseMoveTime;
|
|
|
|
// Update last activity time
|
|
lastActivityTime = currentTime;
|
|
resetAFKTimer();
|
|
|
|
// Calculate movement speed
|
|
const distance = Math.sqrt(
|
|
Math.pow(e.clientX - lastMousePosition.x, 2) +
|
|
Math.pow(e.clientY - lastMousePosition.y, 2)
|
|
);
|
|
const speed = distance / (timeDiff / 1000); // pixels per second
|
|
|
|
// Check if mouse is in a corner and moving slowly
|
|
const isInCorner = isMouseInCorner(e.clientX, e.clientY);
|
|
const isMovingSlowly = speed < SLOW_MOVEMENT_THRESHOLD && distance > MOVEMENT_THRESHOLD;
|
|
|
|
if (isInCorner && isMovingSlowly && !exitIntentDetected) {
|
|
if (!cornerDetectionTimeout) {
|
|
cornerDetectionTimeout = setTimeout(() => {
|
|
if (isMouseInCorner(e.clientX, e.clientY)) {
|
|
exitIntentDetected = true;
|
|
sendExitIntentToUnity();
|
|
}
|
|
}, TIME_THRESHOLD);
|
|
}
|
|
} else if (!isInCorner || !isMovingSlowly) {
|
|
// Reset detection if mouse moves out of corner or moves too fast
|
|
if (cornerDetectionTimeout) {
|
|
clearTimeout(cornerDetectionTimeout);
|
|
cornerDetectionTimeout = null;
|
|
}
|
|
exitIntentDetected = false;
|
|
}
|
|
|
|
// Update last position and time
|
|
lastMousePosition = { x: e.clientX, y: e.clientY };
|
|
lastMouseMoveTime = currentTime;
|
|
}
|
|
|
|
function handleMouseLeave(e) {
|
|
// If mouse leaves through the top, consider it an exit intent
|
|
if (e.clientY <= 0) {
|
|
sendExitIntentToUnity();
|
|
}
|
|
}
|
|
|
|
function isMouseInCorner(x, y) {
|
|
const windowWidth = window.innerWidth;
|
|
const windowHeight = window.innerHeight;
|
|
|
|
// Check top-left corner
|
|
if (x <= CORNER_THRESHOLD && y <= CORNER_THRESHOLD) {
|
|
return true;
|
|
}
|
|
|
|
// Check top-right corner
|
|
if (x >= windowWidth - CORNER_THRESHOLD && y <= CORNER_THRESHOLD) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Initialize when the script loads
|
|
initExitIntentDetection();
|