You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QML实现不同父层级元素与checker的相交碰撞检测

实现方案

核心逻辑分三步:递归找到所有嵌套层级下的Point实例、把所有待检测元素的坐标转到同一参考系、做矩形相交判断。

第一步:给Point组件加识别标记

因为Point本身继承自Rectangle,和界面里其他矩形元素是同个基类,先给Point.qml加个专属只读属性,避免遍历的时候把其他矩形误判成Point:

// Point.qml
import QtQuick 2.0

Rectangle {
    width: 10
    height: 10
    color: "black"
    // 专属标记,用于识别Point实例
    readonly property bool isPoint: true
}

第二步:补全检测逻辑

直接替换MouseArea里的对应代码即可,递归遍历会自动扫完area下所有嵌套层级的子元素,不管Point藏在哪一层都能找到:

MouseArea
{
    anchors.fill: parent
    drag.target: parent

    onReleased:
    {
        var point = getCoveredPoint()
        if (point)
        {
            console.log("Point ", point, " is covered")
        }
    }

    // 递归遍历父元素下所有子节点,收集全部Point实例
    function findAllPoints(parentItem) {
        let pointList = []
        for (let i = 0; i < parentItem.children.length; i++) {
            const child = parentItem.children[i]
            if (child.isPoint === true) {
                pointList.push(child)
            }
            // 有子节点就继续往下找
            if (child.children.length) {
                pointList = pointList.concat(findAllPoints(child))
            }
        }
        return pointList
    }

    function getCoveredPoint()
    {
        // 拿到所有Point实例
        const allPoints = findAllPoints(area)
        // 把checker的坐标转到area的坐标系下,统一参考系
        const checkerPos = checker.mapToItem(area, 0, 0)
        const checkerBounds = {
            left: checkerPos.x,
            top: checkerPos.y,
            right: checkerPos.x + checker.width,
            bottom: checkerPos.y + checker.height
        }

        // 逐个判断相交
        for (let p of allPoints) {
            const pPos = p.mapToItem(area, 0, 0)
            const pBounds = {
                left: pPos.x,
                top: pPos.y,
                right: pPos.x + p.width,
                bottom: pPos.y + p.height
            }
            // 轴对齐矩形相交判断:不满足「完全在上下左右任意一侧」就是相交
            const intersect = !(
                checkerBounds.right < pBounds.left ||
                checkerBounds.left > pBounds.right ||
                checkerBounds.bottom < pBounds.top ||
                checkerBounds.top > pBounds.bottom
            )
            if (intersect) return p
        }
        return null
    }
}

关键说明

  • 绝对不能直接拿元素的x、y属性直接比较:这两个值是元素相对于自己直接父元素的局部坐标,嵌套层级不同时参考系完全不一样,算出来的结果是错的,必须用mapToItem()转到同一坐标系下再计算。
  • 如果界面上元素很多,每次拖拽结束都递归遍历全量子元素会有性能损耗,可以在Point组件初始化的时候把自己注册到一个顶层的数组里,销毁的时候移除,检测的时候直接遍历这个数组就行,不用每次递归扫整棵元素树。
  • 上面的逻辑是找到第一个相交的Point就返回,如果需要拿到所有被覆盖的Point,只要把相交的实例存到数组里,遍历完所有Point之后返回数组即可。

内容的提问来源于stack exchange,提问作者serheo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:24:19