先上效果图
canvas时钟的绘制参考了 # Sunshine_Lin的# 为了让她10分钟入门canvas,我熬夜写了3个小项目和这篇文章
我个人认为比较有难点的就是四个阴影的绘制
代码语言:javascript复制// 外右下阴影
ctx.shadowOffsetX = 10; // 阴影Y轴偏移
ctx.shadowOffsetY = 10; // 阴影X轴偏移
ctx.shadowBlur = 30; // 模糊尺寸
ctx.shadowColor = theme === 'night'?'rgba(36, 37, 46, 0.1)':'rgba(0, 0, 0, 0.1)'; // 颜色
ctx.fillStyle = theme === 'night'?'#24252e':'#ebecf3'; // 填充路径的当前的颜色
ctx.arc(0, 0, 250, 0, 2 * Math.PI);
ctx.fill(); // 关闭该路径,然后填充该路径
// 外左上阴影
ctx.shadowOffsetX = -10; // 阴影Y轴偏移
ctx.shadowOffsetY = -10; // 阴影X轴偏移
ctx.shadowBlur = 30; // 模糊尺寸
ctx.shadowColor = theme === 'night'?'#3d3d3f':'white'; // 颜色
ctx.arc(0, 0, 250, 0, 2 * Math.PI);
ctx.fill(); // 关闭该路径,然后填充该路径
//绘制内部阴影
arcInnerShadow(ctx, 0, 0, 250,theme === 'night'?'#4a4a4e':'white',-10,-10);
arcInnerShadow(ctx, 0, 0, 250,theme === 'night'?'rgba(0, 0, 0, 0.5)':'rgba(0, 0, 0, 0.2)',10,10);
// 【圆形内阴影(边框 阴影,再把边框和外阴影裁剪掉)】
// 参数说明:ctx上下文内容,x,y,r同arc的入参,shadowColor阴影颜色,OffsetX和OffsetY一同控制偏移角度。
function arcInnerShadow (ctx, x, y, r, shadowC, OffsetX, OffsetY) {
let shadowColor = shadowC || 'white'; // 阴影颜色
ctx.save();
ctx.beginPath();
ctx.shadowOffsetX = OffsetX; // 阴影Y轴偏移
ctx.shadowOffsetY = OffsetY; // 阴影X轴偏移
// 裁剪区(只保留内部阴影部分)
ctx.arc(x, y, r, 0, 2*Math.PI);
ctx.clip();
// 边框 阴影
ctx.beginPath();
ctx.lineWidth = 20;
ctx.shadowColor = shadowColor;
ctx.shadowBlur = 30;
// 因线是由坐标位置向两则画的,所以半径加上线宽的一半。同时又因为线有毛边,所以半径再多加1px,处理毛边。
ctx.arc(x, y, r 20/2 1, 0, 2*Math.PI);
ctx.stroke();
// 取消阴影
ctx.shadowBlur = 0;
ctx.restore();
}
复制代码
好了,完整代码奉上,感兴趣的小伙伴可以自己试一试
HTML Css
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
body{
background-color: #ebecf3;
}
.canvas{
text-align: center;
position: relative;
width: 600px;
margin: 250px auto 0 auto;
}
.text{
width: 55%;
margin: 0 auto;
}
.time{
font-size: 165px;
color: #303133;
text-align: center;
}
.APM{
font-size: 35px;
letter-spacing: 3px;
text-align: right;
margin-top: 50px;
}
.date{
font-size: 35px;
color: #303133;
text-align: center;
}
.icon{
box-shadow: 0 0 5px rgba(0,0,0,0.2) inset;
display: table-cell;
vertical-align:middle;
text-align: center;
width: 60px;
height: 60px;
line-height: 55px;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
}
.icon-img{
width: 40px;
height: 40px;
vertical-align:middle;
}
.name{
position: absolute;
bottom: 90px;
font-size: 30px;
left: 45%;
}
</style>
</head>
<body>
<div class="canvas">
<canvas id="canvas" width="600" height="600"></canvas>
<div class="icon" id="icon" onclick="clickIcon()">
<img src="月亮.png" class="icon-img">
</div>
</div>
<div class="text">
<div class="APM t"></div>
<div class="time t"></div>
<div class="date t"></div>
</div>
<div class="name">I'm inline</div>
<script src="canvas.js"></script>
</body>
</html>
复制代码
JS
代码语言:javascript复制// 定义夜间 白天 背景色
const dayColor = '#ebecf3';
const nightColor = '#24252e';
let theme = ''; // 主题 day / night
// 图标点击
function clickIcon(){
let img = document.getElementsByTagName('img');
let imgName = img[0].outerHTML.slice(10,16);
img[0].src = imgName === '月亮.png'?'太阳.png':'月亮.png';
if (imgName === '月亮.png'){ // 夜间
theme = 'night';
document.body.style.backgroundColor = nightColor;
const t = document.getElementsByClassName('t');
for (let i = 0; i < t.length; i ){
document.getElementsByClassName('t')[i].style.color = dayColor;
}
document.getElementsByClassName('name')[0].style.color = dayColor;
}else {
theme = 'day';
document.body.style.backgroundColor = dayColor;
const t = document.getElementsByClassName('t');
for (let i = 0; i < t.length; i ){
document.getElementsByClassName('t')[i].style.color = nightColor;
}
document.getElementsByClassName('name')[0].style.color = nightColor;
}
canvasMapping();
}
let c = document.getElementById("canvas");
let ctx = c.getContext('2d');
// 获取日期时间
timeDate();
// 绘图
canvasMapping();
// 获取日期时间
function timeDate (){
let time = new Date();
let hour = time.getHours() % 12;
let min = time.getMinutes();
let y = time.getFullYear();
let m = time.getMonth();
let d = time.getDate();
hour = hour < 10 ? `0${hour}` : hour;
min = min < 10 ? `0${min}` : min;
let str = time.getHours() > 12 ? 'PM' : 'AM';
let date = new Date(`${y}-${m}-${d}`.replace(/-/g,'/')); //Wed Jan 02 2019 00:00:00 GMT 0800 (China Standard Time)
let chinaDate = date.toDateString(); //"Tue, 01 Jan 2019 16:00:00 GMT"
//之后的处理是一样的
let chinaDateArray = chinaDate.split(' '); //["Wed", "Jan", "02", "2019"]
let displayDate = `${chinaDateArray[2]} ${chinaDateArray[1]} ${chinaDateArray[3]}`; //"Jan 02, 2019"
let timeDate = document.getElementsByClassName('time');
timeDate[0].innerHTML = `${hour}:${min}`;
let APM = document.getElementsByClassName('APM');
APM[0].innerHTML = str;
let t = document.getElementsByClassName('date');
t[0].innerHTML = displayDate;
}
// 定时器 一秒执行一次绘制
setInterval(()=>{ canvasMapping() },1000);
// canvas 绘图
function canvasMapping(){
ctx.save()
ctx.clearRect(0, 0, 600, 600); // 清除画布
ctx.translate(300, 300); // 设置中心点,此时300,300变成了坐标的0,0
ctx.save(); // 保存当前的绘图状态
// 画大圆
ctx.strokeStyle = theme === 'night'?'#24252e':'#ebecf3'; // 设置线条颜色
ctx.beginPath(); // 丢弃当前路径,并且开始一条新路径
ctx.arc(0, 0, 250, 0, 2 * Math.PI); // 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)
ctx.stroke(); // 执行画线段的操作stroke
ctx.closePath(); // 关闭一条打开的子路径
// 外右下阴影
ctx.shadowOffsetX = 10; // 阴影Y轴偏移
ctx.shadowOffsetY = 10; // 阴影X轴偏移
ctx.shadowBlur = 30; // 模糊尺寸
ctx.shadowColor = theme === 'night'?'rgba(36, 37, 46, 0.1)':'rgba(0, 0, 0, 0.1)'; // 颜色
ctx.fillStyle = theme === 'night'?'#24252e':'#ebecf3'; // 填充路径的当前的颜色
ctx.arc(0, 0, 250, 0, 2 * Math.PI);
ctx.fill(); // 关闭该路径,然后填充该路径
// 外左上阴影
ctx.shadowOffsetX = -10; // 阴影Y轴偏移
ctx.shadowOffsetY = -10; // 阴影X轴偏移
ctx.shadowBlur = 30; // 模糊尺寸
ctx.shadowColor = theme === 'night'?'#3d3d3f':'white'; // 颜色
ctx.arc(0, 0, 250, 0, 2 * Math.PI);
ctx.fill(); // 关闭该路径,然后填充该路径
//绘制内部阴影
arcInnerShadow(ctx, 0, 0, 250,theme === 'night'?'#4a4a4e':'white',-10,-10);
arcInnerShadow(ctx, 0, 0, 250,theme === 'night'?'rgba(0, 0, 0, 0.5)':'rgba(0, 0, 0, 0.2)',10,10);
// 【圆形内阴影(边框 阴影,再把边框和外阴影裁剪掉)】
// 参数说明:ctx上下文内容,x,y,r同arc的入参,shadowColor阴影颜色,OffsetX和OffsetY一同控制偏移角度。
function arcInnerShadow (ctx, x, y, r, shadowC, OffsetX, OffsetY) {
let shadowColor = shadowC || 'white'; // 阴影颜色
ctx.save();
ctx.beginPath();
ctx.shadowOffsetX = OffsetX; // 阴影Y轴偏移
ctx.shadowOffsetY = OffsetY; // 阴影X轴偏移
// 裁剪区(只保留内部阴影部分)
ctx.arc(x, y, r, 0, 2*Math.PI);
ctx.clip();
// 边框 阴影
ctx.beginPath();
ctx.lineWidth = 20;
ctx.shadowColor = shadowColor;
ctx.shadowBlur = 30;
// 因线是由坐标位置向两则画的,所以半径加上线宽的一半。同时又因为线有毛边,所以半径再多加1px,处理毛边。
ctx.arc(x, y, r 20/2 1, 0, 2*Math.PI);
ctx.stroke();
// 取消阴影
ctx.shadowBlur = 0;
ctx.restore();
}
// 获取时分秒
let time = new Date();
let hour = time.getHours() % 12;
let min = time.getMinutes();
let sec = time.getSeconds();
if (sec === 0){
timeDate()
}
// 时针 rotate(60);将**旋转60度
ctx.rotate(2 * Math.PI / 12 * hour 2 * Math.PI / 12 * (min / 60) - Math.PI / 2); // (2 * 180 / 12 * 小时 2 * 180 / 12 * (分钟 / 60)- 180 / 2);
ctx.beginPath();
ctx.moveTo(-10, 0); // moveTo设置画线起点
ctx.lineTo(130, 0); // lineTo设置画线经过点
ctx.lineWidth = 8; // 设置线宽
ctx.lineCap="round";
ctx.strokeStyle = theme === 'night'?dayColor:nightColor;
ctx.stroke();
ctx.closePath();
ctx.restore(); // 恢复成上一次save的状态
ctx.save(); // 恢复完再保存一次
// 分针
ctx.rotate(2 * Math.PI / 60 * min 2 * Math.PI / 60 * (sec / 60) - Math.PI / 2)
ctx.beginPath();
ctx.moveTo(-10, 0);
ctx.lineTo(180, 0);
ctx.lineWidth = 8;
ctx.lineCap="round";
ctx.strokeStyle = theme === 'night'?dayColor:nightColor;
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
// 画小圆 外圈
ctx.beginPath();
ctx.arc(0, 0, 15, 0, 2*Math.PI);
ctx.fillStyle = theme === 'night'?nightColor: '#fff' ; // 设置填充色
ctx.strokeStyle = theme === 'night'?nightColor: '#fff'; // 设置线条颜色
ctx.fill(); // 开始填充
ctx.stroke();
ctx.closePath();
// 画小圆
ctx.beginPath();
ctx.arc(0, 0, 10 , 0, 2*Math.PI);
ctx.fillStyle="blue"; // 设置填充色
ctx.fill(); // 开始填充
ctx.stroke();
ctx.closePath();
//秒针
ctx.rotate(2 * Math.PI / 60 * sec - Math.PI / 2);
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(160, 0);
ctx.lineWidth = 5; // 设置线宽
ctx.lineCap="round";
ctx.strokeStyle = 'blue';
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
// 绘制刻度,也是跟绘制时分秒针一样,只不过刻度是死的
ctx.lineWidth = 5;
for (let i = 0; i < 4; i ) {
ctx.rotate(2 * Math.PI / 4);
ctx.beginPath();
ctx.lineCap="round";
ctx.strokeStyle = theme === 'night'?dayColor:nightColor;
ctx.moveTo(230, 0);
ctx.lineTo(200, 0);
ctx.stroke();
ctx.closePath();
}
ctx.restore(); // 恢复之前保存的绘图状态
ctx.restore();
}
复制代码