鸿蒙开发-想做碰撞检测和点击判断?RectUtils矩形工具
摘要:RectUtils 是 HarmonyOS 提供的矩形操作工具类,用于简化图形开发中的矩形处理。它提供静态方法支持:1) 多种方式创建矩形(空矩形、坐标创建、拷贝);2) 获取矩形属性(宽高、中心点坐标);3) 判断包含关系(矩形或点是否在内部);4) 计算交集/并集;5) 平移和调整边界。特别适用于点击检测、元素布局等场景,能有效减少手动计算矩形关系的代码量。
矩形区域怎么计算和判断?RectUtils 帮你搞定各种矩形操作
在做图形开发的时候,你经常会遇到需要处理矩形区域的情况——判断两个矩形有没有重叠、计算它们的交集或并集、检测一个点是不是在矩形里面、把矩形平移到某个位置……这些操作看起来简单,但自己写起来还是挺麻烦的。
HarmonyOS 的 RectUtils 就是一个专门处理这些矩形操作的工具类。它提供了一整套静态方法,让你可以方便地创建、拷贝、计算、判断各种矩形。
创建矩形
RectUtils 提供了完整的矩形操作能力,下面是常用操作的分类:
RectUtils 提供了好几种创建矩形的方式:
创建空矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeEmpty();
makeEmpty() 创建一个上下左右都是 0 的"空矩形"。你可以把它当作一个占位符,后面再用其他方法来设置它的值。
用坐标创建矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
makeLtrb 的四个参数分别是 left(左)、top(上)、right(右)、bottom(下),也就是矩形左上角和右下角的坐标。这是最常用的创建方式。
拷贝矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
let rect2 = drawing.RectUtils.makeCopy(rect);
console.info('rect2.left:', rect2.left);
console.info('rect2.top: ', rect2.top);
console.info('rect2.right: ', rect2.right);
console.info('rect2.bottom: ', rect2.bottom);
makeCopy 创建一个新的矩形,是原矩形的副本。修改其中一个不会影响另一个。
获取矩形的基本信息
创建好矩形之后,你可以获取它的各种属性:
获取宽度和高度:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
let width = drawing.RectUtils.getWidth(rect);
console.info('width:', width);
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
let height = drawing.RectUtils.getHeight(rect);
有个细节:如果矩形的左边界大于右边界(或者上边界大于下边界),返回的宽度/高度会是负值。这种情况通常意味着矩形是"反转"的。
获取中心点坐标:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(20, 30, 30, 40);
let x = drawing.RectUtils.centerX(rect);
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(20, 30, 30, 40);
let x = drawing.RectUtils.centerY(rect);
centerX 返回中心点的横坐标,centerY 返回纵坐标。在需要把某个元素居中放置时,这两个方法非常好用。
判断包含关系
这是最常用的功能之一——判断一个矩形是否包含另一个矩形或点:
判断矩形是否包含另一个矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
let rect2 = drawing.RectUtils.makeLtrb(0, 0, 40, 40);
let isContains = drawing.RectUtils.contains(rect2, rect);
console.info('isContains: ', isContains);
这里 contains(rect2, rect) 判断的是 rect2 是否完全包含 rect。如果 rect 完全在 rect2 内部(或者两者相等),返回 true。注意:空矩形不包含任何矩形。
用坐标判断包含:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 100, 100);
let isContains = drawing.RectUtils.contains(rect, 10, 20, 30, 40);
console.info('isContains :', isContains);
这个重载版本直接用 left/top/right/bottom 坐标来表示要判断的矩形,不用先创建 Rect 对象。
判断矩形是否包含一个点:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 100, 100);
let isContains = drawing.RectUtils.contains(rect, 10, 20);
console.info('isContains: ', isContains);
contains(rect, x, y) 判断点 (x, y) 是否在矩形内部。这个在做点击检测时特别有用——用户点击了屏幕某个位置,你想知道他是不是点在了某个按钮上。
注意一个细节:左边界和上边界属于矩形内部,但右边界和下边界不属于。也就是说,对于一个 {left: 0, top: 0, right: 100, bottom: 100} 的矩形,点 (0, 0) 在内部,但点 (100, 100) 不在内部。
判断和计算交集
判断两个矩形是否相交:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 20, 20);
let rect2 = drawing.RectUtils.makeLtrb(10, 10, 40, 40);
let isIntersect = drawing.RectUtils.isIntersect(rect, rect2);
console.info('isIntersect :', isIntersect);
isIntersect 只判断两个矩形有没有重叠部分,返回 true 或 false。注意:如果两个矩形只是边重叠或者只有角相交,返回的是 false,必须有面积上的重叠才返回 true。
计算交集并更新矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 20, 20);
let rect2 = drawing.RectUtils.makeLtrb(10, 10, 40, 40);
let isIntersect = drawing.RectUtils.intersect(rect, rect2);
console.info('isIntersect :', isIntersect);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
intersect 和 isIntersect 的区别是:intersect 不仅告诉你有没有交集,还会把交集区域的结果更新到第一个参数 rect 里。也就是说,调用之后 rect 变成了两个矩形的重叠部分。
计算并集
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 20, 20);
let rect2 = drawing.RectUtils.makeLtrb(10, 10, 40, 40);
drawing.RectUtils.union(rect, rect2);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
union 计算两个矩形的"最小包围矩形"——就是刚好能包含两个矩形的最小矩形。结果会更新到第一个参数 rect 里。
如果第一个矩形是空的,结果会直接用第二个矩形。如果第二个矩形是空的,不做任何操作。
调整矩形边界
inset:向内/向外调整边界:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 10, 20, 20);
drawing.RectUtils.inset(rect, 10, -20, 30, 60);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
inset 把传入的值分别加到矩形的左、上、右、下边界上。传正数就是边界往外扩(矩形变大),传负数就是边界往里缩(矩形变小)。
这在做"内边距"或"外边距"计算时很有用。比如你想让一个矩形的四周都留出 10px 的间距:
drawing.RectUtils.inset(rect, -10, -10, 10, 10);
平移矩形
offset:相对平移:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(0, 0, 20, 20);
drawing.RectUtils.offset(rect, 10, 20);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
offset(rect, dx, dy) 让矩形向右移动 dx,向下移动 dy。传负数就是往反方向移动。
offsetTo:绝对平移:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(20, 20, 40, 40);
drawing.RectUtils.offsetTo(rect, 10, 20);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
offsetTo 和 offset 的区别是:offset 是相对移动(移动多少),offsetTo 是绝对移动(移动到哪)。offsetTo(rect, 10, 20) 会把矩形的左上角移到 (10, 20) 的位置,矩形的大小不变。
其他实用方法
判断矩形是否为空:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeEmpty();
let isEmpty = drawing.RectUtils.isEmpty(rect);
console.info('isEmpty :', isEmpty);
let rect2 = drawing.RectUtils.makeLtrb(0, 0, 20, 20);
isEmpty = drawing.RectUtils.isEmpty(rect2);
console.info('isEmpty :', isEmpty);
当左边界大于等于右边界,或者上边界大于等于下边界时,矩形就被认为是"空"的。
判断两个矩形是否相等:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 20, 20, 30);
let rect2 = drawing.RectUtils.makeEmpty();
let isEqual = drawing.RectUtils.isEqual(rect, rect2);
console.info('isEqual :', isEqual);
isEqual 逐个比较两个矩形的 left、top、right、bottom,全部相等才返回 true。
修正反转的矩形:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(20, 40, 30, 30);
drawing.RectUtils.sort(rect);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
如果矩形的边界是"反转"的(比如左 > 右,或者上 > 下),sort 会自动交换它们,让矩形变得"正常"。
重新设置矩形的值:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeEmpty();
drawing.RectUtils.setLtrb(rect, 10, 20, 30, 60);
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 20, 30, 40);
let rect2 = drawing.RectUtils.makeEmpty();
drawing.RectUtils.setRect(rect2, rect);
console.info('rect2.left:', rect2.left);
console.info('rect2.top: ', rect2.top);
console.info('rect2.right: ', rect2.right);
console.info('rect2.bottom: ', rect2.bottom);
setLtrb 用坐标值来更新矩形,setRect 用另一个矩形来更新。还有 setEmpty 可以把矩形清空:
import { drawing } from '@kit.ArkGraphics2D';
let rect = drawing.RectUtils.makeLtrb(10, 20, 20, 30);
drawing.RectUtils.setEmpty(rect)
console.info('rect.left:', rect.left);
console.info('rect.top: ', rect.top);
console.info('rect.right: ', rect.right);
console.info('rect.bottom: ', rect.bottom);
实际应用场景
下面是碰撞检测的典型流程:
场景一:点击检测
用户点击了屏幕,你想知道他是不是点在了某个按钮上:
let buttonRect = drawing.RectUtils.makeLtrb(100, 200, 300, 250);
let isHit = drawing.RectUtils.contains(buttonRect, touchX, touchY);
if (isHit) {
// 用户点到了按钮
}
场景二:碰撞检测
在游戏开发中,检测两个物体有没有碰到:
let playerRect = drawing.RectUtils.makeLtrb(playerX, playerY, playerX + 50, playerY + 50);
let enemyRect = drawing.RectUtils.makeLtrb(enemyX, enemyY, enemyX + 40, enemyY + 40);
let isColliding = drawing.RectUtils.isIntersect(playerRect, enemyRect);
场景三:计算可见区域
列表滚动时,计算哪些项在可视区域内:
let viewport = drawing.RectUtils.makeLtrb(0, scrollY, screenWidth, scrollY + screenHeight);
for (let item of items) {
let itemRect = drawing.RectUtils.makeLtrb(item.x, item.y, item.x + item.width, item.y + item.height);
if (drawing.RectUtils.isIntersect(viewport, itemRect)) {
// 这个项在可视区域内,需要渲染
}
}
场景四:布局计算
计算元素的包围盒:
let boundingBox = drawing.RectUtils.makeCopy(firstElement.rect);
for (let i = 1; i < elements.length; i++) {
drawing.RectUtils.union(boundingBox, elements[i].rect);
}
// boundingBox 现在是所有元素的最小包围矩形
注意事项
-
API 版本:RectUtils 从 API version 20 开始支持。
-
物理像素:所有坐标和尺寸都是物理像素 px。
-
线程安全:和其他 drawing 对象一样,是单线程模型。
-
空矩形的特殊性:空矩形不包含任何矩形或点,和任何矩形的交集也是空的。
-
边界包含规则:左边界和上边界属于矩形内部,右边界和下边界不属于。这和 Android 的 Rect 类似。
小结
RectUtils 是一个非常实用的矩形工具类,提供了创建、拷贝、计算宽高、判断包含关系、计算交集并集、平移、调整边界等一系列操作。
在实际开发中,矩形操作无处不在——点击检测、碰撞检测、布局计算、可见区域判断等等。有了 RectUtils,这些操作都变得非常简单。记住它是从 API version 20 开始支持的,确保你的项目版本符合要求。
更多推荐

所有评论(0)