14. Use Canvas to create your own clock component

1. Description

When customizing the clock component, the basic control used is mainly Canvas . There are two ways to draw related elements: one is to draw all component elements in the same canvas, which requires constant adjustment of the properties of the brush and canvas It is easy to be confused when saving and restoring; the other is to create multiple canvas components, and the elements of each part are drawn on their own canvases. The logic is relatively clear, but there will be relatively many canvas components. This article uses the second one. Way.
Show results:
Insert image description here

2. Overall code

import QtQuick 2.15
import QtQuick.Controls 2.15

Item{
    
    
    id:root
    implicitWidth: 400
    implicitHeight: implicitWidth

    // 尺寸属性
    property real outerCircleRadius:root.width / 2.05
    property real innerCircleRadius:root.width / 2.05

    // 颜色属性
    property color bgColor:"white"
    property color outerColor:"black"
    property color innerColor:"black"
    property color innerRootColor:"lightSlateGray"
    property color innerLineColorL:"#484D58"
    property color innerLineColorS:"#63677A"
    property color textColor:"black"
    property color hourLineColor:"#484D58"
    property color minuteLineColor:"#484D58"
    property color secondLineColor:"red"
    property color timeTxtColor:"black"

    // 时间属性
    property var hours
    property var minutes
    property var seconds
    property var currentTime
    property alias hoursAngle:hourLine.angle
    property alias minutesAngle:minuteLine.angle
    property alias secondsAngle:secondLine.angle

    // 组件加载完成后先初始化当前时间
    Component.onCompleted: {
    
    
        calculateAngle()
    }

    // 时间计算
    function calculateAngle(){
    
    
        var date = new Date()
        hours = date.getHours()
        // 模除得到12小时制的小时数
        hours = hours % 12
        minutes = date.getMinutes()
        seconds = date.getUTCSeconds()
        currentTime = hours + ":" + minutes + ":" + seconds
        hoursAngle = Math.PI*2/12*hours+Math.PI*2*minutes/12/60-Math.PI
        minutesAngle = Math.PI*2*minutes/60 + Math.PI*2*seconds/60/60 -Math.PI
        secondsAngle = Math.PI*2*seconds/60-Math.PI
    }

    // 绘制背景
    Canvas{
    
    
        id:bgCircle
        width: root.width
        height: root.height
        anchors.centerIn: parent
        onPaint: {
    
    
            // 绘制背景
            var ctx = getContext("2d")  
            ctx.save()
            ctx.lineWidth = root.width/50   
            ctx.fillStyle = bgColor
            ctx.beginPath()
            ctx.arc(root.width/2,root.height/2,outerCircleRadius,0,Math.PI * (12/6))
            ctx.fill()
            ctx.restore()
        }
    }
    // 绘制圆环轮廓
    Canvas{
    
    
        id:outerCircle
        width: root.width
        height: root.height
        anchors.centerIn: parent
        onPaint: {
    
    
            var ctx = getContext("2d")  //创建画师
            //为画师创建画笔并设置画笔属性
            ctx.lineWidth = root.width/50   //设置画笔粗细
            ctx.strokeStyle = outerColor    //设置画笔颜色
            ctx.beginPath()     //每次绘制调用此函数,重新设置一个路径
            // 按照钟表刻度进行划分,一圈是Math.PI * 2,分成12个刻度,每个刻度占用 1/6
            // canvas绘制圆弧,是按照顺时针绘制,起点默认在三点钟方向
            ctx.arc(root.width/2,root.height/2,outerCircleRadius,0,Math.PI * (12/6))
            ctx.stroke()    //根据strokeStyle对边框进行描绘
        }
    }
    // 绘制圆环内衬
    Canvas{
    
    
        id:innerCircle
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property real endAngle:Math.PI * (12/6)
        onPaint: {
    
    
            var ctx = getContext("2d")  
            ctx.save()
            ctx.lineWidth = root.width/50   
            ctx.strokeStyle = innerColor     
            ctx.beginPath()     
            ctx.arc(root.width/2,root.height/2,innerCircleRadius,0,endAngle)
            ctx.stroke()    
            ctx.restore()

            // 绘制指针根部圆圈
            ctx.save()
            ctx.lineWidth = root.width/50   
            ctx.fillStyle = innerRootColor
            ctx.beginPath()
            ctx.arc(root.width/2,root.height/2,innerCircleRadius/16,0,endAngle)
            ctx.fill()
            ctx.restore()
        }
    }
    // 绘制刻度线
    Canvas{
    
    
        id:innerLine
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property real lineNums:60
        onPaint: {
    
    
            var ctx = getContext("2d")  
            for (var i = 0; i <= lineNums; ++i){
    
    
                ctx.beginPath();
                var angle = 2 * Math.PI / 60 * i;
                var dx = Math.cos(angle)*(outerCircleRadius-15);
                var dy = Math.sin(angle)*(outerCircleRadius-15);
                var dx2 = Math.cos(angle)*(outerCircleRadius-7);
                var dy2 = Math.sin(angle)*(outerCircleRadius-7);
                if (i % 5 === 0){
    
    
                    ctx.lineWidth = root.width/100
                    ctx.strokeStyle = innerLineColorL
                }else{
    
    
                    ctx.lineWidth = root.width/200
                    ctx.strokeStyle = innerLineColorS
                }
                ctx.moveTo(root.width/2+dx,root.height/2+dy);
                ctx.lineTo(root.width/2+dx2,root.height/2+dy2);
                ctx.stroke();
            }
        }
    }
    // 绘制数字
    Canvas{
    
    
        id:drawText
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property var numbers : [1,2,3,4,5,6,7,8,9,10,11,12]
        onPaint: {
    
    
            var ctx = getContext("2d")  
            ctx.font = "18px Arial";
            ctx.textAlign = "center";
            ctx.textBaseline = "middle";
            for(var i = 0; i < 12; ++i)
            {
    
    
                ctx.fillStyle = textColor
                var angle = 2 * Math.PI / 12 * numbers[i] - 3.14 / 2;
                var dx = Math.cos(angle)*(outerCircleRadius-30);
                var dy = Math.sin(angle)*(outerCircleRadius-30);
                ctx.fillText(numbers[i],root.width/2 + dx,root.height / 2 + dy);
                ctx.fill()
            }
        }
    }
    // 绘制时针线
    Canvas{
    
    
        id:hourLine
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property real angle
        onPaint: {
    
    
            var ctx = getContext("2d")  
            // 先清空画布上之前的内容
            ctx.clearRect(0,0,width,height)
            ctx.save()
            ctx.beginPath()
            ctx.lineWidth = root.width/100
            ctx.strokeStyle=hourLineColor
            // 平移坐标点(注意:坐标系原点先平移,再旋转)
            ctx.translate(root.width/2,root.height/2)
            // 旋转坐标系
            ctx.rotate(angle)
            // 坐标原点变化之后再进行实际的绘图
            ctx.moveTo(0,-20);
            ctx.lineTo(0,outerCircleRadius / 2 - 15);
            ctx.stroke()
            ctx.restore()
        }
    }
    // 绘制分针线
    Canvas{
    
    
        id:minuteLine
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property real angle
        onPaint: {
    
    
            var ctx = getContext("2d")  
            ctx.clearRect(0,0,width,height)
            ctx.save()
            ctx.beginPath()
            ctx.lineWidth = root.width/100
            ctx.strokeStyle=minuteLineColor
            // 平移坐标点(注意:坐标系原点先平移,再旋转)
            ctx.translate(root.width/2,root.height/2)
            // 旋转坐标系
            ctx.rotate(angle)
            // 坐标原点变化之后再进行实际的绘图
            ctx.moveTo(0,-25);
            ctx.lineTo(0,outerCircleRadius / 2 - 5);
            ctx.stroke()
            ctx.restore()
        }
    }
    // 绘制秒针线
    Canvas{
    
    
        id:secondLine
        width: root.width
        height: root.height
        anchors.centerIn: parent
        property real angle
        onPaint: {
    
    
            var ctx = getContext("2d")  
            ctx.clearRect(0,0,width,height)
            ctx.save()
            ctx.beginPath()
            ctx.lineWidth = root.width/100
            ctx.strokeStyle=secondLineColor
            // 平移坐标点(注意:坐标系原点先平移,再旋转)
            ctx.translate(root.width/2,root.height/2)
            // 旋转坐标系
            ctx.rotate(angle)
            // 坐标原点变化之后再进行实际的绘图
            ctx.moveTo(0,-30);
            ctx.lineTo(0,outerCircleRadius / 1.5);
            ctx.stroke()
            ctx.restore()
        }
    }
    // 当前时间显示
    Text{
    
    
        id:timeTxt
        y:root.height * 0.75
        anchors.horizontalCenter: root.horizontalCenter
        text: currentTime
        font.pixelSize: root.width/12
        color: timeTxtColor
    }

    // 使用定时器(每秒钟进行计算)
    Timer{
    
    
        id:angleCal
        interval: 1000
        repeat: true
        running: true
        onTriggered: {
    
    
            calculateAngle()
            hourLine.requestPaint()
            minuteLine.requestPaint()
            secondLine.requestPaint()
        }
    }
}

Guess you like

Origin blog.csdn.net/FY_13781298928/article/details/132406683