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

QML中如何将文本居中显示在PathAngleArc形状内

QML楔形内文本居中解决方案

需求说明

需要在QML中实现圆形上的楔形形状,并将显示楔形角度的文本始终居中于楔形内部,对比效果如下:

所需效果:
所需文本效果

当前效果:
当前文本效果

现有代码

Item {
    id: root

    width: size
    height: size

    layer.enabled: true
    layer.samples: 8

    property int size: 600
    property real arcOffset: 0
    property real arcBegin: 90            // start arc angle in degree
    property real arcEnd: 105            // end arc angle in degree
    property real lineWidth: 80

    property string colorCircle: "#CC3333"
    property string colorBackground: "#779933"
    property int animationDuration: 500 // 补充缺失的动画时长属性

    onArcBeginChanged: canvas.requestPaint()
    onArcEndChanged: canvas.requestPaint()

    Behavior on arcBegin {
       id: animationArcBegin
       enabled: true
       NumberAnimation {
           duration: root.animationDuration
           easing.type: Easing.InOutCubic
       }
    }

    Behavior on arcEnd {
       id: animationArcEnd
       enabled: true
       NumberAnimation {
           duration: root.animationDuration
           easing.type: Easing.InOutCubic
       }
    }

    Canvas {
        id: canvas
        anchors.fill: parent
        rotation: -90 + parent.arcOffset

        onPaint: {
            var context = getContext("2d")
            var x = width /2
            var y = height / 2
            var start = Math.PI * (parent.arcBegin / 180)
            var end = Math.PI * (parent.arcEnd / 180)

            context.reset()

            context.beginPath();
            context.arc(x, y, (width / 2) - parent.lineWidth / 2, 0, Math.PI * 2, false)
            context.lineWidth = root.lineWidth
            context.strokeStyle = Material.backgroundDimColor
            context.stroke()

            // 启用楔形绘制代码
            context.beginPath();
            context.arc(x, y, (width / 2) - parent.lineWidth / 2, start, end, false)
            context.lineWidth = root.lineWidth
            context.strokeStyle = root.colorCircle
            context.stroke()
        }

        Shape {
            width: 100
            height: 100
            ShapePath {
                strokeWidth: 0
                strokeColor: "red"
                fillColor: "red"

                PathAngleArc {
                    id: firstArc
                    centerX: root.width/2
                    centerY: root.height/2
                    radiusX: root.width/2
                    radiusY: root.height/2
                    startAngle: root.arcBegin
                    sweepAngle: 15 // 固定值无法适配动态角度
                }

                PathAngleArc {
                    centerX: root.width/2
                    centerY: root.height/2
                    radiusX: root.width/2 - root.lineWidth
                    radiusY: root.height/2 - root.lineWidth
                    startAngle: firstArc.startAngle + firstArc.sweepAngle -1
                    sweepAngle: -13 // 固定值无法适配动态角度
                    moveToStart: false
                }
            }

            Text {
                anchors.centerIn: parent
                font.pointSize: 12
                text: (root.arcBegin + root.arcEnd)/2 + "\xB0"
            }
        }
    }
}

问题分析

  1. Shape定位无效:Shape设置了固定宽高100x100,未覆盖楔形实际区域,导致anchors.centerIn: parent无法将文本定位到楔形中心。
  2. 楔形绘制静态化:Shape中的sweepAngle为固定值,无法跟随arcBegin/arcEnd的动态变化更新楔形范围。
  3. 未适配Canvas旋转:Canvas已设置rotation: -90 + parent.arcOffset,文本位置计算未匹配该旋转逻辑。

解决方案

核心思路

直接通过几何计算确定楔形中心坐标,无需依赖Shape锚定:

  1. 计算楔形的中心角度:(arcBegin + arcEnd)/2
  2. 取环形中线半径:width/2 - lineWidth/2(外半径与内半径的中间值)
  3. 将极坐标转换为Canvas笛卡尔坐标,定位文本并适配旋转逻辑。

修改后的代码

替换原Canvas内的Shape和Text部分为以下内容:

// 动态计算位置的角度文本
Text {
    font.pointSize: 12
    text: (root.arcBegin + root.arcEnd)/2 + "\xB0"

    // 计算中心角度(弧度)
    property real centerAngle: (root.arcBegin + root.arcEnd) / 2
    property real centerAngleRad: Math.PI * centerAngle / 180
    // 环形中线半径
    property real midRadius: root.width/2 - root.lineWidth/2

    // 基于Canvas中心计算文本位置,偏移自身宽高一半实现居中
    x: canvas.width/2 + Math.cos(centerAngleRad) * midRadius - width/2
    y: canvas.height/2 + Math.sin(centerAngleRad) * midRadius - height/2
}

// 适配动态角度的红色楔形填充
Shape {
    anchors.fill: parent // 让Shape充满Canvas,避免固定宽高限制
    ShapePath {
        strokeWidth: 0
        fillColor: "red"

        PathAngleArc {
            id: outerArc
            centerX: root.width/2
            centerY: root.height/2
            radiusX: root.width/2
            radiusY: root.height/2
            startAngle: root.arcBegin
            sweepAngle: root.arcEnd - root.arcBegin // 动态计算扫过角度
        }

        PathAngleArc {
            centerX: root.width/2
            centerY: root.height/2
            radiusX: root.width/2 - root.lineWidth
            radiusY: root.height/2 - root.lineWidth
            startAngle: root.arcEnd // 从外弧终点反向绘制内弧
            sweepAngle: root.arcBegin - root.arcEnd
            moveToStart: false
        }
    }
}

关键修改点

  • 移除Shape的固定宽高,设置anchors.fill: parent使其覆盖整个Canvas区域。
  • 将Shape中固定的sweepAngle改为root.arcEnd - root.arcBegin,实现楔形的动态更新。
  • 文本直接通过极坐标转换计算位置,确保始终处于楔形中心,无需依赖Shape锚定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 17:06:30