Canvas - 时钟绘制

导语:距离上一次写canvas,已经过去两年半,如今业务需要,再次拾起,随手记录。

 

 

【思考】 时钟的绘制主要在于圆的绘制:1. 使用context.arc()方法直接绘制圆或圆弧; 2. 使用圆的方程(x = r * cosA + X, y = r * sinA + Y)进行描点绘制。指针运行速率较慢,故使用setInterval进行刷新重绘。
【优化】可以使用两个canvas,一个用来绘制表盘,另一个绘制指针,如此,只需刷新重绘指针canvas,表盘canvas保持不变。

 

复制代码
<!DOCTYPE html>
<html>
<head>
  <title>Canvas Clock</title>
</head>
<body>
<canvas id="clock">Your borswer does not support canvas element.</canvas>
<script type="text/javascript">
/**
 * 圆的方程:x = r * cosA + X, y = r * sinA + Y
 * 浏览器为了达到抗锯齿的效果会做额外的运算,故为提高渲染效率,均使用整数进行绘制。
 */
(function() {
    let clockCvs = document.getElementById('clock')
    if (!clockCvs || !clockCvs.getContext) return
    clockCvs.width = 310
    clockCvs.height = 310
    let clockCtx = clockCvs.getContext('2d')
    // X坐标偏移
    const X = 155
    // Y坐标偏移
    const Y = 155
    // 钟的半径
    const R = 150

    start()
    setInterval(start, 1000)

    function start () {
        clockCtx.clearRect(0, 0, clockCvs.width, clockCvs.height)
        renderClockPlate()
        renderClockTime()
        renderClockHand()
    }

    // 渲染表盘
    function renderClockPlate () {
        drawCircle(X, Y, '#070702', R, 1)
        drawCircle(X, Y, '#4f4f52', R - 3, 5)
        drawCircle(X, Y, '#070702', R - 6, 3)
        drawCircle(X, Y, '#dddddd', R - 8)
        drawCircle(X, Y, '#121213', R - 10, 3)
        drawCircle(X, Y, '#19191a', R - 12, 0, 'fill', true)

        drawCircle(X, Y, '#4e4738', 15, 0, 'fill')
        drawCircle(X, Y, '#eac55a', 10, 0, 'fill')
        drawCircle(X, Y, '#3e3e41', 8, 0, 'fill')
        drawCircle(X, Y, '#000000', 3, 0, 'fill')
    }

    // 渲染时间
    function renderClockTime () {
        for (let angle = -90; angle < 270; angle = angle + 6) {
            let x = Math.round((R - 18) * Math.cos(angle / 180 * Math.PI) + X)
            let y = Math.round((R - 18) * Math.sin(angle / 180 * Math.PI) + Y)
            let r = angle % 90 === 0 ? 4 : 2
            drawCircle(x, y, '#eac55a', r, 0, 'fill')
            if (angle % 30 === 0) {
                x = Math.round((R - 35) * Math.cos(angle / 180 * Math.PI) + X - 4)
                y = Math.round((R - 35) * Math.sin(angle / 180 * Math.PI) + Y + 6)
                clockCtx.font = angle % 90 === 0 ? 'bold 15px yahei' : '12px yahei'
                clockCtx.fillText((angle + 90) / 30 || 12, x , y)
            }
        }
    }

    // 渲染表针
    function renderClockHand () {
        let date = new Date()
        let hour = date.getHours()
        let minute = date.getMinutes()
        let second = date.getSeconds()
        // 秒针
        let angle1 = (second * 6 - 90)
        let x = Math.round((R - 45) * Math.cos(angle1 / 180 * Math.PI) + X)
        let y = Math.round((R - 45) * Math.sin(angle1 / 180 * Math.PI) + Y)
        drawLine([[X, Y], [x, y]], 1)
        // 分针
        let angle2 = (minute * 6 - 90)
        x = Math.round((R - 65) * Math.cos(angle2 / 180 * Math.PI) + X)
        y = Math.round((R - 65) * Math.sin(angle2 / 180 * Math.PI) + Y)
        drawLine([[X, Y], [x, y]], 2)
        // 时针, 时针角度 = 小时角度 + 分钟角度
        let angle3 = ((hour % 12) * 30 - 90) + (angle2 / 12)
        x = Math.round((R - 90) * Math.cos(angle3 / 180 * Math.PI) + X)
        y = Math.round((R - 90) * Math.sin(angle3 / 180 * Math.PI) + Y)
        drawLine([[X, Y], [x, y]], 4)
        
        
    }

    /**
     * @param {String} color 颜色
     * @param {Number} r 圆半径
     * @param {Number} lineWidth 线条粗细
     * @param {String} type 类型,stroke/fill
     * @param {Boolean} isLinear 是否渐变
     */
    function drawCircle (x, y,color = '#000000', r = 10, lineWidth = 2, type = 'stroke', isLinear = false) {
        let grd = clockCtx.createLinearGradient(0, 0, clockCvs.width, clockCvs.height)
        grd.addColorStop(0, color)
        grd.addColorStop(0.5, '#555555')
        grd.addColorStop(1, color)
        clockCtx[type + 'Style'] = isLinear ? grd : color
        clockCtx.lineWidth = lineWidth
        clockCtx.beginPath()
        clockCtx.arc(x, y, r, 0, Math.PI * 2, true)
        clockCtx.closePath()
        clockCtx[type]()
    }

    /**
     * @param {Array} pos 坐标点集合,如 [[0, 0], [120, 120]]
     * @param {String} color 颜色
     * @param {Number} lineWidth 线条粗细
     */
    function drawLine (pos, lineWidth = 2, color = '#eac55a') {
        clockCtx.strokeStyle = color
        clockCtx.lineWidth = lineWidth
        clockCtx.beginPath()
        clockCtx.moveTo(pos[0][0], pos[0][1])
        for (let i = 0, len = pos.length; i < len; i++) {
            clockCtx.lineTo(pos[i][0], pos[i][1])
        }
        clockCtx.stroke()
        clockCtx.closePath()
    }
})()
</script>
</body>
</html>
优化前
复制代码

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<!DOCTYPE html>
<html>
<head>
  <title>Canvas Clock</title>
</head>
<body>
<canvas id="clock" style="position: absolute;">Your borswer does not support canvas element.</canvas>
<canvas id="clockHand" style="position: absolute;">Your borswer does not support canvas element.</canvas>
<script type="text/javascript">
/**
 * 圆的方程:x = r * cosA + X, y = r * sinA + Y
 * 浏览器为了达到抗锯齿的效果会做额外的运算,故为提高渲染效率,均使用整数进行绘制。
 */
(function() {
    let clockCvs = document.getElementById('clock')
    let clockHandCvs = document.getElementById('clockHand')
    if (!clockCvs || !clockCvs.getContext) return
    clockCvs.width = clockHandCvs.width = 310
    clockCvs.height = clockHandCvs.height = 310
    let clockCtx = clockCvs.getContext('2d')
    let clockHandCtx = clockHandCvs.getContext('2d')
    // X坐标偏移
    const X = 155
    // Y坐标偏移
    const Y = 155
    // 钟的半径
    const R = 150
 
    renderClockPlate()
    renderClockTime()
    renderClockHand()
    setInterval(function () {
        clockHandCtx.clearRect(0, 0, clockHandCvs.width, clockHandCvs.height)
        renderClockHand()
    }, 1000)
 
    // 渲染表盘
    function renderClockPlate () {
        drawCircle(clockCtx, clockCvs, X, Y, '#070702', R, 1)
        drawCircle(clockCtx, clockCvs, X, Y, '#4f4f52', R - 3, 5)
        drawCircle(clockCtx, clockCvs, X, Y, '#070702', R - 6, 3)
        drawCircle(clockCtx, clockCvs, X, Y, '#dddddd', R - 8)
        drawCircle(clockCtx, clockCvs, X, Y, '#121213', R - 10, 3)
        drawCircle(clockCtx, clockCvs, X, Y, '#19191a', R - 12, 0, 'fill', true)
 
        drawCircle(clockCtx, clockCvs, X, Y, '#4e4738', 15, 0, 'fill')
        drawCircle(clockCtx, clockCvs, X, Y, '#eac55a', 10, 0, 'fill')
        drawCircle(clockCtx, clockCvs, X, Y, '#3e3e41', 8, 0, 'fill')
        drawCircle(clockCtx, clockCvs, X, Y, '#000000', 3, 0, 'fill')
    }
 
    // 渲染时间
    function renderClockTime () {
        for (let angle = -90; angle < 270; angle = angle + 6) {
            let x = Math.round((R - 18) * Math.cos(angle / 180 * Math.PI) + X)
            let y = Math.round((R - 18) * Math.sin(angle / 180 * Math.PI) + Y)
            let r = angle % 90 === 0 ? 4 : 2
            drawCircle(clockCtx, clockCvs, x, y, '#eac55a', r, 0, 'fill')
            if (angle % 30 === 0) {
                x = Math.round((R - 35) * Math.cos(angle / 180 * Math.PI) + X - 4)
                y = Math.round((R - 35) * Math.sin(angle / 180 * Math.PI) + Y + 6)
                clockCtx.font = angle % 90 === 0 ? 'bold 15px yahei' : '12px yahei'
                clockCtx.fillText((angle + 90) / 30 || 12, x , y)
            }
        }
    }
 
    // 渲染表针
    function renderClockHand () {
        let date = new Date()
        let hour = date.getHours()
        let minute = date.getMinutes()
        let second = date.getSeconds()
        // 秒针
        let angle1 = (second * 6 - 90)
        let x = Math.round((R - 45) * Math.cos(angle1 / 180 * Math.PI) + X)
        let y = Math.round((R - 45) * Math.sin(angle1 / 180 * Math.PI) + Y)
        drawLine(clockHandCtx, [[X, Y], [x, y]], 1)
        // 分针
        let angle2 = (minute * 6 - 90)
        x = Math.round((R - 65) * Math.cos(angle2 / 180 * Math.PI) + X)
        y = Math.round((R - 65) * Math.sin(angle2 / 180 * Math.PI) + Y)
        drawLine(clockHandCtx, [[X, Y], [x, y]], 2)
        // 时针, 时针角度 = 小时角度 + 分钟角度
        let angle3 = ((hour % 12) * 30 - 90) + (angle2 / 12)
        x = Math.round((R - 90) * Math.cos(angle3 / 180 * Math.PI) + X)
        y = Math.round((R - 90) * Math.sin(angle3 / 180 * Math.PI) + Y)
        drawLine(clockHandCtx, [[X, Y], [x, y]], 4)
    }
 
    /**
     * @param {String} color 颜色
     * @param {Number} r 圆半径
     * @param {Number} lineWidth 线条粗细
     * @param {String} type 类型,stroke/fill
     * @param {Boolean} isLinear 是否渐变
     */
    function drawCircle (ctx, cvs, x, y,color = '#000000', r = 10, lineWidth = 2, type = 'stroke', isLinear = false) {
        let grd = ctx.createLinearGradient(0, 0, cvs.width, cvs.height)
        grd.addColorStop(0, color)
        grd.addColorStop(0.5, '#555555')
        grd.addColorStop(1, color)
        ctx[type + 'Style'] = isLinear ? grd : color
        ctx.lineWidth = lineWidth
        ctx.beginPath()
        ctx.arc(x, y, r, 0, Math.PI * 2, true)
        ctx.closePath()
        ctx[type]()
    }
 
    /**
     * @param {Array} pos 坐标点集合,如 [[0, 0], [120, 120]]
     * @param {String} color 颜色
     * @param {Number} lineWidth 线条粗细
     */
    function drawLine (ctx, pos, lineWidth = 2, color = '#eac55a') {
        ctx.strokeStyle = color
        ctx.lineWidth = lineWidth
        ctx.beginPath()
        ctx.moveTo(pos[0][0], pos[0][1])
        for (let i = 0, len = pos.length; i < len; i++) {
            ctx.lineTo(pos[i][0], pos[i][1])
        }
        ctx.stroke()
        ctx.closePath()
    }
})()
</script>
</body>
</html>

  

posted @   妖艳货  阅读(363)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示