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 + "°"
}
}