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
