带背景的 Qt QML SwipeView,如何在页面移动时模糊正确的区域?

Qt QML SwipeView w/ background, how can I blur the right region as the page moves?

我有一个带背景图片的 SwipeView。它有两个页面,两个页面都有一组控件和文本,我在它们下面放了一个类似 iOS 的模糊圆角矩形,这样它更容易阅读,但不会完全遮挡漂亮的背景

我在这个线程的末尾发现了这个片段,它让我使用了一个白色矩形和一个半透明的 FastBlur 来对背景进行采样:https://forum.qt.io/topic/38297/a-semi-transparent-rectangle-that-also-blurs/7

Rectangle {
    anchors.fill: fastBlur
    color: "white"
}

FastBlur {
    id: fastBlur

    height: 124

    width: parent.width
    radius: 40
    opacity: 0.55

    source: ShaderEffectSource {
        sourceItem: flickable
        sourceRect: Qt.rect(0, 0, fastBlur.width, fastBlur.height)
    }
}

当我移动到 SwipeView 中的下一页时,背景保持静态但模糊没有,当然,因为 sourceRect 区域没有相对于其父级移动,只是父级正在移动相对于背景。模糊仍在从其原始位置采样背景,而不是在它通过滑动视图移动时的新位置

所以我发现我可以获得 SwipeView.contentItem.contentX 的事实(感谢 的回答!)但是在我的两个页面中,我必须以不同的方式考虑基于 contentX 的内容他们的顺序是什么

我在这里设置了一个示例模糊项目来说明我的意思。 https://github.com/ftab/BlurTest

具体来说,在 sourceRect - https://github.com/ftab/BlurTest/blob/master/Page1Form.qml#L23

sourceRect: Qt.rect(
                fastBlur.x - swipeView.contentItem.contentX,
                fastBlur.y,
                fastBlur.width,
                fastBlur.height)

当它在背景中移动时,这会获得模糊更新的预期效果,但仅在模糊区域位于滑动视图的第一页并且 contentX 从 0 变为 640 时才起作用。如果这是在第二页,我必须做 640 - contentX + fastBlur.x,然后如果我再次移动它,我必须再次调整它,等等

有没有更好的方法来做到这一点,或者每当我添加另一个页面或更改顺序时,我几乎都无法手动编辑 sourceRect?

编辑:完整的 QML 文件,例如 3 页:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtGraphicalEffects 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    SwipeView {
        id: swipeView
        anchors.fill: parent
        currentIndex: tabBar.currentIndex
        background: Image {
            id: imgBackground
            anchors.fill: parent
            fillMode: Image.PreserveAspectCrop
            source: "nature_1.jpg"
        }

        Item {
            id: item1

            Rectangle {
                id: recFrost
                anchors.fill: fastBlur
                color: "white"
            }
            FastBlur {
                id: fastBlur
                anchors.fill: recControls
                source: ShaderEffectSource {
                    sourceItem: imgBackground

                    sourceRect: Qt.rect(
                                    fastBlur.x - swipeView.contentItem.contentX,
                                    fastBlur.y,
                                    fastBlur.width,
                                    fastBlur.height)
                }
                radius: 32
                opacity: 0.55
            }
            Rectangle {
                id: recControls
                width: 364
                height: 89
                color: "#00000000"
                anchors.bottomMargin: 8
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.bottom: parent.bottom

                Label {
                    text: qsTr("First page")
                    anchors.centerIn: parent
                }
            }
        }

        Item {
            id: item2

            Rectangle {
                id: recFrost2
                anchors.fill: fastBlur2
                color: "white"
            }
            FastBlur {
                id: fastBlur2
                anchors.fill: recControls2
                source: ShaderEffectSource {
                    sourceItem: imgBackground

                    sourceRect: Qt.rect(
                                    swipeView.width - swipeView.contentItem.contentX + fastBlur2.x,
                                    fastBlur2.y,
                                    fastBlur2.width,
                                    fastBlur2.height)
                }
                radius: 32
                opacity: 0.55
            }
            Rectangle {
                id: recControls2
                width: 364
                height: 89
                color: "#00000000"
                anchors.centerIn: parent

                Label {
                    text: qsTr("Second page")
                    anchors.centerIn: parent
                }
            }
        }

        Item {
            id: item3

            Rectangle {
                id: recFrost3
                anchors.fill: fastBlur3
                color: "white"
            }
            FastBlur {
                id: fastBlur3
                anchors.fill: recControls3
                source: ShaderEffectSource {
                    sourceItem: imgBackground

                    sourceRect: Qt.rect(
                                    swipeView.width * 2 - swipeView.contentItem.contentX + fastBlur2.x,
                                    fastBlur3.y,
                                    fastBlur3.width,
                                    fastBlur3.height)
                }
                radius: 32
                opacity: 0.55
            }
            Rectangle {
                id: recControls3
                width: 364
                height: 89
                color: "#00000000"
                anchors.topMargin: 8
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.top: parent.top

                Label {
                    text: qsTr("Third page")
                    anchors.centerIn: parent
                }
            }
        }
    }

    footer: TabBar {
        id: tabBar
        currentIndex: swipeView.currentIndex
        TabButton {
            text: qsTr("First")
        }
        TabButton {
            text: qsTr("Second")
        }
        TabButton {
            text: qsTr("Third")
        }
    }
}

您可以使用父页面在 SwipeView 中的 x 位置代替 swipeView.width 增量:

sourceRect: Qt.rect(fastBlur2.x - swipeView.contentItem.contentX + item2.x,
                    fastBlur2.y,
                    fastBlur2.width,
                    astBlur2.height)

或者您可以 ItemmapToItem()。 然而,由于 mapToItem 是一个函数,当一个项目相对于另一个项目移动时,使用它的 return 值不会触发绑定更新,我们必须温和地提示 QML 引擎何时重新评估绑定。

sourceRect: {
    swipeView.contentItem.contentX; /*here we access contentX in order to
    force the reevaluation of the entire expression when contentX changes.*/
    return fastBlur3.mapToItem(swipeView, 0, 0, fastBlur3.width, fastBlur3.height);
}