QT Quick QML 实例之椭圆投影,旋转

发布于:2025-02-10 ⋅ 阅读:(30) ⋅ 点赞:(0)


QML 其它文章请点击这里:      QT QUICK QML 学习笔记
国际站点 GitHub:      https://github.com/chenchuhan
国内站点 Gitee :       https://gitee.com/chuck_chee


一、前言

此 Demo 主要用于无人机吊舱视角的模拟,这种动态调整椭圆比例和方向文字的功能可以很好地表示吊舱的俯仰角变化和视野方向。通过滑块调整俯仰角,椭圆比例随之改变,同时方向标记实时旋转,能够直观反映无人机当前的视角范围和角度变化。

二、演示

  • 椭圆的俯仰角与动态缩放pitchangle 随着滑块高度变化改变椭圆纵向半径 (radiusY),模拟俯仰角变化。

  • 文本在椭圆轨迹上旋转,通过 ellipseCanvas.movingTextAngle 动态计算角度,滑块拖动时实时更新。

请添加图片描述

三、部分代码与分析

 
右侧滑块

  • 通过 MouseArea 设置了竖直方向 (Drag.YAxis) 的拖动行为,并限定滑块移动范围。
  • 梯度与样式:滑块本身采用了绿色到蓝色的渐变样式,且支持抗锯齿。
  • 信号处理:onYChanged 调用 ellipseCanvas.requestPaint(),触发椭圆和文字的重新绘制。

   Rectangle {
       anchors.fill: parent;
       color: "lightGray"

       Rectangle {
           id: container
           anchors {
               top: parent.top;
               topMargin: _margin * 2;
               bottom: parent.bottom;
               bottomMargin: _margin *2;
               right: parent.right;
               rightMargin: _margin
           }
           width: _pix
           radius: width/2;
           opacity: 0.6            //不透明度
           antialiasing: true      // 抗锯齿,具体效果见下面图片
           //黑色——>棕色的渐变
           gradient: Gradient {
               GradientStop { position: 0.0; color: "black" }
               GradientStop { position: 1.0; color: "brown" }
           }

           //[重点2]:当面板放大放小的时候,需要保持滑块的比例不变
           onHeightChanged: {
               if(height <= slider.height){     //当缩小的太小的时候,可以把小滑块也跟着变小
                   slider.height = height;      //小滑块高度  =  滑道高度
               }
               else  {
                   slider.height = _pix*2;      //小滑块高度  =  固定高度
                   var _scale =  (height -  _pix*2) / (oldHeight -  _pix*2)
                   //比列尺 * 之前的实际距离
                   slider.y =  slider.y * _scale;
                   oldHeight = height;
                   //[重点3]默认中间值,也会随着长宽拖动而变化的。
                   _defaultSilderValue = height/2-_pix;
               }
           }
           //小滑块条
           Rectangle {
               id: slider
               x: 1; y: container.height/2-_pix;    //y轴向向下为负, 默认滑块放中间
               width: _pix-2;  height: _pix*2  ;
               radius: width/2;
               antialiasing: true
               gradient: Gradient {
                   GradientStop { position: 0.0; color: "green" }
                   GradientStop { position: 1.0; color: "aqua" }
               }
               MouseArea {
                   anchors.fill: parent
                   anchors.margins: -_pix
                   drag.target: parent;
                   drag.axis: Drag.YAxis
                   drag.minimumY: 1;
                   drag.maximumY: container.height - slider.height-1;
               }
               onYChanged: {
                    ellipseCanvas.requestPaint()
               }
           }
       }
   }

下侧滑块

  • 控制旋转角度 ellipseCanvas.movingTextAngle,其值直接绑定到 Text 显示的内容。
  • 滑块范围设置为 0-360,用于控制画布中旋转文本的角度。
    Slider {
        id: slider2
        width: parent.width * 0.8
        height: 20
        minimumValue: 0
        maximumValue: 360
        stepSize: 1
        anchors.bottom:  parent.bottom
        anchors.bottomMargin: 20
        anchors.horizontalCenter: parent.horizontalCenter
        onValueChanged : {
            ellipseCanvas.movingTextAngle = value;
            ellipseCanvas.requestPaint();
        }
    }

中间画布

  • 主要负责绘制动态椭圆和沿轨道旋转的文字。通过纵向半径 (radiusY) 依据滑块位置动态变化,模拟俯仰角比例 (pitchangle)。
  • 绘制椭圆中,绘制了两个椭圆,外圈为黑色,内圈为白色。
  • 动态计算每个文字在椭圆轨迹上的位置,角度通过滑块值和文字索引动态调整。
  • 在每个文字位置绘制刻度线,长度固定。
  • 当滑块 sliderslider2 值发生变化时,调用 ellipseCanvas.requestPaint() 触发重绘。
   Canvas {
       id: ellipseCanvas

       property real movingTextAngle: 0      // 控制“北”文字的角度

       anchors.fill: parent
       onPaint: {
           var ctx = ellipseCanvas.getContext('2d');
           var startX = ellipseCanvas.width / 2 - radius;
           var startY = ellipseCanvas.height / 2 - radius;
           var radiusX = radius

           var pitchangle = (slider.y)/(container.height- _margin *4)

           var radiusY = radiusX * pitchangle;

           console.log("[pitchangle]:", pitchangle)

           ctx.clearRect(0, 0, ellipseCanvas.width, ellipseCanvas.height);

           ctx.lineWidth =         5
           ctx.strokeStyle=        "black"
           ctx.beginPath();
           ctx.ellipse(startX, startY, radiusX*2, radiusY*2);
           ctx.stroke();

           ctx.lineWidth =         3
           ctx.strokeStyle=        "white"
           ctx.beginPath();
           ctx.ellipse(startX, startY, radiusX*2, radiusY*2);
           ctx.stroke();

           // 设置文本样式
           ctx.font = "20px Arial";
           ctx.fillStyle = "black";

           // 绘制沿椭圆轨道旋转的文本
           var textArray = ["东", "南", "西", "北"];
           var textAngleStep = Math.PI / 2; // 每个文本之间的角度差

           for (var i = 0; i < textArray.length; i++) {
               var textAngle = i * textAngleStep; // 计算文本的角度

               // 计算文本位置,输入角度从 0 到 360 度
               var angleInRadians = (Math.PI * ellipseCanvas.movingTextAngle / 180 + i * textAngleStep) % 360;

               var x = startX + radiusX + (radiusX - ctx.measureText(textArray[i]).width*1.2) * Math.cos(angleInRadians) ;
               var y = startY + radiusY + (radiusY - ctx.measureText(textArray[i]).width*1.2) * Math.sin(angleInRadians);

               // 绘制文本
               ctx.fillText(textArray[i], x-ctx.measureText(textArray[i]).width/2, y+ctx.measureText(textArray[i]).width/2);

               // 绘制刻度线
               ctx.strokeStyle = "red"
               var tickLength = 10; // 刻度长度

               // 绘制刻度线
               ctx.beginPath();
               ctx.moveTo(startX + radiusX + radiusX*Math.cos(angleInRadians),
                          startY + radiusY + radiusY*Math.sin(angleInRadians))

               ctx.lineTo(startX + radiusX + radiusX*Math.cos(angleInRadians) - tickLength * Math.cos(angleInRadians),
                          startY + radiusY + radiusY*Math.sin(angleInRadians) - tickLength * Math.sin(angleInRadians));
               ctx.stroke();
           }
       }
   }
}

QML 其它文章请点击这里:     QT QUICK QML 学习笔记


网站公告

今日签到

点亮在社区的每一天
去签到