cocos / cocos-engine

Cocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.
https://www.cocos.com/en/creator
Other
8.23k stars 1.94k forks source link

[3.6.2] getBoundingClientRect is incompatibility in Android < 8, cause touch is not reactive #14043

Open HopperGithub opened 1 year ago

HopperGithub commented 1 year ago

Cocos Creator version

3.6.2

System information

Android < 8,Web Mobile

Issue description

Android < 8,touch event location will be recognized out of rect of canvas(GameCanvas). The returing is ClientRect and it's not having x and y properties.

sourcecode: cocos-engine/pal/input/web/touch-input.ts

private _getCanvasRect (): Rect {
        const canvas = this._canvas;
        const box = canvas?.getBoundingClientRect();
        if (box) {
            return new Rect(box.x, box.y, box.width, box.height);
        }
        return new Rect(0, 0, 0, 0);
    }

sourcecode: cocos-engine/cocos/core/scene-graph/node-event-processor.ts

    private _handleTouchStart (event: EventTouch) {
        const node = this.node;
        if (!node || !node._uiProps.uiTransformComp) {
            return false;
        }

        event.getLocation(pos);

        if (node._uiProps.uiTransformComp.hitTest(pos)) {
            event.type = NodeEventType.TOUCH_START;
            event.bubbles = true;
            this._dispatchingTouch = event.touch;
            node.dispatchEvent(event);
            return true;
        }

        return false;
    }

console.log

> document.getElementById('GameCanvas').getBoundingClientRect()
>
> ClientRect {
>   bottom:500.8125
>   height:250
>   left:55
>   right:305
>   top:250.8125
>   width:250
>}

Relevant error log output

No response

Steps to reproduce

Minimal reproduction project

No response

HopperGithub commented 1 year ago

hope getBoundingClientRect has a polyfill to support ClientRect ReturnType

eg:

function getBoundingClientRect() {
                var rect = canvas.getBoundingClientRect();
                return {
                    top: rect.top,
                    right: rect.right,
                    bottom: rect.bottom,
                    left: rect.left,
                    width: rect.width,
                    height: rect.height,
                    x: rect.x ?? rect.left,
                    y: rect.y ?? rect.top
                };
            }
HopperGithub commented 1 year ago

my solution:

function getBoundingClientRectPolyfill() {
    const rect = document.body.getBoundingClientRect()
    if ('x' in rect && 'y' in rect) return
    // @ts-ignore
    Element.prototype._getBoundingClientRect = Element.prototype.getBoundingClientRect
    Element.prototype.getBoundingClientRect = function (): DOMRect {
        // @ts-ignore
        const rect = Element.prototype._getBoundingClientRect.call(this)
        rect.x = rect.x ?? rect.left
        rect.y = rect.y ?? rect.top
        return rect
    }
}

call getBoundingClientRectPolyfill before the game init

qiuguohua commented 1 year ago

image This did not happen in my case. You can try it with chrome browser.

HopperGithub commented 1 year ago

some real android devices could happen, using chrome devtool to showcase is unmeaning