Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Zooming/Scaling an Image at a specific point within a Flickable



  • By moving a Slider, I want to be able to scale (zoom in) on an Image and once scaled (zoomed in), be able to move around, ( flick ) I have this example working here below, but my problem is the following.

    If I zoom in, scale in, and then flick around to a spot on the image, then I want to zoom (scale) in further on that specific point in the center of the window, when I scale in using the Slider, the zoom (scale) occurs at the center of the image because by default the 'transformOrigin' is Item.Center

    Try running the code to understand what I am talking about: ( you will need to change the image source)

    import QtQuick 2.12
    import QtQuick.Controls 2.3
    
    
    Item {
        id: mainId
        width: 1280
        height: 720
        property int scaleMultiplier: 3
    
    
        Flickable {
            id: flickArea
            anchors.fill: parent
            focus: true
            contentWidth: Math.max(inner.width * slider.value * scaleMultiplier, width)
            contentHeight: Math.max(inner.height  * slider.value * scaleMultiplier, height)
            anchors.centerIn: parent
            boundsBehavior: Flickable.StopAtBounds
            contentX: contentWidth === width ? 0 : inner.width * slider.value * scaleMultiplier / 2 - flickArea.width / 2
            contentY: contentHeight === height ? 0 : inner.height * slider.value * scaleMultiplier / 2 - flickArea.height / 2
    
    
            Image {
                id: inner
                scale: slider.value * scaleMultiplier
                anchors.centerIn: parent
                source: "test_images/1.png"
            }
        }
    
        Slider {
            id: slider
            value: .01
            orientation: Qt.Vertical
            anchors {
                bottom: parent.bottom
                right: parent.right
                top: parent.top
                margins: 50
            }
    
            from: 0.01
        }
    
    }
    

    I tried setting an arbitrary transform origin point like so:

    Image {
                id: inner
                scale: slider.value * scaleMultiplier
                anchors.centerIn: parent
    
                source: "test_images/1.png"
    
                transform: Scale {
                                    id: scaleID ;
                                    origin.x: flickArea.contentX + flickArea.width * flickArea.visibleArea.widthRatio / 2
                                    origin.y: flickArea.contentY + flickArea.height * flickArea.visibleArea.heightRatio / 2
                }
            }
    
    

    But it seems to have no effect. Any help is appreciated. What am I missing?

    Again what I want is when I flick to a specific point on the Image, and scale in, I want the point of origin to scale into be the area in the center of the viewable flickarea. For example let's say I am zoomed in the upper right corner of the image, then I wish to zoom in further, when I zoom in further, the Image is brought back to the center of the image.

    Thanks.


  • Moderators

    You need to use resizeContents() method from Flickable. And example: https://github.com/sierdzio/closecombatfree/blob/master/qml/scenarios/Scenario.qml#L43

    So in your case it'll look like this:

    property real zoom: 1;
    onZoomChanged: {
            var zoomPoint = Qt.point(flickArea.width/2 + flickArea.contentX,
                                 flickArea.height/2 + flickArea.contentY);
    
            flickArea.resizeContent((inner.width * zoom), (inner.height * zoom), zoomPoint);
            flickArea.returnToBounds();
        }
    


  • I sincerely appreciate your reply @sierdzio , I am trying to understand your response.

    What drives the 'zoom' property, do I bind it to the slider.value? I tried many variations but the image is still zooming at the center instead of where I am currently viewing the the Flickable

    import QtQuick 2.12
    import QtQuick.Controls 2.3
    
    
    Item {
        id: mainId
        width: 1280
        height: 720
        property int scaleMultiplier: 3
        property real zoom
    
    
        onZoomChanged: {
                var zoomPoint = Qt.point(flickArea.width/2 + flickArea.contentX,
                                     flickArea.height/2 + flickArea.contentY);
    
                flickArea.resizeContent((inner.width * zoom), (inner.height * zoom), zoomPoint);
                flickArea.returnToBounds();
            }
    
    
        Flickable {
            id: flickArea
            anchors.fill: parent
            focus: true
            contentWidth: Math.max(inner.width * slider.value * scaleMultiplier, width)
            contentHeight: Math.max(inner.height  * slider.value * scaleMultiplier, height)
            anchors.centerIn: parent
            boundsBehavior: Flickable.StopAtBounds
            contentX: contentWidth === width ? 0 : inner.width * slider.value * scaleMultiplier / 2 - flickArea.width / 2
            contentY: contentHeight === height ? 0 : inner.height * slider.value * scaleMultiplier / 2 - flickArea.height / 2
    
            Image {
                id: inner
                //scale: slider.value * scaleMultiplier
                scale: zoom
                anchors.centerIn: parent
    
                source: "test_images/1.png"
    
                transform: Scale {
                                    id: scaleID ;
                                    origin.x: flickArea.contentX + flickArea.width * flickArea.visibleArea.widthRatio / 2
                                    origin.y: flickArea.contentY + flickArea.height * flickArea.visibleArea.heightRatio / 2
                }
            }
        }
    
        Slider {
            id: slider
            value: .01
            orientation: Qt.Vertical
            anchors {
                bottom: parent.bottom
                right: parent.right
                top: parent.top
                margins: 50
            }
    
            from: 0.01
    
            onValueChanged: {
                zoom = value * scaleMultiplier
            }
        }
    }
    

  • Moderators

    @Edwin-F. said in Zooming/Scaling an Image at a specific point within a Flickable:

    What drives the 'zoom' property, do I bind it to the slider.value?

    Yes, in your case you can use slider's value, either by binding or remove zoom property and use only the slider's property.

    Try this:

    // REMOVE THESE LINES:
    contentWidth: Math.max(inner.width * slider.value * scaleMultiplier, width)
    contentHeight: Math.max(inner.height  * slider.value * scaleMultiplier, height)
    contentX: contentWidth === width ? 0 : inner.width * slider.value * scaleMultiplier / 2 - flickArea.width / 2
    contentY: contentHeight === height ? 0 : inner.height * slider.value * scaleMultiplier / 2 - flickArea.height / 2
    
    // Instead, do:
    contentWidth: inner.width
    contentHeight: inner.height
    

    I suspect these lines are inhibiting my zoom slot.



  • This works beautifully, THANK YOU! You're the man. If you were here I'd hug you and buy you a beer.


  • Moderators

    Haha, thanks. Happy coding :-)


Log in to reply