QML 指南方向條 (類似喫雞的指南方向條Canvas實現方式)控件

QML 指南方向條 (類似喫雞的指南方向條Canvas實現方式)控件

效果圖:
在這裏插入圖片描述

Canvas實現方式:
qml 源代碼:

import QtQuick 2.12
import QtQuick.Controls 2.4

Canvas {
    id:myCanvas
    width: parent.width
    height: parent.height
    contextType: "2d"

    //左右刻度與Canvas邊距
    property int _SPACING : 40

    //刻度尺每條線的長度
    property int _SCALE_LINE_LENGTH : 15

    //頂部距離Canvas
    property int topMargin:height/2 + 20



    //需要把刻度尺分成幾等份大刻度,取偶數
    property int _SCALE_LINE_SPACE : 8

    //每1大刻度代表多少度
    property int _UNIT_SPACING: 15

    //每1大刻度的像素寬度
    property int _UNIT_PIX_SPACING : (width - _SPACING * 2) / _SCALE_LINE_SPACE

    //輸入角度的像素偏移(相對於最近的前一個大刻度線) = 每一度的像素寬度*當前輸入角度的度數偏移(相對於最近的前一個大刻度線度數)
    property int offset : (_UNIT_PIX_SPACING / _UNIT_SPACING) * (intputAngle % _UNIT_SPACING);

    //輸入角度
    property int intputAngle: 0

    //1大刻度等分_UNIT_MINI_SPACING個小刻度
    property int _UNIT_MINI_SPACING : 5


    onPaint: {
        var ctx = getContext("2d")
        ctx.fillStyle = "#668866"
        ctx.fillRect(0, 0, width, height)

        drawCurrentAngle(ctx)
        drawScale(ctx)
        drawTriangle(ctx)

    }

    function drawScale(ctx){

        for (var i = 0; i < _SCALE_LINE_SPACE + 1; i++) {

            // 需要畫大刻度線的x座標值
            var x = _UNIT_PIX_SPACING * i + _SPACING  - offset

            //當前角度最近前一個大刻度線上的刻度
            var stdLineCurrentDrawAngle = intputAngle - intputAngle%_UNIT_SPACING
            //大刻度線上的刻度
            var stdLineAngle = stdLineCurrentDrawAngle + _UNIT_SPACING * (i - _SCALE_LINE_SPACE/2)

            // 標準化 0-360
            stdLineAngle = (stdLineAngle%360 + 360)%360

            var largeMark;
            if(stdLineAngle%45===0){
                largeMark = true
            }else{
                largeMark = false
            }

            //畫大刻度線
            ctx.beginPath()
            ctx.lineWidth="2"
            ctx.strokeStyle=largeMark?"red":"white"
            ctx.moveTo(x,topMargin);
            ctx.lineTo(x,topMargin + _SCALE_LINE_LENGTH)
            ctx.stroke();

            //畫小刻度線
            for (var j = 1; j < _UNIT_MINI_SPACING; j++){
                var miniX = x + j*_UNIT_PIX_SPACING/_UNIT_MINI_SPACING
                ctx.beginPath()
                ctx.lineWidth="1"
                ctx.strokeStyle="white"
                ctx.moveTo(miniX,topMargin + 2);
                ctx.lineTo(miniX,topMargin + _SCALE_LINE_LENGTH/2)
                ctx.stroke();
            }


            // 畫刻度值
            ctx.font =largeMark?"25px Source Han Sans K Medium":"20px Source Han Sans K Medium"
            ctx.fillStyle = "#ffffff";
            var mark = calculateDegree(stdLineAngle)

            var markPosX = x - (mark.length===1?8:
                                mark.length===2?18:
                                mark.length===4?20:15)
            var markPosY = topMargin -5

            //context.fillText(text,x,y,maxWidth=default);
            ctx.fillText(mark, markPosX, markPosY)

            // 進行繪製
            ctx.stroke();

        }

    }

    function drawTriangle(ctx){
        //中間刻度位置
        var x = (_SCALE_LINE_SPACE * _UNIT_PIX_SPACING)/2.0 + _SPACING

        ctx.fillStyle = "red"
        ctx.beginPath()
        ctx.moveTo(x, myCanvas.height-20)
        ctx.lineTo(x - 6, myCanvas.height)
        ctx.lineTo(x + 6,  myCanvas.height)
        ctx.fill()
        ctx.stroke();
    }

    function drawCurrentAngle(ctx){

        ctx.font ="25px Source Han Sans K Medium"
        ctx.fillStyle = "#ffffff";
        //中間刻度位置
        var x = (_SCALE_LINE_SPACE * _UNIT_PIX_SPACING)/2.0 + _SPACING

        var mark = getDirectionText(intputAngle)

        var markPosX = x - (mark.length/2.0)*10
        var markPosY = topMargin -40

        //context.fillText(text,x,y,maxWidth=default);
        ctx.fillText(mark, markPosX, markPosY)
        ctx.stroke();
    }


    function calculateDegree(degree) {
        degree = (degree%360 + 360)%360
        if (degree === 45) {
            return "NE";
        }
        else if (degree === 135) {
            return "SE";
        }
        else if (degree === 225) {
            return "SW";
        }
        else if (degree === 315) {
            return "NW";
        }
        else if (degree === 0 || degree === 360) {
            return "N";
        } else if (degree === 90) {
            return "E";
        } else if (degree === 180) {
            return "S";
        } else if (degree === 270) {
            return "W";
        }else{
            return degree + "°"
        }
    }


    function getDirectionText(direction){

        direction = (direction%360 + 360)%360
         //東南西北
         if(direction<22.5||direction>=337.5&&direction<360){
             return "北" +direction + "°"
         }else if(direction>=22.5&&direction<67.5){
             return "東北" +direction + "°"
         }else if(direction>=67.5&&direction<112.5){
             return "東" +direction + "°"
         }else if(direction>=112.5&&direction<157.5){
             return "東南" +direction + "°"
         }else if(direction>=157.5&&direction<202.5){
             return "南" +direction + "°"
         }else if(direction>=202.5&&direction<247.5){
             return "西南" +direction + "°"
         }else if(direction>=247.5&&direction<292.5){
             return "西" +direction + "°"
         }else if(direction>=292.5&&direction<337.5){
             return "西北" +direction + "°"
         }

         //default
         return "北" + 0 + "°"
     }



}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章