QML,在缩放到 ListView 时保持位置

huangapple go评论63阅读模式
英文:

QML, Keeping the position while zooming into a ListView

问题

我卡在一个关于多天的问题上,关于ListView的缩放问题。我希望这里有人能提供解决方案或想法。

我有一个ListView(带有id pageView),它具有图像模型。ListView有一个defaultPageHeight属性,表示每个图像的默认高度。它还有一个zoomFactor属性,默认初始化为1
每个代理的height设置为:

height: Math.round(pageView.defaultPageHeight * pageView.zoomFactor)

我已经重写了我的ListViewonWheel事件,以便我可以手动处理它。当我按下CTRL+滚轮时,会调用zoom(factor)函数,其中factor参数表示缩放量。

缩放函数如下:

function zoom(factor)
{
    let newZoomFactor = pageView.zoomFactor * factor;
    let newPageHeight = Math.round(pageView.defaultPageHeight * newZoomFactor);
    
    // 确保在缩放之前存储列表视图所看位置
    let currentPageHeight = Math.round(pageView.defaultPageHeight * pageView.zoomFactor);
    let currentPageNumber = /*获取当前页码*/;
    let currentPos = pageView.contentY - pageView.originY;
    
    // 获取偏移量,即位置距离当前页面顶部有多远
    let pageOffset = currentPos - (currentPageHeight * currentPageNumber);
    
    // 应用新的缩放
    pageView.zoomFactor = newZoomFactor;

    // 设置新的contentY
    pageView.contentY = newPageHeight * currentPageNumber + Math.round(pageOffset * newZoomFactor) + pageView.originY;
}

缩放本身工作正常,但我无法恢复缩放之前ListView的位置。所以当我缩放时,出现了在列表视图中上下移动的情况。

您可以在以下示例中查看此情况:https://imgur.com/a/cO4cAxq
正如您所看到的,ListView成功缩放,但在缩放时也会改变其位置。我希望它在缩放时能够缩放到屏幕中间,而不是在缩放时在文档中移动。

我已经设置了一个简单的、最小的副本,以便您可以在本地进行测试。它看起来是这样的:https://imgur.com/a/acUmjjx
再次看到,它成功缩放,但在缩放时会在ListView中移动,而不是缩放到一个点。以下是最小副本的代码:https://gist.github.com/DavidLazarescu/337d8b28e941cdbe6db3d4873ae45fd3

提前感谢您的任何帮助。

英文:

I am stuck on an issue regarding zooming into a ListView for multiple days now. I hope someone here might have a solution or an idea.

I have a ListView (with the id pageView) that has a model of images. The ListView has a property defaultPageHeight which indicates the default height of each image. It also has a zoomFactor property, that is default initialized to 1.
The height of each delegate is set to:

height: Math.round(pageView.defaultPageHeight * pageView.zoomFactor)

I have overwritten the onWheel event of my ListView so that I can handle it manually. When I press CTRL+Scroll the zoom(factor) function is called, with a factor parameter that indicates how much to zoom.

The zooms function looks like this:

function zoom(factor)
{
    let newZoomFactor = pageView.zoomFactor * factor;
    let newPageHeight = Math.round(pageView.defaultPageHeight * newZoomFactor);
    
    // Make sure to store the position the listview was looking at before zooming
    let currentPageHeight = Math.round(pageView.defaultPageHeight * pageView.zoomFactor);
    let currentPageNumber = /*Gets the current page*/;
    let currentPos = pageView.contentY - pageView.originY;
    
    // Get the offset, meaning how far is the position away from the current page's top
    let pageOffset = currentPos - (currentPageHeight * currentPageNumber);
    
    // Apply the new zoom
    pageView.zoomFactor = newZoomFactor;

    // Set the new contentY
    pageView.contentY = newPageHeight * currentPageNumber + Math.round(pageOffset * newZoomFactor) + pageView.originY;
}

The zooming itself works fine, but I am not able to restore the position of the ListView before the zoom. So when I am zooming, I am also moving up or down in the listview for some reason.

You can see an example oft this in the following: https://imgur.com/a/cO4cAxq
As you can see, the ListView is successfully zooming, but it is changing its position. I'd like it to zoom into the middle of the screen, instead of moving around the document while zooming.

I have set up a simple, minimal replica of this so that you can test it out locally. This is how it looks: https://imgur.com/a/acUmjjx
You again see that it is zooming successfully, but moving across the ListView instead of zooming onto one point. Here is the code of the minimal replica: https://gist.github.com/DavidLazarescu/337d8b28e941cdbe6db3d4873ae45fd3

Thank you for any help in advance

答案1

得分: 0

我进行了重新编写,并发现 ListView 和 GridView 做了类似的事情,但我觉得 GridView 可能更好,因为它预先警告了所有子单元格。

我发现的另一件事是,需要使用 contentY 和 originY 来确定滚动位置。如果只使用 contentY,就会出现难以解释的漂移,最终会导致缩放出现问题。简单来说,方程大致如下:

currentIndex = (currentY - originY) / cellHeight

currentY = currentIndex * cellHeight + originY

没有这些,你可能会尝试假设 originY === 0,并且在缩放时出现一些问题。

以下是我的 GridView 实现:

import QtQuick
import QtQuick.Controls
Page {
    GridView {
        id: pageView
        property int defaultCellHeight: 400
        cellHeight: defaultCellHeight
        cellWidth: Math.round(cellHeight * 0.7)
        height: parent.height
        width: cellWidth
        anchors.centerIn: parent
        clip: true
        model: 500
        delegate: Rectangle
        {
            height: pageView.cellHeight
            width: pageView.cellWidth
            color: modelData % 2 == 0 ? "gray" : "green"
            border.color: GridView.isCurrentItem ? "black" : "transparent"
            border.width: 5
            Label
            {
                anchors.centerIn: parent
                font.bold: true
                font.pointSize: 20 * (pageView.cellHeight / 400)
                text: modelData
            }
        }
        WheelHandler {
            acceptedModifiers: Qt.ShiftModifier
            onWheel: event => {
                let currentIndex = (pageView.contentY - pageView.originY + pageView.height / 2) / pageView.cellHeight - 1/2;
                let newCellHeight = Math.round(pageView.cellHeight * Math.pow(1.001, event.pixelDelta.y));
                pageView.cellHeight = newCellHeight;
                pageView.contentY = (currentIndex + 1 / 2) * newCellHeight - pageView.height / 2 + pageView.originY;
                event.accepted = true;
            }
        }
    }
}

你可以在 此处在线尝试!

英文:

[REWRITE]

I had a reattempt and found that ListView and GridView do a similar thing, but, feel that GridView is possibly better because it forewarns what all the children cells will be.

The other thing I found is that contentY AND originY is needed to determine the scroll position. If you use contentY alone, there will be an unaccountable drifting that occurs that eventually puts your zooming out of what. To understand it simply, the equation is approximately:

currentIndex = (currentY - originY) / cellHeight

and

currentY = currentIndex * cellHeight + originY

Without it, you may try to assume originY === 0 and get some creeping in zooming problems.

Here's my implementation with GridView:

import QtQuick
import QtQuick.Controls
Page {
    GridView {
        id: pageView
        property int defaultCellHeight: 400
        cellHeight: defaultCellHeight
        cellWidth: Math.round(cellHeight * 0.7)
        height: parent.height
        width: cellWidth
        anchors.centerIn: parent
        clip: true
        model: 500
        delegate: Rectangle
        {
            height: pageView.cellHeight
            width: pageView.cellWidth
            color: modelData % 2 == 0 ? "gray" : "green"
            border.color: GridView.isCurrentItem ? "black" : "transparent"
            border.width: 5
            Label
            {
                anchors.centerIn: parent
                font.bold: true
                font.pointSize: 20 * (pageView.cellHeight / 400)
                text: modelData
            }
        }
        WheelHandler {
            acceptedModifiers: Qt.ShiftModifier
            onWheel: event => {
                let currentIndex = (pageView.contentY - pageView.originY + pageView.height / 2) / pageView.cellHeight - 1/2;
                let newCellHeight = Math.round(pageView.cellHeight * Math.pow(1.001, event.pixelDelta.y));
                pageView.cellHeight = newCellHeight;
                pageView.contentY = (currentIndex + 1 / 2) * newCellHeight - pageView.height / 2 + pageView.originY;
                event.accepted = true;
            }
        }
    }
}

You can Try it Online!

答案2

得分: 0

在你的onWheel事件(positionViewatIndex)中,你可以尝试这样修改:

onWheel: {event => {
    let currentIndex = (pageView.contentY - pageView.originY + pageView.height / 2) / pageView.cellHeight - 1/2;
    pageView.positionViewatIndex(currentIndex,ListView.Beginning)
}}
英文:

You could try this modification to your onWheel event (positionViewatIndex)

onWheel: {event => {
let currentIndex = (pageView.contentY - pageView.originY + pageView.height / 2) / pageView.cellHeight - 1/2;
pageView.positionViewatIndex(currentIndex,ListView.Beginning)}

答案3

得分: 0

似乎出现了与originY相关的问题。在记录了一些缩放数据后,我注意到设置pageView.zoomFactor = newZoomFactor;并没有导致originY刷新,所以我将代码更改为:

pageView.contentY = newPageHeight * currentPageNumber + Math.round(pageOffset * newZoomFactor) + pageView.originY;

而此时originY仍然是旧的originY,因此我移动到了实际位置应该在originY的位置上方。
为了在计算新位置之前刷新originY,我需要调用pageView.forceLayout();以便我可以使用更新后的originY来计算实际位置。

编辑:我已经更新了代码以包含修复。

英文:

It seems to have been a problem with originY. After recording some data of zooming I noticed that that setting pageView.zoomFactor = newZoomFactor; did not cause originY to refresh, so I was setting:

pageView.contentY = newPageHeight * currentPageNumber + Math.round(pageOffset * newZoomFactor) + pageView.originY;

while originY was the old originY and so I moved to a position that was originY away from where it should actually be.
To refresh originY before I was calculating the new position, I needed to call pageView.forceLayout(); so that I can use the updated originY to calculate the actual position.

EDIT: I have updated the gist to contain the fix.

huangapple
  • 本文由 发表于 2023年6月26日 06:20:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76552632.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定