在Qt Quick(QML) Flickable中实现缩放/比例的正确方法

10

我正在使用Qt 5.2 beta,Qt Quick 2.1。

我遇到了一个关于Flickable组件的问题。

这是一个最小工作示例:

import QtQuick 2.1
import QtQuick.Controls 1.0

ApplicationWindow {
    width: 300
    height: 200

    Rectangle {
        anchors.fill: parent
        color: "green"

        Flickable {
            id: flickArea
            anchors {fill: parent; margins: 10; }
            contentWidth: rect.width;
            contentHeight: rect.height
            Rectangle {
                id: rect
                x: 0; y: 0;
                width: 200; height: 300;
                color: "lightgrey"

                Rectangle {
                    anchors { fill: parent; margins: 10; }
                    color: "red"
                }
            }
        }
    }
    Button {
        anchors.bottom: parent.bottom;
        anchors.horizontalCenter: parent.horizontalCenter;
        text: "Scale flickArea"
        onClicked: { flickArea.scale += 0.2; }
    }
}

当我进行缩放时,我希望所有子元素仍然保持原来的可见性,并且内部区域变得更大。
但是,相反,子元素会移出可滚动内容。
有人能提出一种正常的方法来避免这个问题,而不需要手动重新计算所有偏移量吗?
3个回答

5
我用这种方式使其按预期工作了,但我认为这种方式有点棘手:
import QtQuick 2.1
import QtQuick.Controls 1.0

ApplicationWindow {
    width: 300
    height: 200

    Rectangle {
        anchors.fill: parent
        color: "green"

        Flickable {
            id: flickArea
            anchors {fill: parent; margins: 10; }
            contentWidth: rect.width*rect.scale
            contentHeight: rect.height*rect.scale
            Rectangle {
                id: rect
                transformOrigin: Item.TopLeft
                x: 0; y: 0;
                width: 200; height: 300;
                color: "lightgrey"

                Rectangle {
                    id: inner
                    anchors { fill: parent; margins: 10; }
                    color: "red"
                }
            }
        }
    }
    Button {
        anchors.bottom: parent.bottom;
        anchors.horizontalCenter: parent.horizontalCenter;
        text: "Scale flickArea"
        onClicked: {
            rect.scale += 0.2;
        }
    }
}

有没有更好的建议?

更新:我已经一个多年都没有关闭这个问题,但仍然没有得到任何更好的解决方案或更好的方法来完成事情。


我现在正在使用手机,这只是一个提醒,稍后再回答这个问题。我认为更好的方法是使用Scale QML类型并设置相应的原点。 - Herr von Wurst

0

在 QML 中实现可缩放图像的正确方法

import QtQuick 2.15

Flickable
{
    id:flickable
    property int imageWidth
    property int imageHeight
    property alias source: mImage.source
    contentWidth: imageWidth
    contentHeight:imageHeight

    boundsBehavior: Flickable.StopAtBounds
    boundsMovement: Flickable.StopAtBounds


    states: [
        State {
            name: "state_StickToCenter" // state is used when content size is less than flickable size then content
            // center should stick to flickable center
            when : ( flickable.contentWidth < flickable.width || flickable.contentHeight< flickable.height )
            PropertyChanges {
                target: flickable.contentItem
                anchors.horizontalCenter:  width < flickable.width ? flickable.horizontalCenter : undefined
                anchors.verticalCenter:   height < flickable.height ? flickable.verticalCenter : undefined
            }
        }
    ]
    onStateChanged: { cancelFlick(); returnToBounds(); }


    Image
    {
        id:mImage
        width:flickable.contentWidth;
        height: flickable.contentHeight;
        source: flickable.imageSource
        fillMode: Image.PreserveAspectFit;
        Component.onCompleted:
        {
            imageWidth = mImage.paintedWidth;
            imageHeight = mImage.paintedHeight
        }

        autoTransform: true

        PinchArea
        {
            id:pinchArea
            anchors.fill: parent
            property bool zoomTriggeredFromPinchArea:false
            property point pinchCenter;


            onPinchStarted: zoomTriggeredFromPinchArea=true;
            onPinchUpdated:
            {
                var newZoomFactor = privateProperties.currentZoomFactor+ privateProperties.currentZoomFactor*(pinch.scale-1);
                pinchCenter =pinch.center;
                privateProperties.zoom(privateProperties.getBoundedScaleFactor(newZoomFactor))
            }

            onPinchFinished:
            {
                privateProperties.currentZoomFactor +=  privateProperties.currentZoomFactor*(pinch.scale-1);
                privateProperties.currentZoomFactor = privateProperties.getBoundedScaleFactor(privateProperties.currentZoomFactor);
                zoomTriggeredFromPinchArea=false;
            }

            MouseArea
            {
                id:mouseArea
                anchors.fill: parent
                propagateComposedEvents :true
                scrollGestureEnabled: false
                hoverEnabled: true

                onDoubleClicked:
                {
                    if(privateProperties.currentZoomFactor>1)resetScale();
                    else
                    {
                        var widthScale = (flickable.width+20)/mImage.width;
                        var heightScale = (flickable.height+20)/mImage.height;
                        var maxScale = Math.max(widthScale,heightScale);
                        if(maxScale>1)
                        {
                            privateProperties.pointOfDoubleClick = Qt.point(mouseX,mouseY);
                            privateProperties.useDoubleClickPoint = true;
                            privateProperties.currentZoomFactor = maxScale;
                        }
                    }
                }

                onWheel:
                {
                    if(wheel.modifiers===Qt.ControlModifier)
                    {
                        wheel.accepted=true;
                        var newZoomFactor;
                        if(wheel.angleDelta.y>0)
                            newZoomFactor = privateProperties.currentZoomFactor + (privateProperties.currentZoomFactor*privateProperties.zoomStepFactor);
                        else  newZoomFactor = privateProperties.currentZoomFactor - (privateProperties.currentZoomFactor*privateProperties.zoomStepFactor);
                        privateProperties.currentZoomFactor = privateProperties.getBoundedScaleFactor(newZoomFactor);
                        return;
                    }
                    wheel.accepted=false;
                }
            }
        }
    }

    QtObject
    {
        id : privateProperties
        property bool useDoubleClickPoint:false;
        property point pointOfDoubleClick;
        property real maxZoomFactor : 30.0
        property real zoomStepFactor :0.3;
        property real currentZoomFactor: 1
        property real minZoomFactor :1;
        property point scaleCenter : pinchArea.zoomTriggeredFromPinchArea
                                     ? pinchArea.pinchCenter : Qt.point(mouseArea.mouseX,mouseArea.mouseY);
        Behavior on currentZoomFactor {
            NumberAnimation { id:scaleNumberAnimation
                duration: pinchArea.zoomTriggeredFromPinchArea ? 0 : privateProperties.useDoubleClickPoint ?
                                                 Math.min(200*privateProperties.currentZoomFactor,500) : 200 ;
                onRunningChanged:  if(!running) privateProperties.useDoubleClickPoint=false;
            }
        }

        onCurrentZoomFactorChanged:
        {
            if(!pinchArea.zoomTriggeredFromPinchArea)
                zoom(currentZoomFactor);
        }

        function zoom(scaleFactor)
        {
            var targetWidth =  imageWidth*scaleFactor;
            var targetHeight  = imageHeight*scaleFactor;
            if(useDoubleClickPoint) resizeContent(targetWidth,targetHeight,mapToItem(mImage,pointOfDoubleClick));
            else resizeContent(targetWidth,targetHeight,scaleCenter);
            returnToBounds();
        }

        function getBoundedScaleFactor(ScaleFactor)
        {
            if(ScaleFactor>maxZoomFactor)ScaleFactor = maxZoomFactor;
            else if(ScaleFactor<minZoomFactor)ScaleFactor = minZoomFactor;
            return ScaleFactor;
        }
    }

    function resetScale()
    {
        privateProperties.pointOfDoubleClick = Qt.point(0,0);
        privateProperties.useDoubleClickPoint = true;
        privateProperties.currentZoomFactor = 1;
    }

}

0

使用比例变换似乎是更好的解决方案:

import QtQml 2.11
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.11
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.4
import QtCharts 2.2
import QtGraphicalEffects 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    property int scaleX: 1;

    Rectangle {
        anchors.fill: parent
        color: "green"

        Flickable {
            id: flickArea
            anchors {fill: parent; margins: 10; }
            contentWidth: rect.width*rect.scale
            contentHeight: rect.height*rect.scale
            transform: Scale { origin.x: 0; origin.y: 240; xScale: scaleX}
            Rectangle {
                id: rect
                transformOrigin: Item.TopLeft
                x: 0; y: 0;
                width: 200; height: 300;
                color: "lightgrey"

                Rectangle {
                    id: inner
                    anchors { fill: parent; margins: 10; }
                    color: "red"
                }
            }
        }
    }
    Button {
        anchors.bottom: parent.bottom;
        anchors.horizontalCenter: parent.horizontalCenter;
        text: "Scale flickArea"
        onClicked: {
//            flickArea.scale += 0.2;
            scaleX += 1;
            console.log(flickArea.contentWidth);
            console.log(flickArea.scale)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接