Qml 图像内可滑动缩放移位(和剪辑)

问题描述 投票:0回答:1

我正在尝试向我的图像添加缩放控件。我尝试从尽可能简单的开始,但我尝试的任何操作最终都会剪切图像。

图像已加载到屏幕的一部分中,我想将其放置在

Flickable
中,以便能够缩放和平移。稍后我将添加捏合和鼠标滚轮,现在我有按钮来设置各种比例值。

问题:缩放时,图像会移动并被剪切。这种转变很麻烦,因为缩放后的图像比容器大,为什么要显示空白空间?但剪辑更糟糕,我不明白。看起来偏移与剪辑一样大(也许?)。

我尝试改变

transformOrigin
,不好。我尝试使用
transform
而不是
scale
,但我没有看到任何反应。

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.0
import QtQuick.Window 2.2

Window {
    width: 1015; height: 550; visible: true
    Item {      // A bunch of buttons to easily test several zoom values and layout is similar
        id: leftSideContainer; width: 100; height: parent.height
        ColumnLayout {
            anchors.fill: parent; Layout.alignment: Qt.AlignCenter;
            Layout.preferredHeight: 40; Layout.fillWidth: true;
            Button { text: "50 %"; onClicked: imageContainer.imageScale = 0.5; }
            Button { text: "80 %"; onClicked: imageContainer.imageScale = 0.8; }
            Button { text: "100 %"; onClicked: imageContainer.imageScale = 1; }
            Button { text: "110 %"; onClicked: imageContainer.imageScale = 1.1; }
            Button { text: "120 %"; onClicked: imageContainer.imageScale = 1.2; }
            Button { text: "150 %"; onClicked: imageContainer.imageScale = 1.5; }
            Button { text: "200 %"; onClicked: imageContainer.imageScale = 2; }
            Button { text: "400 %"; onClicked: imageContainer.imageScale = 4; }
            Button { text: "landscape"; onClicked: { paintedCanvasImage.source = ""; paintedCanvasImage.source = "/examples/landscape.jpg"; } }
            Button { text: "portrait"; onClicked: { paintedCanvasImage.source = ""; paintedCanvasImage.source = "/examples/portrait.jpg"; } }
            Button { text: "square"; onClicked: { paintedCanvasImage.source = ""; paintedCanvasImage.source = "/examples/square.jpg"; } }
        }
    }
    Item {  // Using separate variable and item a bit like my code
        id: imageContainer; property var imageScale: 1;
        anchors { left: leftSideContainer.right; right: parent.right;
                  top: parent.top; bottom: parent.bottom; }
        Rectangle {  // Easy to show background compared to actual image, no other use
            anchors.fill: parent; color: "lightgreen"; }

        onImageScaleChanged: paintedCanvasImage.scale = imageScale;

        Flickable { // I want to pan
            id: flickImage
            anchors.fill: parent

            contentWidth: paintedCanvasImage.paintedWidth * paintedCanvasImage.scale
            contentHeight: paintedCanvasImage.paintedHeight * paintedCanvasImage.scale
            //contentX: ??? I have no idea
            clip: true   // required
            boundsBehavior: Flickable.StopAtBounds
            ScrollBar.vertical: ScrollBar { active: true; policy: ScrollBar.AsNeeded; }
            ScrollBar.horizontal: ScrollBar { active: true; policy: ScrollBar.AsNeeded; }

            Image {
                id: paintedCanvasImage
                width: imageContainer.width; height: imageContainer.height
                source: "/examples/landscape.jpg"  // Trying square, landscape or portrait
                fillMode: Image.PreserveAspectFit   // required
                transformOrigin: Item.TopLeft       // With this, weirdly the vertical fits correctly, horizontal only shifted/clipped
            }
        }
    }
}

示例运行:我将在左上角和右下角显示 100 和 200%,以显示剪切和空白区域。请注意,使用

transformOrigin: Item.TopLeft
时,图像垂直放置(如果容器比图像更横向,否则水平放置) - 但向右移动了一些我不知道的量:

我不知道

transformOrigin
应该是什么,或者如何使用
transform
代替
scale
,或者 flickable 的起源必须是什么。
更改
contentX
我以为可以修复它,但事实并非如此 - 必须需要一些有关转换的东西。

平台:windows以及嵌入式linux;
Qt版本:5.12

qt qml zooming
1个回答
0
投票

您的代码中有一些错误导致了该问题。 首先,裁剪问题是由于缺少

transformOrigin: Item.TopLeft
,这会导致默认情况下原点位于中心,并使图像的 x 和 y 比例为负值。
移动问题是由
paintedWidth
值引起的,根据 文档,该值可能小于图像项。
此外,如果其比例不适合您的
fillMode: Image.PreserveAspectFit
,使用
container
总会导致图像周围出现空白区域。

对于解决方案,我做了一些更改,以便您可以轻松使用滚轮或捏合处理程序。

在下面的代码中,我添加了一个

fitSize
属性,它存储图像的初始大小,并且还使用图像的
sourceSize
计算长宽比。我还删除了图像的缩放部分以便能够使用
flickable.resizeContent

通过将

fitSize
iscale
相乘,您可以获得新的尺寸。

这可能不是最佳实践,但效果很好,我认为您也可以使用

PinchHandler
代替
WheelHandler

Control {
    id: container

    property real iscale: 1
    property size fitSize: {
        const ss = image.sourceSize;
        const ratio = ss.width / ss.height;
        return ratio < 1 ? Qt.size(height * ratio, height) : Qt.size(width, width / ratio);
    }
    
    contentItem: Flickable {
        id: flickable
    
        clip: true
    
        leftMargin: Math.max(0, width - contentWidth) / 2 // Centering the content
        topMargin: Math.max(0, height - contentHeight) / 2
    
        contentWidth: container.fitSize.width
        contentHeight: container.fitSize.height
    
        rebound: Transition {}
        boundsBehavior: Flickable.StopAtBounds
        ScrollBar.vertical: ScrollBar {}
        ScrollBar.horizontal: ScrollBar {}
    
        Image {
            id: image
    
            width: flickable.contentWidth
            height: flickable.contentHeight
            source: "win11.jpg"
            onSourceChanged: container.iscale = 1
    
            WheelHandler {
                onWheel: function(e) {
                    const { width, height } = container.fitSize;
    
                    container.iscale += e.angleDelta.y / 1200;
                    flickable.resizeContent(width * container.iscale, height * container.iscale, point.position);
                    flickable.returnToBounds();
                }
            }
        }
    }
    
    background: Rectangle { color: "#121314" }
}
© www.soinside.com 2019 - 2024. All rights reserved.