Skip to content

Commit

Permalink
fix: HTML element bounding box calculation logic #1743 (#1744)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wang1212 authored Aug 5, 2024
1 parent 7c68899 commit 8832fb0
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-avocados-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@antv/g-lite': patch
---

fix: HTML element bounding box calculation logic (#1743)
138 changes: 138 additions & 0 deletions demo/camera+html.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width,user-scalable=no,initial-scale=1,shrink-to-fit=no"
/>
<title>Camera</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html,
body {
height: 100vh;
}

#container {
width: 100%;
height: 100%;
}
</style>
</head>

<body>
<div id="container"></div>
<script
src="../packages/g/dist/index.umd.min.js"
type="application/javascript"
></script>
<script
src="../packages/g-canvas/dist/index.umd.min.js"
type="application/javascript"
></script>
<script src="../packages/g-plugin-control/dist/index.umd.min.js"></script>
<!-- <script src="../packages/g-svg/dist/index.umd.min.js" type="application/javascript"></script>
<script src="../packages/g-webgl/dist/index.umd.min.js" type="application/javascript"></script> -->
<script>
console.log(window.G);
const { Circle, CanvasEvent, Canvas, HTML } = window.G;

// create a renderer
const canvasRenderer = new window.G.Canvas2D.Renderer();
canvasRenderer.registerPlugin(new window.G.Control.Plugin());

// create a canvas
const canvas = new Canvas({
container: 'container',
width: 800,
height: 800,
renderer: canvasRenderer,
});

const circle = new Circle({
style: {
cx: 200,
cy: 200,
r: 50,
fill: 'red',
},
});

const html = new HTML({
style: {
x: 150,
y: 150,
width: 100,
height: 100,
innerHTML:
'<h1 style="width: 100px; height: 100px;">This is Title</h1>',
},
});

canvas.addEventListener(CanvasEvent.READY, () => {
canvas.appendChild(circle);
canvas.appendChild(html);

console.log('canvas.getCamera()', canvas.getCamera(), circle, html);
console.log(
'circle',
'getBoundingClientRect',
circle.getBoundingClientRect(),
'getBounds',
circle.getBounds(),
);
console.log(
'html',
'getBoundingClientRect',
html.getBoundingClientRect(),
'getBounds',
html.getBounds(),
);

console.log('Camera pan(-50, 50) --------------------------');
canvas.getCamera().pan(-50, 50);
requestAnimationFrame(() => {
console.log(
'circle',
'getBoundingClientRect',
circle.getBoundingClientRect(),
'getBounds',
circle.getBounds(),
);
console.log(
'html',
'getBoundingClientRect',
html.getBoundingClientRect(),
'getBounds',
html.getBounds(),
);

console.log('Camera setZoom(1.2) --------------------------');
canvas.getCamera().setZoom(1.2);
requestAnimationFrame(() => {
console.log(
'circle',
'getBoundingClientRect',
circle.getBoundingClientRect(),
'getBounds',
circle.getBounds(),
);
console.log(
'html',
'getBoundingClientRect',
html.getBoundingClientRect(),
'getBounds',
html.getBounds(),
);
});
});
});
</script>
</body>
</html>
10 changes: 9 additions & 1 deletion packages/g-lite/src/display-objects/HTML.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ export class HTML extends DisplayObject<HTMLStyleProps, ParsedHTMLStyleProps> {
*/
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);
Expand Down
80 changes: 79 additions & 1 deletion packages/g-lite/src/shapes/Rectangle.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down

0 comments on commit 8832fb0

Please sign in to comment.