Source: events/EventTicker.ts

import { UPDATE_PRIORITY } from '../ticker/const';
import { Ticker } from '../ticker/Ticker';

import type { EventSystem } from './EventSystem';

/**
 * This class handles automatic firing of PointerEvents
 * in the case where the pointer is stationary for too long.
 * This is to ensure that hit-tests are still run on moving objects.
 * @since 7.2.0
 * @memberof events
 * @class EventsTicker
 */
class EventsTickerClass
{
    /** The event system. */
    public events: EventSystem;
    /** The DOM element to listen to events on. */
    public domElement: HTMLElement;
    /** The frequency that fake events will be fired. */
    public interactionFrequency = 10;

    private _deltaTime = 0;
    private _didMove = false;
    private _tickerAdded = false;
    private _pauseUpdate = true;

    /**
     * Initializes the event ticker.
     * @param events - The event system.
     */
    public init(events: EventSystem): void
    {
        this.removeTickerListener();
        this.events = events;
        this.interactionFrequency = 10;
        this._deltaTime = 0;
        this._didMove = false;
        this._tickerAdded = false;
        this._pauseUpdate = true;
    }

    /** Whether to pause the update checks or not. */
    get pauseUpdate(): boolean
    {
        return this._pauseUpdate;
    }

    set pauseUpdate(paused: boolean)
    {
        this._pauseUpdate = paused;
    }

    /** Adds the ticker listener. */
    public addTickerListener(): void
    {
        if (this._tickerAdded || !this.domElement)
        {
            return;
        }

        Ticker.system.add(this._tickerUpdate, this, UPDATE_PRIORITY.INTERACTION);

        this._tickerAdded = true;
    }

    /** Removes the ticker listener. */
    public removeTickerListener(): void
    {
        if (!this._tickerAdded)
        {
            return;
        }

        Ticker.system.remove(this._tickerUpdate, this);

        this._tickerAdded = false;
    }

    /** Sets flag to not fire extra events when the user has already moved there mouse */
    public pointerMoved(): void
    {
        this._didMove = true;
    }

    /** Updates the state of interactive objects. */
    private _update(): void
    {
        if (!this.domElement || this._pauseUpdate)
        {
            return;
        }

        // if the user move the mouse this check has already been done using the mouse move!
        if (this._didMove)
        {
            this._didMove = false;

            return;
        }

        // eslint-disable-next-line dot-notation
        const rootPointerEvent = this.events['_rootPointerEvent'];

        if (this.events.supportsTouchEvents && (rootPointerEvent as PointerEvent).pointerType === 'touch')
        {
            return;
        }

        globalThis.document.dispatchEvent(new PointerEvent('pointermove', {
            clientX: rootPointerEvent.clientX,
            clientY: rootPointerEvent.clientY,
        }));
    }

    /**
     * Updates the state of interactive objects if at least interactionFrequency
     * milliseconds have passed since the last invocation.
     *
     * Invoked by a throttled ticker update from Ticker.system.
     * @param ticker - The throttled ticker.
     */
    private _tickerUpdate(ticker: Ticker): void
    {
        this._deltaTime += ticker.deltaTime;

        if (this._deltaTime < this.interactionFrequency)
        {
            return;
        }

        this._deltaTime = 0;

        this._update();
    }
}

export const EventsTicker = new EventsTickerClass();