From 756f10ac74622bcc5cb908cc0495cf71b7944356 Mon Sep 17 00:00:00 2001 From: wang1212 Date: Mon, 5 Aug 2024 14:43:12 +0800 Subject: [PATCH] fix: `HTML` element bounding box calculation logic (#1743) Subtract the camera's transformation from the bounding box calculation logic of the `HTML` element to keep the result consistent with the native canvas element. --- .changeset/friendly-avocados-drum.md | 5 + demo/camera+html.html | 138 ++++++++++++++++++++ packages/g-lite/src/display-objects/HTML.ts | 10 +- packages/g-lite/src/shapes/Rectangle.ts | 80 +++++++++++- 4 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 .changeset/friendly-avocados-drum.md create mode 100644 demo/camera+html.html diff --git a/.changeset/friendly-avocados-drum.md b/.changeset/friendly-avocados-drum.md new file mode 100644 index 000000000..9aa4543b0 --- /dev/null +++ b/.changeset/friendly-avocados-drum.md @@ -0,0 +1,5 @@ +--- +'@antv/g-lite': patch +--- + +fix: HTML element bounding box calculation logic (#1743) diff --git a/demo/camera+html.html b/demo/camera+html.html new file mode 100644 index 000000000..87182a935 --- /dev/null +++ b/demo/camera+html.html @@ -0,0 +1,138 @@ + + + + + + Camera + + + + +
+ + + + + + + diff --git a/packages/g-lite/src/display-objects/HTML.ts b/packages/g-lite/src/display-objects/HTML.ts index 88aad7890..f947dab22 100644 --- a/packages/g-lite/src/display-objects/HTML.ts +++ b/packages/g-lite/src/display-objects/HTML.ts @@ -64,7 +64,15 @@ export class HTML extends DisplayObject { */ getBoundingClientRect(): Rectangle { if (this.parsedStyle.$el) { - return this.parsedStyle.$el.getBoundingClientRect(); + const cameraMatrix = this.ownerDocument.defaultView + .getCamera() + .getOrthoMatrix(); + const bBox = this.parsedStyle.$el.getBoundingClientRect(); + + return Rectangle.applyTransform( + bBox, + mat4.invert(mat4.create(), cameraMatrix), + ); } else { const { x, y, width, height } = this.parsedStyle; return new Rectangle(x, y, width, height); diff --git a/packages/g-lite/src/shapes/Rectangle.ts b/packages/g-lite/src/shapes/Rectangle.ts index b482ef26c..1135d4deb 100644 --- a/packages/g-lite/src/shapes/Rectangle.ts +++ b/packages/g-lite/src/shapes/Rectangle.ts @@ -1,9 +1,87 @@ +import { mat4, vec4 } from 'gl-matrix'; + +type RectangleLike = { + x: number; + y: number; + width: number; + height: number; +}; + export class Rectangle implements DOMRect { + /** + * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect/fromRect_static + */ + static fromRect(rect: RectangleLike) { + return new Rectangle(rect.x, rect.y, rect.width, rect.height); + } + + /** + * will return a new rect instance + */ + static applyTransform(rect: Rectangle, matrix: mat4) { + const topLeft = vec4.fromValues(rect.x, rect.y, 0, 1); + const topRight = vec4.fromValues(rect.x + rect.width, rect.y, 0, 1); + const bottomLeft = vec4.fromValues(rect.x, rect.y + rect.height, 0, 1); + const bottomRight = vec4.fromValues( + rect.x + rect.width, + rect.y + rect.height, + 0, + 1, + ); + + const transformedTopLeft = vec4.create(); + const transformedTopRight = vec4.create(); + const transformedBottomLeft = vec4.create(); + const transformedBottomRight = vec4.create(); + + vec4.transformMat4(transformedTopLeft, topLeft, matrix); + vec4.transformMat4(transformedTopRight, topRight, matrix); + vec4.transformMat4(transformedBottomLeft, bottomLeft, matrix); + vec4.transformMat4(transformedBottomRight, bottomRight, matrix); + + const minX = Math.min( + transformedTopLeft[0], + transformedTopRight[0], + transformedBottomLeft[0], + transformedBottomRight[0], + ); + const minY = Math.min( + transformedTopLeft[1], + transformedTopRight[1], + transformedBottomLeft[1], + transformedBottomRight[1], + ); + const maxX = Math.max( + transformedTopLeft[0], + transformedTopRight[0], + transformedBottomLeft[0], + transformedBottomRight[0], + ); + const maxY = Math.max( + transformedTopLeft[1], + transformedTopRight[1], + transformedBottomLeft[1], + transformedBottomRight[1], + ); + + return Rectangle.fromRect({ + x: minX, + y: minY, + width: maxX - minX, + height: maxY - minY, + }); + } + left: number; right: number; top: number; bottom: number; - constructor(public x: number, public y: number, public width: number, public height: number) { + constructor( + public x: number, + public y: number, + public width: number, + public height: number, + ) { this.left = x; this.right = x + width; this.top = y;