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
            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     // Tried to change this, no worky
            }
        }
    }
}

示例运行:我将显示 100、200% 左上角和右下角,以显示剪切和空白区域:

请帮助我改进我的代码,它一定有几个问题,因为它的行为非常奇怪。

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

qt qml zooming
1个回答
0
投票

代码中的问题是您不应该使用

fillMode: Image.PreserveAspectFit
,这会导致图像周围有空白区域,并且您应该将图像的
width
设置为
contentWidth
而不是
paintedWidth
。另外,您应该保留
transformOrigin: Item.TopLeft

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

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.