导语:距离上一次写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>

优化前

<!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>

  

Canvas - 时钟绘制的更多相关文章

  1. canvas自适应圆形时钟绘制

    前面的话 前面介绍过canvas粒子时钟的绘制,本文将详细介绍canvas自适应圆形时钟绘制 效果演示 最终自适应圆形时钟的效果如下所示 功能分析 下面来分析一下该圆形时钟的功能 [1]静态背景 对于 ...

  2. 环形进度条的实现方法总结和动态时钟绘制(CSS3、SVG、Canvas)

    缘由: 在某一个游戏公司的笔试中,最后一道大题是,“用CSS3实现根据动态显示时间和环形进度[效果如下图所示],且每个圆环的颜色不一样,不需要考虑IE6~8的兼容性”.当时第一想法是用SVG,因为SV ...

  3. canvas基础绘制-绚丽时钟

    效果图: 与canvas基础绘制-绚丽倒计时的代码差异: // var endTime = new Date();//const声明变量,不可修改,必须声明时赋值: // endTime.setTim ...

  4. 原生js之canvas时钟组件

    canvas一直是前端开发中不可或缺的一种用来绘制图形的标签元素,比如压缩上传的图片.比如刮刮卡.比如制作海报.图表插件等,很多人在面试的过程中也会被问到有没有接触过canvas图形绘制. 定义 ca ...

  5. Javascript高级编程学习笔记(88)—— Canvas(5)绘制文本

    绘制文本 同样的,canvas也为绘制文本提供了相应的方法. 2D上下文提供的文本绘制方法主要有两个: fillText() strokeText() 这两个方法都接受四个参数 要绘制的文本字符串 绘 ...

  6. Javascript高级编程学习笔记(87)—— Canvas(4)绘制路径

    绘制路径 2D上下文支持许多在画布上绘制路径的方法 通过路径可以创造出复杂的形状和线条,要绘制路径首先必须调用beginPath()方法,表示开始绘制路径 然后再通过下列的方法绘制路径: arc(x, ...

  7. 浅谈JavaScript的Canvas(绘制图形)

    HTML5中新增加的一个元素canvas,要使用canvas元素,浏览器必须支持html5.通过canvas标签来创建元素,并需要为canvas指定宽度和高度,也就是绘图区域的大小. <canv ...

  8. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  9. Coffeescript实现canvas时钟

    前言 参照Mozilla 官方教程,要在Canvas上画动画时钟,思路非常有意思. 把动画看作是多个帧组成,定时每个时间点在Canvas上画一帧来实现动画.而Mozilla 官方教程画图实现的思路有意 ...

随机推荐

  1. phpcms v9 列表页直接下载功能代码实现

    {pc:content action="lists" catid="$catid" num="3" order="id DESC& ...

  2. 巨人大哥谈Web应用中的Session(session详解)

    巨人大哥谈Web应用中的Session(session详解) 虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术. ...

  3. 第七周PTA作业

    第一题: #include<stdio.h> int main() { ; ; ){ sum=sum+i; i++; } printf("sum = %d\n",sum ...

  4. 数据故障的恢复-MSSQL ndf文件大小变为0 KB恢复过程

    一.故障描述 成都某客户,存储损坏,数据库崩溃.重组存储,恢复数据库文件,发现有四个ndf文件大小变为0 KB.数据库大小约80TB.数据库中有1223个文件,数据库每10天生成一个NDF文件,每个N ...

  5. C#中的函数式编程:递归与纯函数(二)

    在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential transparency)来定义的.如果一个 ...

  6. 第四章 Ajax与jQuery

    第四章   Ajax与jQuery 一.Ajax简介 在传统的Web应用中,每次请求服务器都会生成新的页面,用户在提交请求后,总是要等待服务器的响应.如果前一个请求没有响应,则后一个请求就不能发送,在 ...

  7. PHP模式设计之单例模式、工厂模式、注册树模式、适配器模式、观察者模式

    php模式设计之单例模式 什么是单例模式? 单例模式是指在整个应用中只有一个实例对象的设计模式 为什么要用单例模式? php经常要链接数据库,如果在一个项目中频繁建立连接数据库,会造成服务器资源的很大 ...

  8. LxmlLinkExtractor类参数解析

    LxmlLinkExtractor LxmlLinkExtractor 是一种强大的链接提取器,使用他能很方便的进行选项过滤,他是通过xml中强大的HTMLParser实现的 源代码如下: class ...

  9. 新概念英语(1-103)The French Test

    Lesson 103 The French test 法语考试 Listen to the tape then answer this question. How long did the exam ...

  10. Spring Boot面试题

    Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家. 问题一 Spring Boot.Spring MVC 和 Spring 有什么区别 ...