计算在鼠标光标位置放大的视图偏移

2024-03-22

我有一个“画布”,用户可以在上面绘制像素等。它运行良好,但我的缩放功能当前使用相同的原点,无论鼠标的位置如何。我想实现类似 Google 地图缩放行为的功能:

也就是说,缩放的原点应始终是鼠标光标的位置。

我目前拥有的 https://i.stack.imgur.com/usa28.gif不完全正确...

我的尝试主要是在黑暗中进行尝试,但我也尝试使用来自这个答案 https://stackoverflow.com/a/14085161/904422没有成功。

main.cpp:

#include <QGuiApplication>
#include <QtQuick>

class Canvas : public QQuickPaintedItem
{
    Q_OBJECT

public:
    Canvas() :
        mTileWidth(25),
        mTileHeight(25),
        mTilesAcross(10),
        mTilesDown(10),
        mOffset(QPoint(400, 400)),
        mZoomLevel(1)
    {
    }

    void paint(QPainter *painter) override {
        painter->translate(mOffset);

        const int zoomedTileWidth =  mTilesAcross * mZoomLevel;
        const int zoomedTileHeight =  mTilesDown * mZoomLevel;
        const int zoomedMapWidth = qMin(mTilesAcross * zoomedTileWidth, qFloor(width()));
        const int zoomedMapHeight = qMin(mTilesDown * zoomedTileHeight, qFloor(height()));
        painter->fillRect(0, 0, zoomedMapWidth, zoomedMapHeight, QColor(Qt::gray));

        for (int y = 0; y < mTilesDown; ++y) {
            for (int x = 0; x < mTilesAcross; ++x) {
                const QRect rect(x * zoomedTileWidth, y * zoomedTileHeight, zoomedTileWidth, zoomedTileHeight);
                painter->drawText(rect, QString::fromLatin1("%1, %2").arg(x).arg(y));
            }
        }
    }

protected:
    void wheelEvent(QWheelEvent *event) override {
        const int oldZoomLevel = mZoomLevel;
        mZoomLevel = qMax(1, qMin(mZoomLevel + (event->angleDelta().y() > 0 ? 1 : -1), 30));

        const QPoint cursorPosRelativeToOffset = event->pos() - mOffset;

        if (mZoomLevel != oldZoomLevel) {
            mOffset.rx() -= cursorPosRelativeToOffset.x();
            mOffset.ry() -= cursorPosRelativeToOffset.y();

            // Attempts based on https://stackoverflow.com/a/14085161/904422
//            mOffset.setX((event->pos().x() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.x()));
//            mOffset.setY((event->pos().y() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.y()));

//            mOffset.setX((cursorPosRelativeToOffset.x() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.x()));
//            mOffset.setY((cursorPosRelativeToOffset.y() * (mZoomLevel - oldZoomLevel)) + (mZoomLevel * -mOffset.y()));

            update();
        }
    }

    void keyReleaseEvent(QKeyEvent *event) override {
        static const int panDistance = 50;
        switch (event->key()) {
        case Qt::Key_Left:
            mOffset.rx() -= panDistance;
            update();
            break;
        case Qt::Key_Right:
            mOffset.rx() += panDistance;
            update();
            break;
        case Qt::Key_Up:
            mOffset.ry() -= panDistance;
            update();
            break;
        case Qt::Key_Down:
            mOffset.ry() += panDistance;
            update();
            break;
        }
    }

private:
    const int mTileWidth;
    const int mTileHeight;
    const int mTilesAcross;
    const int mTilesDown;
    QPoint mOffset;
    int mZoomLevel;
};

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<Canvas>("App", 1, 0, "Canvas");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

#include "main.moc"

main.qml:

import QtQuick 2.5
import QtQuick.Window 2.2

import App 1.0 as App

Window {
    visible: true
    width: 1200
    height: 900
    title: qsTr("Hello World")

    Shortcut {
        sequence: "Ctrl+Q"
        onActivated: Qt.quit()
    }

    App.Canvas {
        focus: true
        anchors.fill: parent
    }
}

我在做什么错了wheelEvent()功能?


你有一个矩形R = [x_0, x_0 + w] x [y_0, y_0 + h]具有绝对坐标。当您将其映射到小部件(另一个矩形)时,您应用了一些转换T到一个区域W of R。该变换与偏移量呈线性关系:

的价值观a_x, b_x, a_y, b_y计算满足一些简单的条件,你就已经做到了。

You also have a cursor (x_c, y_c) in R. It's coordinates in W are T(x_c, y_c). Now you want to apply another transformation T'\colon R \rightarrow W',

改变尺度系数a_x, a_y已知a_x', a_y'具有以下条件:您希望光标指向相同的坐标(x_c, y_c) in R. I.e. T'(x_c, y_c) = T(x_c, y_c)——相对坐标中的同一点指向绝对坐标中的同一位置。我们推导出未知偏移量的系统b_x', b_y'具有已知的休息值。它给

最后的工作是找到(x_c, y_c)从小部件光标位置(x_p, y_p) = T(x_c, y_c):

并替换它:

用你的话来说就是

mOffset = event->pos() - float(mZoomLevel) / float(oldZoomLevel) *
     (event->pos() - mOffset);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

计算在鼠标光标位置放大的视图偏移 的相关文章

  • 编译时运算符

    有人可以列出 C 中可用的所有编译时运算符吗 C 中有两个运算符 无论操作数如何 它们的结果始终可以在编译时确定 它们是sizeof 1 and 2 当然 其他运算符的许多特殊用途可以在编译时解决 例如标准中列出的那些整数常量表达式 1 与
  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 通过 CMIS (dotCMIS) 连接到 SP2010:异常未经授权

    我正在使用 dotCMIS 并且想要简单连接到我的 SP2010 服务器 我尝试用 C 来做到这一点 如下所示http chemistry apache org dotnet getting started with dotcmis htm
  • 为什么两个不同的 Base64 字符串的转换会返回相等的字节数组?

    我想知道为什么从 base64 字符串转换会为不同的字符串返回相同的字节数组 const string s1 dg const string s2 dq byte a1 Convert FromBase64String s1 byte a2
  • 按成员序列化

    我已经实现了template
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 秒表有最长运行时间吗?

    多久可以Stopwatch在 NET 中运行 如果达到该限制 它会回绕到负数还是从 0 重新开始 Stopwatch Elapsed返回一个TimeSpan From MSDN https learn microsoft com en us
  • 用于检查类是否具有运算符/成员的 C++ 类型特征[重复]

    这个问题在这里已经有答案了 可能的重复 是否可以编写一个 C 模板来检查函数是否存在 https stackoverflow com questions 257288 is it possible to write a c template
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 关于 C++ 转换:参数 1 从“[some_class]”到“[some_class]&”没有已知的转换

    我正在研究 C 并且遇到了一个错误 我不知道确切的原因 我已经找到了解决方案 但仍然想知道原因 class Base public void something Base b int main Base b b something Base
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • C# 成员变量继承

    我对 C 有点陌生 但我在编程方面有相当广泛的背景 我想做的事情 为游戏定义不同的 MapTiles 我已经像这样定义了 MapTile 基类 public class MapTile public Texture2D texture pu
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定

随机推荐