QML動態標注線


QML動態標注線

1,目的

有些情況對某個位置進行標注,但是背景過於復雜, 需要將標注點和標注內容進行分離,這時就需要用到標注線。我們即明確知道了標注的的信息也讓界面更加均衡。
效果圖:

圖1

圖2

 

2,設計分析

如果單純將標識點連線到標注區,這樣在標注內容較多時是會給人雜亂不堪的感覺。這里我們先使用30度傾角拉出斜線,再使用水平畫線延伸到目標點。后期在邏輯上控制斜線盡量不發生交叉,那么給人的感覺就較為整齊。
基本原理:我們已知信息是起始點、結束點坐標,首先通過起始點向結束的點的水平面畫傾角30度的斜線,與結束點水平面重疊時轉為畫水平線直至結束坐標。

3,設計內容

首先在主qml中創建tagLine.qml組件,添加組件屬性和基本元素。代碼如下:

import QtQuick 2.0

Item {
    id: tagLine

    property point startPoint: Qt.point(0, 0)
    property point endPoint: Qt.point(0, 0)
    property var lineColor: "70ffe42f"
    property var lineWidth: 1

    //斜線長度
    property var oblLineLength: 0
    //水平線長度
    property var horLineLength: 0

    Rectangle{
        id: oblLine

        antialiasing: true
        height: lineWidth
        color: lineColor
        transformOrigin: Item.TopLeft
    }

    Rectangle{
        id: horLine

        antialiasing: true
        height: lineWidth
        color: lineColor
        transformOrigin: Item.TopLeft
    }
}

從代碼中可知 我們是使用rectangle作為線條進行繪制。現在基本元素已經齊全,下一步就是計算線條長度,和添加畫線動畫。
我們先實現線條長度計算函數。
首先我們需要了解界面的坐標系,水平方向右是x軸正向,垂直向下是y軸正向。

圖3

在這個坐標系中,結束點可能會出現在起始點的如下4個區域中,我們先實現區域1中的計數,其他區域只需要在這個基礎上稍微調整角度即可。

圖4

下圖5所示,我們最終需要的是a,c這兩條線長。利用三角函數,已知b、f先算a=b/cos30°,d=b*tan30° 。下圖是收藏的常用三角函數值表圖6

圖5

圖6

這里還有一種情況就是d大於f的情況,這時我們就不能使用30度斜邊改用垂線。實現代碼如下

function drawCala() {
    //相對角度
    var angle= 0

    //實際角度值
    var realAngle = 0;

    var newOblLeng = 0;
    var newHorLeng = 0;

    var tmpx = Math.abs(startPoint.x - endPoint.x);
    var tmpy = Math.abs(startPoint.y - endPoint.y);

    //情況1 30°夾角
    if (tmpx >= Math.floor((Math.sqrt(3) / 3) * tmpy))
    {
        newOblLeng = Math.floor(tmpy / (Math.sqrt(3) / 2));
        newHorLeng = tmpx - Math.floor((Math.floor((Math.sqrt(3) / 3) * tmpy)));
        angle = 60;
    }
    //情況2 垂線和直線配合
    else
    {
        newOblLeng = tmpy;
        newHorLeng = tmpx;
        angle = 90;
    }

    oblLine.width = newOblLeng;
    horLine.width = newHorLeng;
}

到此我們已經計算出了這兩根線的線長。還需要繼續處理如下內容才算完成。
1,計算出c/a交匯的位置坐標,作為水平線c的起始點;
2,根據終點在起點的4個不同區域內,計算出線段a的傾角;
3,根據終點在起點的4個不同區域內,計算出水平線c的延伸方向;
4,添加動畫,在a從起點開始延伸到達計算值后,水平線開始延伸,最終到達終點。
這里就不多說了,直接看代碼吧,效果如下

圖7

最終實現的代碼:
tagLine.qml

import QtQuick 2.0

Item {
    property point startPoint: Qt.point(0, 0)
    property point endPoint: Qt.point(0, 0)
    property var lineColor: "#70ffe42f"
    property var lineWidth: 1

    //斜線長度
    property var oblLineLength: 0
    //水平線長度
    property var horLineLength: 0

    onStartPointChanged: {
        drawCala();
    }
    onEndPointChanged: {
        drawCala();
    }

    function drawCala() {
        //相對角度
        var angle= 0

        //實際角度值
        var realAngle = 0;

        var newOblLeng = 0;
        var newHorLeng = 0;

        var tmpx = Math.abs(startPoint.x - endPoint.x);
        var tmpy = Math.abs(startPoint.y - endPoint.y);

        //情況1 30°夾角
        if (tmpx >= Math.floor((Math.sqrt(3) / 3) * tmpy))
        {
            newOblLeng = Math.floor(tmpy / (Math.sqrt(3) / 2));
            newHorLeng = tmpx - Math.floor((Math.floor((Math.sqrt(3) / 3) * tmpy)));
            angle = 60;
        }
        //情況2 垂線和直線配合
        else
        {
            newOblLeng = tmpy;
            newHorLeng = tmpx;
            angle = 90;
        }

        //水平線的Y坐標 和結束點Y坐標相同
        horLine.y = endPoint.y;

        //結束的點在起始點的左上方
        if ((startPoint.x >= endPoint.x) && (startPoint.y >= endPoint.y))
        {
            realAngle = 180 + angle;
            horLine.x = endPoint.x + newHorLeng;
            horLine.rotation = 180;
        }
        //結束的點在起始點的右上方
        else if ((startPoint.x <= endPoint.x) && (startPoint.y >= endPoint.y))
        {
            realAngle = -angle;
            horLine.x = endPoint.x - newHorLeng;
            horLine.rotation = 0;
        }
        //結束的點在起始點的右下方
        else if ((startPoint.x <= endPoint.x) && (startPoint.y <= endPoint.y))
        {
            realAngle = angle;
            horLine.x = endPoint.x - newHorLeng;
            horLine.rotation = 0;
        }
        //結束的點在起始點的左下方
        else if ((startPoint.x >= endPoint.x) && (startPoint.y <= endPoint.y))
        {
            realAngle = 180 - angle;
            horLine.x = endPoint.x + newHorLeng;
            horLine.rotation = 180;
        }

        oblLine.x = startPoint.x;
        oblLine.y = startPoint.y;
        oblLine.rotation = realAngle
        oblLineLength = newOblLeng;
        horLineLength = newHorLeng;

        if (oblLineLength > 0)
        {
            oblAnimation.restart();
        }
        else
        {
            //當使用垂線時斜線長度清零
            oblLine.width = oblLineLength;
            //直接進行水平延伸
            horLine.visible = true;
            horAnimation.restart();
        }
    }

    Rectangle{
        id: oblLine

        antialiasing: true
        height: lineWidth
        color: lineColor
        transformOrigin: Item.TopLeft
    }

    Rectangle{
        id: horLine

        antialiasing: true
        height: lineWidth
        color: lineColor
        transformOrigin: Item.TopLeft
    }

    PropertyAnimation {
        id: oblAnimation
        target: oblLine
        property: "width"
        duration: 500
        from: 0
        to: oblLineLength
        onStopped: {
            if (horLineLength > 0)
            {
                horLine.visible = true
                horAnimation.restart()
            }
        }
        onStarted: {
            horLine.visible = false
        }
    }

    PropertyAnimation {
        id: horAnimation
        target: horLine
        property: "width"
        duration: 600
        from: 0
        to: horLineLength
    }
}

 

使用標注線main.qml

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    visible: true
    width: 480
    height: 240

    color: "#1f1f1f"

    TagLine {
        id: myTagLine
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            myTagLine.startPoint = Qt.point(parent.width / 2, parent.height / 2)
            myTagLine.endPoint = Qt.point(50, 50)
        }
    }
}

 

4,總結

利用Rectangle組件進行畫線,然后再進行動畫拼接,最終實現線條延伸效果,此組件比較適合應用在標識內容較多的場合。后續還可以配合標識框的組合動畫讓效果更佳。對以上代碼稍作調整可實現如圖1,2的效果。

下來大家發現有什么問題或需要討論交流,可以在簡書、博客園、或郵箱將問題進行留言,我會及時回復和更新。
郵箱: whqcxz@163.com
原創:https://www.simbahiker.com/news/0220200524001.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM