Source: packages/events/src/EventTicker.ts

import { Ticker, UPDATE_PRIORITY } from '@pixi/core';

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.
 * @memberof PIXI
 * @since 7.2.0
 * @see PIXI.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 PIXI.Ticker.system.
     * @param deltaTime - time delta since the last call
     */
    private tickerUpdate(deltaTime: number): void
    {
        this._deltaTime += deltaTime;

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

        this._deltaTime = 0;

        this.update();
    }
}

/**
 * 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.
 * @memberof PIXI
 * @type {PIXI.EventsTickerClass}
 * @since 7.2.0
 */
export const EventsTicker = new EventsTickerClass();