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.
-
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 } } }
-
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 } } }
@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.
-
Haha, thanks. Happy coding :-)
-
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(); }
@sierdzio Hi, I am running into a similar problem, but I can't figure out how to get the correct zoom point.
I have a ListView and I want to zoom in and out on it. The zooming itself works fine, but it is not zooming into the middle of the listview, but scrolls up while zooming (https://streamable.com/o1frzj).I am using code which is very similar to yours:
function zoom(factor) { let newWidth = listView.contentWidth * factor; // Copied from your response var zoomPoint = Qt.point(listView.width/2 + listView.contentX, listView.height/2 + listView.contentY); listView.resizeContent(Math.round(newWidth), Math.round(newWidth / listView.currentItem.pageRatio), zoomPoint); listView.returnToBounds(); }
Do you have an idea why it doesn't scroll into the middle of the screen?
Thanks in advance