如何在 qml 中创建一个粘性按钮?

How can I create a sticky button in qml?

我想创建一个可以被用户拖动的按钮,当释放时将 return 回到原来的位置。

在移动过程中,按钮必须按照一些缓和曲线进行动画处理。

我目前的情况是这样的:

import QtQuick 2.2
import QtGraphicalEffects 1.0

Item {
    property bool wasDragged: false
    property real basePosX: (menuButton.width / 2) - (menuButtonIcon.width / 2) //menuButton.horizontalCenter
    property real basePosY: (menuButton.height / 2) - (menuButtonIcon.height / 2) //menuButton.verticalCenter

    id: menuButton
    x: parent.width - 55
    y: parent.height - 60
    state: "Closed"

    Rectangle {
        id: menuButtonIcon
        x: basePosX
        y: basePosY
        color: "#D23F31"
        width: 65;
        height: 65;
        radius: width * 0.5
        antialiasing: true
        smooth: true
    }

    Rectangle {
        id: menuButtonIconBar1
        anchors.centerIn: menuButtonIcon
        width: 17
        height: 3
        antialiasing: true
    }

    Rectangle {
        id: menuButtonIconBar2
        anchors.centerIn: menuButtonIcon
        width: 17
        height: 3
        antialiasing: true
        rotation: 90
    }

    MouseArea {
        id: mouseArea
        anchors.fill: menuButtonIcon
        onPressed: menuButton.state = menuButton.state === "Open" ? "Closed" : "Open"
        onReleased: {
            if (wasDragged) {
                wasDragged = false
                menuButton.state = "Closed"
            }
        }

        onPositionChanged:
        {
            wasDragged = true
            var currentPosition = mapToItem(parent, mouse.x, mouse.y)
            updateButtonPosition(currentPosition.x, currentPosition.y)
        }
    }

    function updateButtonPosition(mouseX, mouseY)
    {
        console.log("pos change")
        menuButtonIcon.x = mouseX - (menuButtonIcon.width / 2);
        menuButtonIcon.y = mouseY - (menuButtonIcon.height / 2);
    }

    states: [
        State {
            name: "Closed"
            PropertyChanges { target: menuButtonIcon; x: basePosX; y: basePosY}
        },
        State {
            name: "Open"
            PropertyChanges { target: menuButtonIconBar1; rotation: 135;}
            PropertyChanges { target: menuButtonIconBar2; rotation: 225;}
            PropertyChanges { target: menuButtonIcon; x: basePosX; y: basePosY}
        }
    ]

    transitions: [
        Transition {
            SpringAnimation { target: menuButtonIcon; properties: "x, y"; spring: 3; damping: 0.25; epsilon: 0.2; mass: 1; modulus: 0; velocity: 0 }
            SpringAnimation { target: menuButtonIconBar1; properties: "rotation, scale"; spring: 3; damping: 0.25; epsilon: 0.5; mass: 1; modulus: 0; velocity: 0 }
            SpringAnimation { target: menuButtonIconBar2; properties: "rotation, scale"; spring: 3; damping: 0.25; epsilon: 0.5; mass: 1; modulus: 0; velocity: 0 }
        }
    ]
}

但这样做的问题是,如果您在旋转动画结束前拖动它并保持不动,即使您从未松开它,该按钮也会 return 到其原始位置。

如果删除构成十字的两个 Rectangle 以及它们的动画,则它会按预期工作。

为什么按钮 return 会自行回到原来的位置,如何修复?

试试这个:

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
    property bool wasDragged: false
    property real basePosX: (menuButton.width / 2) - (menuButtonIcon.width / 2)
    property real basePosY: (menuButton.height / 2) - (menuButtonIcon.height / 2)
    property real currentPosX: (menuButton.width / 2) - (menuButtonIcon.width / 2)
    property real currentPosY: (menuButton.height / 2) - (menuButtonIcon.height / 2)

    id: menuButton
    x: parent.width - 55
    y: parent.height - 60
    state: "Closed"

    Rectangle {
        id: menuButtonIcon
        x: basePosX
        y: basePosY
        color: "#D23F31"
        width: 65;
        height: 65;
        radius: width * 0.5
        antialiasing: true
        smooth: true
    }

    Rectangle {
        id: menuButtonIconBar1
        anchors.centerIn: menuButtonIcon
        width: 17
        height: 3
        antialiasing: true
    }

    Rectangle {
        id: menuButtonIconBar2
        anchors.centerIn: menuButtonIcon
        width: 17
        height: 3
        antialiasing: true
        rotation: 90
    }

    MouseArea {
        id: mouseArea
        anchors.fill: menuButtonIcon
        onPressed: menuButton.state = menuButton.state === "Open" ? "Closed" : "Open"
        onReleased: {
            if (wasDragged) {
                wasDragged = false
                menuButton.state = "Closed"
            }
        }

        onPositionChanged:
        {
            wasDragged = true
            var currentPosition = mapToItem(parent, mouse.x, mouse.y)
            updateButtonPosition(currentPosition.x, currentPosition.y)
        }
    }

    function updateButtonPosition(mouseX, mouseY)
    {
        console.log("pos change")
        menuButtonIcon.x = mouseX - (menuButtonIcon.width / 2);
        menuButtonIcon.y = mouseY - (menuButtonIcon.height / 2);
        currentPosX = mouseX - (menuButtonIcon.width / 2);
        currentPosY = mouseY - (menuButtonIcon.height / 2);
    }

    states: [
        State {
            name: "Closed"
            PropertyChanges { target: menuButtonIcon; x: currentPosX; y: currentPosY}
        },
        State {
            name: "Open"
            PropertyChanges { target: menuButtonIconBar1; rotation: 135;}
            PropertyChanges { target: menuButtonIconBar2; rotation: 225;}
            PropertyChanges { target: menuButtonIcon; x: currentPosX; y: currentPosY}
        }
    ]

    onStateChanged: if(state === "Closed"){
                        currentPosX = basePosX
                        currentPosY = basePosY
                    }

    transitions: [
        Transition {
            SpringAnimation {
                target: menuButtonIcon;
                properties: "x, y";
                spring: 3;
                damping: 0.25;
                epsilon: 0.2;
                mass: 1;
                modulus: 0;
                velocity: 0
            }
            SpringAnimation {
                id:tr1;
                target: menuButtonIconBar1;
                properties: "rotation, scale";
                spring: 3;
                damping: 0.25;
                epsilon: 0.5;
                mass: 1;
                modulus: 0;
                velocity: 0
            }
            SpringAnimation {
                id:tr2;
                target: menuButtonIconBar2;
                properties: "rotation, scale";
                spring: 3;
                damping: 0.25;
                epsilon: 0.5;
                mass: 1;
                modulus: 0;
                velocity: 0
            }
        }
    ]
}

在 open 状态下你应该做一个 属性当前位置的 x 和 y 的变化,绑定在基础位置导致 x 和 y 的重置。您将需要采用当前 x 和当前 y 的其他属性。 解决了 90% 的问题后,您会看到一个小的 decalage 大约 1 秒,按钮将 return 移到合适的位置。 为了解决它,我将 属性 更改为关闭状态,x 和 y 更改为 currentPosition 并在其中添加了一个 StateChanged 插槽,我重置了位置。 我不知道这种奇怪行为的根本原因。但有了这个解决方法,它工作正常。 问候