买不起劳力士,一气之下熬夜写一个
点击关注公众号,”技术干货”及时达!最近喜欢研究起了手表,对劳力士这款“百事圈”实在是心水的不行啊!
心痒难耐无奈钱包不支持,作为一个前端程序员,买不起的东西该怎么办?
当然是「自己做一个」啊!
说干就干,熬夜自己做了个“百事圈”出来!源码在最后!
先看成品截屏2023-11-23 11.37.58.png还是有那么六七成相像了吧!主要还是在颜色选择和细节处理上要花些功夫,无奈通过单纯的平面很难展现出“「材质感」”。
录屏2023-11-23 11.37.12.2023-11-23 11_38_44.gif虽然质感比不上人家吧,但咱们这个走时精准度肯定比那钢砣子强不是??。
除了实现了手表的“走时”这个基础功能外,还把「日历窗」、「GMT双时区功能」、「外圈旋转功能」也一并实现啦!图片里不好展示,大家可以看完文章,自行领取源码来玩玩!
实现思路本想想用尽量多的CSS来实现,但CSS实现这种较为复杂的动态功能还是有些头疼,所以还是把思路放到了用canvas来实现。
小小构思后手表被分为三个模块:
表盘
表圈
截屏2023-11-23 13.41.33.png表针先在html结构中建立三个canvas标签:
divclass="watch-box"
!--表针--
canvasid="watchPointer"width="1800"height="1800"/canvas
!--表盘--
canvasid="dial"width="1800"height="1800"/canvas
!--表圈--
canvasid="bezel"width="1800"height="1800"/canvas
/div
并在JS中响应地对三个canvas进行初始化。
每个canvas画布绘制的第一步,我们首先用getContext("2d")方法来获取到canvas画布的二维渲染上下文,并且为了能让画面清晰度更高,我们使用.scale()方法对canvas进行四倍缩放,这样我们可以得到一个清晰度更高的canvas画布:
constwatchBox=document.querySelector(".watch-box");
constwatchPointer=document.querySelector("#watchPointer");
constdial=document.querySelector("#dial");
constbezel=document.querySelector("#bezel");
constctx=watchPointer.getContext("2d");
constdialCtx=dial.getContext("2d");
constbezelCtx=bezel.getContext("2d");
constratio=4;
ctx.scale(ratio,ratio);
dialCtx.scale(ratio,ratio);
bezelCtx.scale(ratio,ratio);
鉴于我们的手表中包含了不少颜色,这些颜色都会在canvas绘制中使用到,所以我们先把颜色存储在「变量」中方便后续使用:
//初始化颜色变量
constgmtBezelRed="#8a2811";
constblue="#133760";
constblack="#10111e";
constwhite="#fff";
constgrayD="#ddd";
constgrayC="#ccc";
constgrayB="#bbb";
constgrayA="#aaa";
constgray9="#999";
constgray8="#888";
constgmtPointerRed="#aa0d0f";
consttransparent="grba(0,0,0,255)";
好,准备部分做完了,我们开始正题!构建绘制方法drawGmtBezel,首先对最简单的表圈部分进行绘制:
//绘制表圈
functiondrawGmtBezel(){
//设置中心点,此时225,225变成了坐标的0,0
bezelCtx.translate(225,225);
//阴影的x偏移
bezelCtx.shadowOffsetX=50;
//阴影的y偏移
bezelCtx.shadowOffsetY=50;
//阴影颜色
bezelCtx.shadowColor="rgba(0,0,0,0.5)";
//阴影的模糊半径
bezelCtx.shadowBlur=100;
/**
*绘制陶瓷表圈
*@param{CanvasRenderingContext2D}bezelCtx
*@param{number}begin
*@param{number}end
*@param{string}color
*@returns
**/
constdrawCeramicCircle=(bezelCtx,begin,end,color)={
bezelCtx.beginPath();
bezelCtx.lineWidth=26.5;
bezelCtx.arc(0,0,113.25,begin,end);
bezelCtx.strokeStyle=color;
bezelCtx.stroke();
bezelCtx.closePath();
}
//画上表圈(蓝)
drawCeramicCircle(bezelCtx,Math.PI,2*Math.PI,blue)
//画下表圈(红)
drawCeramicCircle(bezelCtx,0,Math.PI,gmtBezelRed)
}
目前的代码只是绘制出了双色表圈:
截屏2023-11-23 14.26.57 2.png我们首先使用bezelCtx.translate(225, 225)来设置画布的原始点,由于我们要画的是圆心在画布中心点的表圈,所以我们把画布原始点设置到225, 225这个画布中心点的位置。
之所以是225这个数字,是因为我们在canvas标签中将canvas大小设置为1800x1800,又在canvas初始化时把scale设置为4倍缩放,所以画布分辨率实际上就是1800/4,也就是450x450像素,中心点自然就是225, 225了。
随后我们对表圈部分的阴影进行设置,这里就不用我多介绍啦,和CSS的box-shadow逻辑是一样的。
接下来就是绘制的部分了,我们再来看看代码:
constdrawCeramicCircle=(bezelCtx,color,begin,end)={
bezelCtx.beginPath();
bezelCtx.lineWidth=26.5;
bezelCtx.arc(0,0,113.25,begin,end);
bezelCtx.strokeStyle=color;
bezelCtx.stroke();
bezelCtx.closePath();
}
我们首先用beginPath方法来开始一个新路径,可以理解为调用一只新“画笔”,我们在canvas中的所有线条绘制都要靠这个“画笔”来进行。
思路其实很简单,每个画面都是由一笔一画的基础元素组成的,我们需要对这些基础元素进行拆解,每一笔不一样的笔触都需要换一只新笔,然后设置这只笔的各种属性,最后再进行绘画。
我们随后就使用lineWidth()和strokeStyle()设置这只“画笔”的粗细、颜色属性,并且使用arc方法来绘制一个“弧形”,这个arc方法接收五个参数:圆心的 x 轴坐标、圆心的 y 轴坐标、圆弧的半径、圆弧的起始点、圆弧的终点。
我们在前面已经把画布的起始点设置为画布中心了,所以前两个圆心参数我们都传入0,半径就选用113.25这个数字(纯为了比例协调),起点和终点的设置就需要稍微计算一下了,所谓的“圆弧起始点”默认是x轴上右侧的半径切点:
截屏2023-11-23 15.03.28.png所以如果我们要先画“下半圈”的话,起点也就是0,终点也就是在x轴的左侧。这两个参数的单位都是“弧度”,一个半圆对应的弧度也就是PI,所以下半圆的起始点是0,终点是PI。
按照这样调用方法drawCeramicCircle(bezelCtx, 0, Math.PI, gmtBezelRed),看看效果:
截屏2023-11-23 15.10.52.png没问题,我们再用同样的逻辑drawCeramicCircle(bezelCtx, Math.PI, 2 * Math.PI, blue)制作上半圈:
最后我们用stroke方法来进行绘制,图像就被绘制出来了!
这就实现啦,逻辑其实不过就是这样:新建路径(画笔)— 设置路径属性 — 设置路径信息 — 绘制。
表盘的逻辑也是一致,只要你稍微掌握如何在canvas中绘制矩形、线条,实现起来其实没有什么难度,我们直接快进到表针部分:
functiondrawWatchPointer(){
//设置中心点,此时225,225变成了坐标的0,0
ctx.translate(225,225);
//获取当前时分秒
lettime=newDate();
letday=time.getDate();
lethour=time.getHours()%12;
letmin=time.getMinutes();
letsecond=time.getSeconds();
letmillsecond=time.getMilliseconds();
//时针
ctx.rotate(((2*Math.PI)/12)*hour+((2*Math.PI)/12)*(min/60)-Math.PI/2);
ctx.beginPath();
ctx.lineWidth=3;
ctx.fillStyle=white;
ctx.fillRect(0,-4,40,8);
ctx.strokeStyle=grayA;
ctx.strokeRect(0,-3,40,6);
ctx.closePath();
//奔驰针头上三角
ctx.beginPath();
ctx.moveTo(48,-4.5);
ctx.lineTo(57,0);
ctx.lineTo(48,4.5);
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.fillStyle=white;
ctx.fill();
ctx.stroke();
ctx.closePath();
//绘制奔驰针
ctx.beginPath();
ctx.arc(40,0,10,0,2*Math.PI);
ctx.fillStyle=white;
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(30,0);
ctx.lineTo(39,0);
ctx.lineTo(46.5,7);
ctx.lineTo(39,0);
ctx.lineTo(46.5,-7);
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.stroke();
ctx.closePath();
}
其实可以看到,整体逻辑和画表圈并没有什么不同,只是有一些新的方法需要学习。我们还是顺着逻辑走一遍:
设置画布中心点为255, 255通过new Date()获取当前时间表针其实就是一个固定的矩形,只是需要改变矩形的旋转就可以表示时间。所以通过((2 * Math.PI) / 12) * hour + ((2 * Math.PI) / 12) * (min / 60) - Math.PI / 2来计算出当前时间对于时针来说需要旋转的角度并传参给rotate方法,使用rotate方法可以旋转绘制的角度新建路径,并设置路径属性(粗细、颜色)使用fillRect来绘制矩形,四个参数分别代表x起始点、y起始点、宽、高结束表针本体的绘制,使用closePath来清除画笔信息。接下来绘制表针的针头(三角形)使用moveTo方法来移动画笔起始位置使用lineTo方法来从画笔起始位置绘制一条直线,参数为直线终点的x和y连续绘制三条直线,形成三角形使用fill来填充矩形内部的颜色结束表针本体的绘制,使用closePath来清除画笔信息。接下来绘制表针的奔驰针部分(圆形)…可以看到,其实使用逻辑都是一样的,不过就是上面说的这些,你可以自己尝试一下把分针和秒针给实现出来,应该就会对canvas有个基本的认识啦。
时针的实现效果:
截屏2023-11-23 15.43.12.png完整源码Canvas的基础知识都是比较零碎但深度普遍不深的,我就不带着大家把每个实现都过一遍了,直接把源码拿出来,大家随意取用!
可以试着在这个源码基础上,把自己喜欢的别的表也给做出来,做这玩意有种玩“我的世界”的快感,快来试试吧!
熬夜写的代码比较匆忙,还有很大的优化空间,我就以此抛砖引玉啦:
!DOCTYPEhtml
htmllang="en"
head
metacharset="UTF-8"/
metahttp-equiv="X-UA-Compatible"content="IE=edge"/
metaname="viewport"content="width=device-width,initial-scale=1.0"/
titleRolexGMT-MASTER/title
style
@font-face{
font-family:"Optima";
src:url("fonts/Optima.ttc");
}
@font-face{
src:url("./fonts/Palatino.ttc");
font-family:"TrebuchetMS";
}
@font-face{
font-family:"NunitoSans";
src:url("./fonts/NunitoSans-Regular.ttf");
}
body{
margin:0;
}
.watch-box{
width:100vw;
height:100vh;
display:flex;
justify-content:center;
align-items:center;
background:radial-gradient(circle,#eee,#ccc,#aaa,#777);
}
h2{
position:absolute;
top:0;
font-family:"NunitoSans";
}
canvas{
position:absolute;
transform:scale(0.25);
}
#bezel{
z-index:0;
font-weight:bold;
font-stretch:0px;
}
#dial{
z-index:1;
letter-spacing:0.5px;
}
#watchPointer{
z-index:2;
}
/style
/head
body
divclass="watch-box"
!--表针--
canvasid="watchPointer"width="1800"height="1800"/canvas
!--表盘--
canvasid="dial"width="1800"height="1800"/canvas
!--表圈--
canvasid="bezel"width="1800"height="1800"/canvas
/div
script
constwatchBox=document.querySelector(".watch-box");
constwatchPointer=document.querySelector("#watchPointer");
constdial=document.querySelector("#dial");
constbezel=document.querySelector("#bezel");
constctx=watchPointer.getContext("2d");
constdialCtx=dial.getContext("2d");
constbezelCtx=bezel.getContext("2d");
constratio=4;
ctx.scale(ratio,ratio);
dialCtx.scale(ratio,ratio);
bezelCtx.scale(ratio,ratio);
constlogo=newImage();
constrolexLogo=newImage();
constimgResources=[logo,rolexLogo];
rolexLogo.src="./images/rolex.png";
logo.src="./images/logo.png";
//图片资源加载后绘制表盘
constrenderDrawDial=(()={
letimageOnloadSuccessCount=0;
return()={
imageOnloadSuccessCount
if(imageOnloadSuccessCount=imgResources.length){//图片资源加载完毕
drawDial();
drawGmtBezel();
setInterval(drawWatchPointer,100);
}
}
})()
rolexLogo.onload=renderDrawDial;
logo.onload=renderDrawDial;
constgmtBezelRed="#8a2811";
constblue="#133760";
constblack="#10111e";
constwhite="#fff";
constgrayD="#ddd";
constgrayC="#ccc";
constgrayB="#bbb";
constgrayA="#aaa";
constgray9="#999";
constgray8="#888";
constgmtPointerRed="#aa0d0f";
consttransparent="grba(0,0,0,255)";
//绘制表圈
functiondrawGmtBezel(){
bezelCtx.save();
bezelCtx.clearRect(0,0,1800,1800);
//设置中心点,此时225,225变成了坐标的0,0
bezelCtx.translate(225,225);
bezelCtx.save();
//阴影的x偏移
bezelCtx.shadowOffsetX=50;
//阴影的y偏移
bezelCtx.shadowOffsetY=50;
//阴影颜色
bezelCtx.shadowColor="rgba(0,0,0,0.5)";
//阴影的模糊半径
bezelCtx.shadowBlur=100;
/**
*绘制陶瓷表圈
*@param{CanvasRenderingContext2D}bezelCtx
*@param{number}begin
*@param{number}end
*@param{string}color
*@returns
**/
constdrawCeramicCircle=(bezelCtx,begin,end,color)={
bezelCtx.beginPath();
bezelCtx.lineWidth=26.5;
bezelCtx.arc(0,0,113.25,begin,end);
bezelCtx.strokeStyle=color;
bezelCtx.stroke();
bezelCtx.closePath();
}
//画上表圈(蓝)
drawCeramicCircle(bezelCtx,Math.PI,2*Math.PI,blue)
//画下表圈(红)
drawCeramicCircle(bezelCtx,0,Math.PI,gmtBezelRed)
//最外层金属旋转外圈
bezelCtx.beginPath();
bezelCtx.lineWidth=6;
bezelCtx.arc(0,0,129.5,0,2*Math.PI);
bezelCtx.strokeStyle=grayD;
bezelCtx.stroke();
bezelCtx.closePath();
bezelCtx.save();
bezelCtx.rotate(-Math.PI/2);
for(leti=1;i=60;i++){
bezelCtx.rotate((2*Math.PI)/60);
//绘制旋转外圈上的凹槽
bezelCtx.beginPath();
bezelCtx.lineWidth=0.6;
bezelCtx.arc(132.5,0,4.2,Math.PI/2,(3/2)*Math.PI);
if((i13i18)||(i28i33)){
bezelCtx.fillStyle=gray9;
}elseif(i=18i=28){
bezelCtx.fillStyle=gray8;
}else{
bezelCtx.fillStyle=grayA;
}
bezelCtx.strokeStyle=white;
bezelCtx.fill();
bezelCtx.stroke();
bezelCtx.closePath();
bezelCtx.lineWidth=1;
if(i===60){
//绘制十二点方向外圈
bezelCtx.beginPath();
bezelCtx.lineWidth=1;
bezelCtx.moveTo(106,0);
bezelCtx.lineTo(120,16);
bezelCtx.lineTo(120,-16);
bezelCtx.lineTo(107,0);
bezelCtx.fillStyle=white;
bezelCtx.strokeStyle=white;
bezelCtx.fill();
bezelCtx.stroke();
bezelCtx.closePath();
}
if(i%5===0i!==60){
bezelCtx.save();
bezelCtx.rotate(Math.PI/2);
bezelCtx.beginPath();
bezelCtx.fillStyle=white;
bezelCtx.font="50024pxSaira";
bezelCtx.textBaseline="bottom";
letwidth=bezelCtx.measureText((i*4)/10).width;
if(width20){
bezelCtx.fillText((i*4)/10,-8,-99.5);
}else{
bezelCtx.fillText((i*4)/10,-12,-99.5);
}
bezelCtx.fill();
bezelCtx.stroke();
bezelCtx.closePath();
bezelCtx.restore();
}
if(i%5===3){
bezelCtx.beginPath();
bezelCtx.fillStyle=white;
bezelCtx.strokeStyle=white;
bezelCtx.arc(109,-4,2.7,0,2*Math.PI);
bezelCtx.fill();
bezelCtx.stroke();
bezelCtx.closePath();
}
}
bezelCtx.restore();
bezelCtx.restore();
bezelCtx.rotate(0.5*Math.PI);
}
//绘制表盘
functiondrawDial(){
dialCtx.save();
dialCtx.clearRect(0,0,1800,1800);
//设置中心点,此时225,225变成了坐标的0,0
dialCtx.translate(225,225);
//画表盘外圈
dialCtx.beginPath();
//画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)
dialCtx.arc(0,0,100,0,2*Math.PI);
dialCtx.strokeStyle=grayC;
dialCtx.stroke();
//执行画线段的操作stroke
dialCtx.closePath();
//画表盘
dialCtx.beginPath();
//画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)
dialCtx.arc(0,0,53,0,2*Math.PI);
dialCtx.fillStyle=black;
dialCtx.strokeStyle=black;
dialCtx.lineWidth=94;
dialCtx.stroke();
//执行画线段的操作stroke
dialCtx.closePath();
dialCtx.drawImage(rolexLogo,-25,-56,50,27);
dialCtx.fillStyle=white;
dialCtx.font="5006pxNunitoSans";
dialCtx.textBaseline="bottom";
dialCtx.fillText(
"OYSTERPERPETUALDATE",
-dialCtx.measureText("OYSTERPERPETUALDATE").width/2,
-21
dialCtx.font="6pxNunitoSans";
dialCtx.fillText("GMT-MASTER",-28,34);
dialCtx.font="6pxMarmelad";
dialCtx.fillText("II",25,34.3,4);
dialCtx.font="5pxTrebuchetMS";
dialCtx.fillText("SUPERLATIVECHRONOMETER",-32.5,40,65);
dialCtx.fillText("OFFICIALLYCERTIFIED",-24,46,48);
//绘制刻度
dialCtx.save();
dialCtx.lineWidth=1;
dialCtx.shadowOffsetX=5;
dialCtx.shadowOffsetY=5;
dialCtx.shadowColor="rgba(0,0,0,0.4)";
dialCtx.shadowBlur=10;
dialCtx.rotate(-Math.PI/2);
for(leti=1;i=60;i++){
dialCtx.rotate((2*Math.PI)/60);
dialCtx.beginPath();
dialCtx.lineWidth=1;
dialCtx.strokeStyle=grayD;
if(i%5===0){
dialCtx.strokeStyle=white;
dialCtx.lineWidth=1.3;
}
if(i===28||i===29||i===31||i===32){
dialCtx.moveTo(94,0);
dialCtx.lineTo(96,0);
}else{
dialCtx.moveTo(94,0);
dialCtx.lineTo(98.5,0);
}
if(i!==30)dialCtx.stroke();
if(i===29){
dialCtx.save();
dialCtx.rotate(-Math.PI/2-0.05);
dialCtx.textBaseline="middle";
dialCtx.font="4pxNunitoSans";
dialCtx.fillStyle=white;
dialCtx.fillText(
"MADE",
-dialCtx.measureText("MADE").width/2,
98,
13
dialCtx.restore();
}
if(i===30){
dialCtx.save();
dialCtx.rotate(-Math.PI/2);
ctx.mozImageSmoothingEnabled=false;
ctx.webkitImageSmoothingEnabled=false;
ctx.msImageSmoothingEnabled=false;
ctx.imageSmoothingEnabled=false;
dialCtx.drawImage(logo,-3.5,93,7,6);
dialCtx.restore();
}
if(i===31){
dialCtx.save();
dialCtx.rotate(-Math.PI/2+0.05);
dialCtx.textBaseline="middle";
dialCtx.font="4pxNunitoSans";
dialCtx.fillStyle=white;
dialCtx.fillText(
"SWISS",
-dialCtx.measureText("SWISS").width/2,
98,
13.5
dialCtx.restore();
}
dialCtx.closePath();
if(i===60){
dialCtx.beginPath();
dialCtx.moveTo(90,12);
dialCtx.lineTo(62,0);
dialCtx.lineTo(90,-12);
dialCtx.lineTo(90,12.5);
dialCtx.lineWidth=1.5;
dialCtx.strokeStyle=gray9;
dialCtx.fillStyle=white;
dialCtx.fill();
dialCtx.stroke();
dialCtx.closePath();
}
//绘制刻度
if(i%5===0i%15!==0){
dialCtx.beginPath();
dialCtx.arc(82,0,8.5,0,2*Math.PI);
dialCtx.lineWidth=1.5;
dialCtx.strokeStyle=gray9;
dialCtx.fillStyle=white;
dialCtx.fill();
dialCtx.stroke();
dialCtx.closePath();
}
//绘制刻度
if(i%15===0i!==60i!==15){
dialCtx.beginPath();
dialCtx.lineWidth=1.5;
dialCtx.strokeStyle=gray9;
dialCtx.fillStyle=white;
dialCtx.fillRect(60,-5,30,10);
dialCtx.strokeRect(60,-5,30,10);
dialCtx.fill();
dialCtx.stroke();
dialCtx.closePath();
}
//绘制日历窗
if(i===15){
dialCtx.beginPath();
dialCtx.lineWidth=2;
dialCtx.strokeStyle=gray9;
dialCtx.fillStyle=white;
dialCtx.fillRect(57,-8,25,16);
dialCtx.fill();
dialCtx.stroke();
dialCtx.closePath();
}
}
dialCtx.restore();
dialCtx.restore();
}
functiondrawWatchPointer(){
ctx.save();
ctx.clearRect(0,0,1800,1800);
//设置中心点,此时225,225变成了坐标的0,0
ctx.translate(225,225);
//把状态保存起来
ctx.save();
//获取当前时分秒
lettime=newDate();
letday=time.getDate();
lethour=time.getHours()%12;
letmin=time.getMinutes();
letsecond=time.getSeconds();
letmillsecond=time.getMilliseconds();
//渲染日历窗数字
ctx.fillStyle="#000";
ctx.font="bold16pxAppleGothic";
letwidth=ctx.measureText(day).width;
ctx.fillText(day,width15?63.5:58,6);
ctx.fill();
//绘制圆轴
ctx.beginPath();
ctx.arc(0,0,7,0,2*Math.PI);
ctx.fillStyle=grayA;
ctx.fill();
ctx.closePath();
//时针
ctx.rotate(((2*Math.PI)/12)*hour+((2*Math.PI)/12)*(min/60)-Math.PI/2);
ctx.beginPath();
ctx.lineWidth=3;
ctx.fillStyle=white;
ctx.fillRect(0,-4,40,8);
ctx.strokeStyle=grayA;
ctx.strokeRect(0,-3,40,6);
ctx.stroke();
ctx.closePath();
//奔驰针头上三角
ctx.beginPath();
ctx.moveTo(48,-4.5);
ctx.lineTo(57,0);
ctx.lineTo(48,4.5);
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.fillStyle=white;
ctx.fill();
ctx.stroke();
ctx.closePath();
//绘制奔驰针
ctx.beginPath();
ctx.arc(40,0,10,0,2*Math.PI);
ctx.fillStyle=white;
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(30,0);
ctx.lineTo(39,0);
ctx.lineTo(46.5,7);
ctx.lineTo(39,0);
ctx.lineTo(46.5,-7);
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.stroke();
ctx.closePath();
//恢复成上一次save的状态
ctx.restore();
ctx.save();
//GMT针
ctx.rotate(((2*Math.PI)/24)*time.getHours()+((2*Math.PI)/24)*(min/60)-Math.PI/2);
ctx.beginPath();
ctx.shadowOffsetX=5;
ctx.shadowOffsetY=5;
ctx.shadowColor="rgba(0,0,0,0.2)";
ctx.shadowBlur=15;
ctx.lineWidth=2;
ctx.fillStyle=white;
ctx.strokeStyle=gmtPointerRed;
ctx.moveTo(0,0);
ctx.lineTo(80,0);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle=grayA;
ctx.moveTo(79,-9);
ctx.lineTo(95,0);
ctx.lineTo(80,8);
ctx.lineTo(80,-9);
ctx.fill();
ctx.stroke();
ctx.closePath();
//绘制圆轴
ctx.beginPath();
ctx.arc(0,0,6,0,2*Math.PI);
ctx.fillStyle=grayD;
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(0,0,2.5,0,2*Math.PI);
ctx.fillStyle=grayA;
ctx.fill();
ctx.closePath();
ctx.restore();
ctx.save();
//分针
ctx.rotate(((2*Math.PI)/60)*min+((2*Math.PI)/60)*(second/60)-Math.PI/2);
ctx.beginPath();
ctx.lineWidth=2;
ctx.fillStyle=white;
ctx.fillRect(10,-4,70,8);
ctx.strokeStyle=grayA;
ctx.fillStyle=grayA;
ctx.strokeRect(0,-4,80,8);
ctx.moveTo(80.7,-5.1);
ctx.lineTo(90,0);
ctx.lineTo(80.7,5.1);
ctx.fillRect(0,-4,10,8);
ctx.fill();
ctx.closePath();
//绘制圆轴
ctx.beginPath();
ctx.arc(0,0,6,0,2*Math.PI);
ctx.fillStyle=grayD;
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(0,0,2.5,0,2*Math.PI);
ctx.fillStyle=grayA;
ctx.fill();
ctx.closePath();
ctx.restore();
ctx.save();
//秒针
ctx.rotate(((2*Math.PI)/60)*second+((2*Math.PI)/60)*(millsecond/1000)-Math.PI/2);
ctx.beginPath();
ctx.shadowOffsetX=5;
ctx.shadowOffsetY=5;
ctx.shadowColor="rgba(0,0,0,0.2)";
ctx.shadowBlur=15;
ctx.moveTo(-30,0);
ctx.lineTo(90,0);
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.closePath();
ctx.stroke();
//绘制秒针尾部
ctx.beginPath();
ctx.arc(-30,0,5,0,2*Math.PI);
ctx.fillStyle=white;
ctx.fill();
ctx.closePath();
//绘制秒针中间圆形
ctx.shadowOffsetX=5;
ctx.shadowOffsetY=5;
ctx.shadowColor="rgba(0,0,0,0.2)";
ctx.shadowBlur=15;
ctx.beginPath();
ctx.arc(55,0,5.5,0,2*Math.PI);
ctx.fillStyle=white;
ctx.lineWidth=2;
ctx.strokeStyle=grayA;
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
ctx.restore();
ctx.restore();
}
/script
/body
/html
点击关注公众号,”技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线