在 QML 中为 MapQuickItem 设置位置更新动画

Posted

技术标签:

【中文标题】在 QML 中为 MapQuickItem 设置位置更新动画【英文标题】:Animating MapQuickItem in QML on position updates 【发布时间】:2020-01-17 01:42:40 【问题描述】:

我有一个QAbstractListModel 对象,它维护要在地图上显示的项目列表。这些项目的位置每隔几秒钟就会改变一次,很容易计算出未来 60 秒后的非常准确的位置。我要做的是在项目获得新位置时设置项目的位置(该部分效果很好),然后立即将项目移向计算出的未来位置。

没有动画的代码是这样的,而且运行良好:

    Component 
       id: drawTarget

       MapQuickItem 
           id: marker

           coordinate: data.coords

           sourceItem: Item 
               id: item
               ...

data 对象有一个属性,它返回项目在未来 60 秒后的估计位置,所以我尝试了这个:

    Component 
       id: drawTarget

       MapQuickItem 
           id: marker

           coordinate: data.coords

           CoordinateAnimation 
               id:anim
               property: "coordinate"
           

           onCoordinateChanged: 
               anim.stop()
               anim.from = data.coords
               anim.to = data.coordsIn60sec
               anim.duration = 60000
               anim.start()
                      

           sourceItem: Item 
               id: item
               ...

但是,尽管对象的位置在每次位置更新时都会正确更新,但朝向未来估计位置的动画根本不起作用。

如何去做这样的事情?

【问题讨论】:

【参考方案1】:

在它的代码中,它做了一个绑定coordinate: data.coords,声明“坐标”取“坐标”的值,但同时又说“坐标”取决于动画,是不是矛盾?嗯,这是矛盾的。

想法不是绑定coordinate: data.coords,而是仅通过动画更新属性。

以下代码是一个可行的例子:

ma​​in.qml

import QtQuick 2.14
import QtQuick.Window 2.14
import QtLocation 5.6
import QtPositioning 5.6

Window 
    visible: true
    width: 640
    height: 480
    Plugin 
        id: mapPlugin
        name: "osm"
    
    Map 
        anchors.fill: parent
        plugin: mapPlugin
        center: QtPositioning.coordinate(59.91, 10.75) // Oslo
        zoomLevel: 10
        MapItemView
            model: datamodel
            delegate: MapQuickItem
                id: item
                // begin configuration
                property var position: model.position
                property var nextposition: model.nextposition
                onPositionChanged: restart();
                onNextpositionChanged: restart();
                function restart()
                    anim.stop()
                    anim.from = position
                    anim.to = nextposition
                    anim.start()
                
                CoordinateAnimation 
                    id: anim
                    target: item
                    duration: 60 * 1000
                    property: "coordinate"
                
                // end of configuration
                anchorPoint.x: rect.width/2
                anchorPoint.y: rect.height/2
                sourceItem: Rectangle
                    id: rect
                    color: "green"
                    width: 10
                    height: 10
                
            
        
    

datamodel.h

#ifndef DATAMODEL_H
#define DATAMODEL_H

#include <QAbstractListModel>
#include <QGeoCoordinate>
#include <QTimer>
#include <random>

#include <QDebug>

struct Data
    QGeoCoordinate position;
    QGeoCoordinate nextposition;
;

static QGeoCoordinate osloposition(59.91, 10.75); // Oslo;

class DataModel : public QAbstractListModel

    Q_OBJECT
    QList<Data> m_datas;
public:
    enum PositionRoles 
        PositionRole = Qt::UserRole + 1,
        NextPositionRole
    ;
    explicit DataModel(QObject *parent = nullptr)
        : QAbstractListModel(parent)
    
        init();
    
    int rowCount(const QModelIndex &parent = QModelIndex()) const override
        return parent.isValid() ? 0: m_datas.count();
    
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        if (!index.isValid() || index.row() < 0 || index.row() >= m_datas.count())
            return QVariant();
        const Data &data = m_datas[index.row()];
        if (role == PositionRole)
            return QVariant::fromValue(data.position);
        else if (role == NextPositionRole)
            return QVariant::fromValue(data.nextposition);
        return QVariant();
    
    QHash<int, QByteArray> roleNames() const override
        QHash<int, QByteArray> roles;
        roles[PositionRole] = "position";
        roles[NextPositionRole] = "nextposition";
        return roles;
    
private:
    void init()
        for(int i=0; i< 10; ++i)
            Data data;
            data.position = osloposition;;
            data.nextposition = data.position;
            m_datas << data;
        
        QTimer *timer = new QTimer(this);
        QObject::connect(timer, &QTimer::timeout, this, &DataModel::updateData);
        timer->start(60 * 1000);
        updateData();
    
    void updateData()
        qDebug() << __PRETTY_FUNCTION__;
        static std::default_random_engine e;
        static std::uniform_real_distribution<> dis(-.1, .1);
        for(int i=0; i < m_datas.count(); ++i)
            Data & data = m_datas[i];
            QModelIndex ix = index(i);
            data.position = data.nextposition;
            data.nextposition = QGeoCoordinate(osloposition.latitude() + dis(e),
                                           osloposition.longitude() + dis(e));
            Q_EMIT dataChanged(ix, ix, PositionRole, NextPositionRole);
        
    
;

#endif // DATAMODEL_H

下面link是完整的例子

【讨论】:

Eyllanesc,这是一篇极好的帖子。它不仅解决了我的问题,而且我同时学到了其他一些东西。赞一个!

以上是关于在 QML 中为 MapQuickItem 设置位置更新动画的主要内容,如果未能解决你的问题,请参考以下文章

如何在qt qml中更改地图上代表组件的颜色

如何在 QML 中为矩形创建滚动条

有没有办法在占位符文本 QML 中为特定单词着色?

在 C++ 中为 QML 创建一个中等复杂的绘图类

如何显示 MapQuickItem 的动态列表?

用 Loader 加载的 MapQuickItem 不显示