0

In the application below, you can click anywhere in the scene to centre the "view" over that point.

import QtQuick 2.7
import QtQuick.Controls 2.2

ApplicationWindow {
    width: 640
    height: 480
    visible: true

    property real zoom: 1
    property point offset
    property point scenePosToCentreOn

    Item {
        id: sceneView
        anchors.fill: parent

        MouseArea {
            anchors.fill: parent
            onClicked: {
                scenePosToCentreOn = scene.mapFromItem(sceneView, mouse.x, mouse.y)
                offset = Qt.point(scenePosToCentreOn.x - width / 2,
                                  scenePosToCentreOn.y - height / 2);
            }
            onWheel: {
                var zoomFactor = Math.pow(1.4, wheel.angleDelta.y / 120.0);
                var newZoom = Math.min(8.0, Math.max(0.25, zoom * zoomFactor));
                zoom = newZoom;
            }
        }

        Item {
            id: scene
            implicitWidth: backgroundImage.implicitWidth
            implicitHeight: backgroundImage.implicitHeight

            transform: [
                Translate {
                    x: -offset.x
                    y: -offset.y
                },
                Scale {
                    xScale: zoom
                    yScale: zoom
                }
            ]

            Image {
                id: backgroundImage
                source: "http://cdn.akamai.steamstatic.com/steam/apps/393010/ss_29cf93db42617dd08ceb0a0bf0a4b62ad12a1cfc.1920x1080.jpg?t=1459456906"
            }

            Rectangle {
                x: scenePosToCentreOn.x - width / 2
                y: scenePosToCentreOn.y - height / 2
                width: 8
                height: width
                radius: width / 2
                color: "#fff"
            }

            Rectangle {
                anchors.fill: parent
                color: "transparent"
                border.color: "darkorange"
                border.width: 4

                Label {
                    text: "Scene"
                }
            }
        }

        Label {
            text: zoom.toFixed(2)
            font.pixelSize: Qt.application.font.pixelSize * 2
            color: "salmon"
            anchors.right: parent.right
            anchors.bottom: parent.bottom
        }
    }
}

I'd like to be able to zoom in and out on the point that is selected (scenePosToCentreOn). It currently works when zoom is 1, but seems to have its origin at the top left of the screen for any other zoom value. I suspect I'm missing something from the list of transforms, but I can't figure it out.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • The tags are relevant, and please don't change my British English. :) I'll take a look at that answer to see if it helps. – Mitch Jan 24 '18 at 22:37
  • Why camera is relevant? – eyllanesc Jan 24 '18 at 22:38
  • The view is a camera onto the scene. The original use case is a game, so I'd like to attract people who have experience with that. – Mitch Jan 24 '18 at 22:39
  • Conceptually yes, but this element is not appropriate in this case, it would be best to use a more general tag like `qt`, so you will attract more people who can help you. – eyllanesc Jan 24 '18 at 22:41
  • Ok, fair point. On the subject of helping, I don't see how the answer you voted to close my question as a duplicate of solves my problem. Perhaps you'd care to post an answer with that code adapted to my snippet? :) – Mitch Jan 24 '18 at 22:44
  • One of the errors that I see in your code is that you define the transformations with respect to an item of size zero and position 0,0. Transformations are always relative to transformOrigin. and by default it is the center of the item, http://doc.qt.io/qt-5/qml-qtquick-item.html#transformOrigin-prop. the solution that this thinking would involve at least that change. Then I wonder if it bothers you, do you only want that when you click on some point of the image it is centered on it, and when you zoom in you want to keep the center? Why is your scene size 0? – eyllanesc Jan 24 '18 at 23:17
  • see [Zooming graphics based on current mouse position](https://stackoverflow.com/a/37269366/2521214) and [Zoom in and out and keep current pixel at mouse coordinates](https://stackoverflow.com/a/42872752/2521214) – Spektre Jan 25 '18 at 06:45
  • @eyllanesc, "Why is your scene size 0" is a very good question. :D In my actual use case it's not, but I wrote the snippet up quickly and forgot to give it an implicit size... edited it now. – Mitch Jan 25 '18 at 08:02

1 Answers1

3

You need to compensate because of the way you calculate the centering position and apply the scale.

    Translate {
      x: -offset.x + (((1 / zoom) - 1) * (sceneView.width * .5))
      y: -offset.y + (((1 / zoom) - 1) * (sceneView.height * .5))
    },
dtech
  • 47,916
  • 17
  • 112
  • 190
  • Thanks! What does the ((z / zoom) - 1) part do? – Mitch Jan 25 '18 at 08:03
  • 1
    Since you apply the transformation and the scale to the same item, your zoom diminishes the offset if lower than 1 and exaggerates the offset if higher than 1. At scale 0.5 your offset only goes 50% of the way it has to, at scale 2 it goes 200%. The part in question simply calculates the zoom ratio `1 / zoom` and then subtracts 1 to "normalize" it to zero so you have 0 compensation for zoom of 1. So you end up with a value of between -1 and x that gives you by how much pixels your center point drifts away from the scene center due to scaling. – dtech Jan 25 '18 at 10:00
  • Also, I would probably go for a "relative" center point rather than absolute, then calculate the absolute from the relative and window size, so that the absolute center point changes when the window size changes, so you can keep things centered as the window size changes too, something you currently don't have. – dtech Jan 25 '18 at 10:01
  • Ah, thanks! That makes sense. As for the relative center point thing, do you want to edit that into your answer? It sounds good but I'm not sure how to do it. – Mitch Jan 25 '18 at 10:05