英文:
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)
我已经重写了我的ListView
的onWheel
事件,以便我可以手动处理它。当我按下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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论