Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. ZoomAndDragable QML
Qt 6.11 is out! See what's new in the release blog

ZoomAndDragable QML

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
1 Posts 1 Posters 173 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    Redman
    wrote on last edited by Redman
    #1

    Hi,

    a friend of mine provided me with this code which works really good
    ZoomAndDragable.qml

    import QtQuick 2.15
    
    Flickable {
        id: flickable
        property int itemWidth
        property int itemHeight
        default property Component sourceComponent
        contentWidth: itemWidth
        contentHeight: itemHeight
    
        boundsBehavior: Flickable.StopAtBounds
        boundsMovement: Flickable.StopAtBounds
        clip: true
    
        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)
                AnchorChanges {
                    target: flickable.contentItem
                    anchors.horizontalCenter: width
                                              < flickable.width ? flickable.horizontalCenter : undefined
                    anchors.verticalCenter: height
                                            < flickable.height ? flickable.verticalCenter : undefined
                }
            }
        ]
        onStateChanged: {
            cancelFlick()
            returnToBounds()
        }
    
        Rectangle {
            id: mItem
            color: "yellow"
            width: flickable.contentWidth
            height: flickable.contentHeight
            Component.onCompleted: {
                itemWidth = contentLoader.implicitWidth
                itemHeight = contentLoader.implicitHeight
            }
    
            Loader {
                id: contentLoader
                sourceComponent: flickable.sourceComponent
                transform: [
                    Scale {
                        xScale: pinchArea.pinchActive ? pinchArea.scaleFactorBeforePinchReleased : privateProperties.currentZoomFactor
                        yScale: pinchArea.pinchActive ? pinchArea.scaleFactorBeforePinchReleased : privateProperties.currentZoomFactor
                    }
                ]
            }
    
            PinchArea {
                id: pinchArea
                anchors.fill: parent
                property bool zoomTriggeredFromPinchArea: false
                property point pinchCenter
                property bool pinchActive: false
                property real scaleFactorBeforePinchReleased: privateProperties.currentZoomFactor
    
                onPinchStarted: zoomTriggeredFromPinchArea = true
                onPinchUpdated: function (pinch) {
                    pinchActive = true
                    var newZoomFactor = privateProperties.currentZoomFactor
                            + privateProperties.currentZoomFactor * (pinch.scale - 1)
                    pinchCenter = pinch.center
                    let boundedScaleFactor = privateProperties.getBoundedScaleFactor(
                            newZoomFactor)
                    scaleFactorBeforePinchReleased = boundedScaleFactor
                    privateProperties.zoom(boundedScaleFactor)
                }
    
                onPinchFinished: function (pinch) {
                    privateProperties.currentZoomFactor
                            += privateProperties.currentZoomFactor * (pinch.scale - 1)
                    privateProperties.currentZoomFactor = privateProperties.getBoundedScaleFactor(
                                privateProperties.currentZoomFactor)
                    zoomTriggeredFromPinchArea = false
                    scaleFactorBeforePinchReleased = privateProperties.currentZoomFactor
                    pinchActive = 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) / mItem.width
                            var heightScale = (flickable.height + 20) / mItem.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
            // adjust according to your need
            property real maxZoomFactor: 2.5
            property real zoomStepFactor: 0.3
            property real minZoomFactor: 0.7
            // do not modify
            property bool useDoubleClickPoint: false
            property point pointOfDoubleClick
            property real currentZoomFactor: 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 = itemWidth * scaleFactor
                var targetHeight = itemHeight * scaleFactor
                if (useDoubleClickPoint)
                    resizeContent(targetWidth, targetHeight,
                                  mapToItem(mItem, 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
        }
    }
    
    

    You can pass a sourceComponent to ZoomAndDragable and it will get loaded.
    My sourceComponent is a x-stretched rectangle.
    When I zoom in the state at the top of the file works good, until anchors.horizontalCenter will be set to undefined. Then it kinda snaps to a fixed position and disturbs the zoom feeling.

    I tried to fix it but I couldnt manage it.

    Can anyone suggest to me how to resolve this?

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved