使用中继器而不是 canvas 来弯曲文本

Using a repeater instead of a canvas to curve text

而不是使用 canvas 来弯曲文本以适合图像。是否可以使用 Repeater 来达到同样的效果?目前,我的代码使用 Canvas 完成了我需要它做的事情。但我很好奇,使用 Repeater 来完成我最终想要的东西对我来说也可能更好。我怎样才能修改我现有的代码来实现这个?

import QtQuick 2.15
import QtQuick.Window 2.15
Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    property string letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    Image {
        id: abcBar
        source: "alphabetBar.png"
        visible: false
        anchors.right:  parent.right
        anchors.top: parent.top
        anchors.rightMargin: -10
    }

//    Rectangle {
//        id: body
//        anchors.fill: parent
//        readonly property real dx: 12

//        function midpoint(idx) {
//          return 10 + idx * body.dx;
//        }

//        Repeater {
//            anchors.fill: parent
//            model: root.letters.length
//            Text {
//                anchors.horizontalCenter: parent.left
//                anchors.horizontalCenterOffset: body.midpoint(index)

//                anchors.verticalCenter: parent.verticalCenter
//                text: root.letters[index]
//            }
//        }
//    }

    Canvas {
        anchors.fill: parent

        onPaint: {
            var ctx = getContext('2d');
            ctx.save();
            ctx.canvas.width = 160;
            ctx.canvas.height = 432;
            ctx.font = "18px Roboto";
            ctx.textAlign = "center";
            ctx.fillStyle = "#000000"

            const centerX = 10;
            const centerY = ctx.canvas.height / 2;
            const angle = Math.PI;
            const radius = 130
            ctx.fillStyle = "#000000"
            ctx.restore();


            ctx.save();
            ctx.drawImage(abcBar, 0, 0, abcBar.width, abcBar.height);
            console.log('loaded')
            ctx.restore();

            const args = {
                ctx,
//                text: "A • D • G • J • M • P • S • V • Z",
                text: root.letters,
                offset: 0,
                G1: {
                    x: 20,
                    y: 80,
                },
                G2: {
                    x: 190,
                    y: 230,
                },
                G3: {
                    x: 0,
                    y: 372
                },
            }
            textOnCurve(args);
        }
    }
    function textOnCurve({ ctx, text, offset, G1, G2, G3, G4}){

        const x1 = G1.x;
        const y1 = G1.y;
        const x2 = G2.x;
        const y2 = G2.y;
        const x3 = G3.x;
        const y3 = G3.y;
        const x4 = G3.x;
        const y4 = G3.y;

        ctx.save();
        ctx.textAlign = "center";

        var widths = [];
        for (var i = 0; i < text.length; i++)
        {
            widths[widths.length] = ctx.measureText(text[i]).width;
        }

        ctx.beginPath();
        var ch = curveHelper(x1, y1, x2, y2, x3, y3, x4, y4);
        ctx.stroke();

        var pos = offset;
        var cpos = 0;
        for (var j = 0; j < text.length; j++)
        {
            pos += widths[j] / 2;
            cpos = ch.forward(pos);
            ch.tangent(cpos);
            ctx.setTransform(ch.vect.x, ch.vect.y, -ch.vect.y, ch.vect.x, ch.vec.x, ch.vec.y);
            ctx.fillText(text[j], 0, 0);
            pos += widths[j] / 2;
        }
        ctx.restore();
    }

    function curveHelper(x1, y1, x2, y2, x3, y3, x4, y4)
    {
        var tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4;
        var a, b, c, u;
        var vec, currentPos, vec1, vect, quad, currentDist;
        vec = { x: 0, y: 0 } ;
        vec1 = { x: 0, y: 0 } ;
        vect = { x: 0, y: 0 } ;
        quad = false;
        currentPos = 0;
        currentDist = 0;
        if (x4 === undefined || x4 === null)
        {
            quad = true;
            x4 = x3;
            y4 = y3;
        }
        var estLen = Math.sqrt((x4 - x1)* (x4 - x1)+ (y4 - y1)* (y4 - y1));
        var onePix = 1 / estLen;

        function posAtC(c)
        {
            tx1 = x1; ty1 = y1;
            tx2 = x2; ty2 = y2;
            tx3 = x3; ty3 = y3;
            tx1 += (tx2 - tx1)* c;
            ty1 += (ty2 - ty1)* c;
            tx2 += (tx3 - tx2)* c;
            ty2 += (ty3 - ty2)* c;
            tx3 += (x4 - tx3)* c;
            ty3 += (y4 - ty3)* c;
            tx1 += (tx2 - tx1)* c;
            ty1 += (ty2 - ty1)* c;
            tx2 += (tx3 - tx2)* c;
            ty2 += (ty3 - ty2)* c;
            vec.x = tx1 + (tx2 - tx1)* c;
            vec.y = ty1 + (ty2 - ty1)* c;
            return vec;
        }

        function posAtQ(c)
        {
            tx1 = x1; ty1 = y1;
            tx2 = x2; ty2 = y2;
            tx1 += (tx2 - tx1)* c;
            ty1 += (ty2 - ty1)* c;
            tx2 += (x3 - tx2)* c;
            ty2 += (y3 - ty2)* c;
            vec.x = tx1 + (tx2 - tx1)* c;
            vec.y = ty1 + (ty2 - ty1)* c;
            return vec;
        }

        function forward(dist)
        {
            var step;
            helper.posAt(currentPos);
            while (currentDist < dist)
            {
                vec1.x = vec.x;
                vec1.y = vec.y;
                currentPos += onePix;
                helper.posAt(currentPos);
                currentDist += step = Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
            }
                currentPos -= ((currentDist - dist)/ step)* onePix
                currentDist -= step;
                helper.posAt(currentPos);
                currentDist += Math.sqrt((vec.x - vec1.x)* (vec.x - vec1.x)+ (vec.y - vec1.y)* (vec.y - vec1.y));
                return currentPos;
        }

        function tangentQ(pos)
        {
            a = (1 - pos)* 2;
            b = pos * 2;
            vect.x = a * (x2 - x1)+ b * (x3 - x2);
            vect.y = a * (y2 - y1)+ b * (y3 - y2);
            u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
            vect.x /= u;
            vect.y /= u;
        }

        function tangentC(pos)
        {
            a = (1 - pos)
            b = 6 * a * pos;
            a *= 3 * a;
            c = 3 * pos * pos;
            vect.x = -x1 * a + x2 * (a - b)+ x3 * (b - c)+ x4 * c;
            vect.y = -y1 * a + y2 * (a - b)+ y3 * (b - c)+ y4 * c;
            u = Math.sqrt(vect.x * vect.x + vect.y * vect.y);
            vect.x /= u;
            vect.y /= u;
        }

        var helper = {
            vec: vec,
            vect: vect,
            forward: forward,
        }

        if (quad)
        {
            helper.posAt = posAtQ;
            helper.tangent = tangentQ;
        } else
        {
            helper.posAt = posAtC;
            helper.tangent = tangentC;
        }
            return helper
    }
}

当然可以:

Rectangle {
    id: control
    width: Math.min(parent.height, parent.width)
    height: width
    anchors.centerIn: parent
    color: "#40ff0000" //to see where it is going, which helps in positioning

    property double startAngle: -45
    property double endAngle: 45

    Repeater {
        model: 26

        Text {
            property double angle: control.startAngle + (control.endAngle - control.startAngle) * (index / 26)
            x: (control.width  / 2) * (1 + 0.9 * Math.cos(Math.PI * angle / 180)) - width * 0.5
            y: (control.height / 2) * (1 + 0.9 * Math.sin(Math.PI * angle / 180)) - height * 0.5
            text: String.fromCharCode(index + 65)
            //rotation: angle
        }
    }
}

如果您想为每个文本添加一个 MouseArea 或将其更改为一个按钮,这也是一个明智的选择。

请注意,您必须根据图像稍微调整矩形的位置。