Clock
/*
年(y)可以用 1-4 个占位符
月(M)、日(d)、时(H,24时)、时(h,12时)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符
毫秒(S)只能用 1 个占位符(是 1-3 位数字)
AM或PM只能用 1 个占位符(是 2 位英文)
上午或下午(T)只能用 1 个占位符(是 2 位中文)
星期(E)可以用 1-3 个占位符
季度(q)只能用 1 个占位符(是 1 位数字)
*/
Date.prototype.format =function(fmt) {
varmap = {
"M+":this.getMonth() + 1,//月
"d+":this.getDate(),//日
"H+":this.getHours(),//24时
/*
上午12时只代表当天上午的12时,下午的12时代表当天下午的12时,
0时代表第二天的开始,即前面一天12时已过0时开始计算新一天的时间,
虽然说时间上跟前面那一天下午12时重合,但日期已经发生更改,所以不能说0时就是12时
*/
"h+":this.getHours()%12 == 0 ? 12 :this.getHours()%12,//12时
"m+":this.getMinutes(),//分
"s+":this.getSeconds(),//秒
"S":this.getMilliseconds(),//毫秒
"t":this.getHours() < 12 ?"AM":"PM",
"T":this.getHours() < 12 ?"上午":"下午",
};
varweek = {
"0":"日",
"1":"一",
"2":"二",
"3":"三",
"4":"四",
"5":"五",
"6":"六",
}
varquarter = {
"0":"一",
"1":"二",
"2":"三",
"3":"四",
}
if(/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
}
if(/(E+)/.test(fmt)) {
varweekPreStr;
switch(RegExp.$1.length) {
case1:
weekPreStr ="";
break;
case2:
weekPreStr ="周";
break;
default:
weekPreStr ="星期";
break;
}
fmt = fmt.replace(RegExp.$1, weekPreStr + week[this.getDay()]);
}
if(/(q)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, quarter[Math.floor(this.getMonth() / 3)]);
}
for(varkeyinmap) {
if(newRegExp("("+ key +")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? map[key] : ("00"+ map[key]).substr((map[key]+"").length));
}
}
returnfmt;
}
varcanvas = document.createElement("canvas");
document.body.appendChild(canvas);
varctx = canvas.getContext("2d");
varhalfPI = Math.PI / 2;
vardoublePI = Math.PI * 2;
//阴影级别
varshadowBlur = 10;
//阴影宽度
varshadow;
//阴影在X方向上的偏移
varshadowOffsetX = 5;
//阴影在Y方向上的便宜
varshadowOffsetY = 5;
//深色阴影
varshadowDarkColor ="rgba(0,0,0,0.8)";
//浅色阴影
varshadowLightColor ="rgba(0,0,0,0.1)";
//画布中心到边缘的内切圆半径
varcanvasRadius = 250;
canvas.line number106 index105 alt1"> canvas.line number107 index106 alt2"> //获取画布中心的坐标
varcx = canvasRadius;
varcy = canvasRadius;
//时钟外圈的贝塞尔花纹个数
varbezierPatternCount = 36;
//时钟外圈的贝塞尔花纹波峰处半径
varbezierPeakRadius = canvasRadius - 10;
//时钟外圈的贝塞尔花纹一半的角度
varbezierHalfSpan = doublePI / bezierPatternCount / 2;
//时钟外圈的贝塞尔花纹底部半径
varbezierRadius = bezierPeakRadius - 20;
//时钟外圈的贝塞尔花纹颜色
varbezierPatternColor ="Plum";
//时钟外圈半径
varclockBorderRadius = bezierRadius - 10;
//时钟外圈宽度
varclockBorder;
//时钟外圈颜色
varclockBorderColor ="Aqua";
//时钟外圈阴影半径
varclockBorderShadowRadius = clockBorderRadius - shadowWidth + 1;
//时钟整数时间刻度线宽
varclockScale;
//时钟整数时间刻度外半径
varclockScaleOuterRadius = clockBorderRadius - shadowWidth;
//时钟整数时间刻度内半径
varclockScaleInnerRadius = clockScaleOuterRadius - 20;
//时钟刻度颜色
varclockScaleColor ="Black";
//时钟非整数时间处半径
varclockScaleMiddleRadius = clockScaleOuterRadius - 10;
//时钟数字半径
varclockNumRadius = clockBorderShadowRadius - 40;
//时钟数字字体
varclockNumFont ="25px Arial";
//时钟数字颜色
varclockNumColor ="black";
//数字日期距中心的垂直距离
vardigitalDateMarginCenter = 50;
//数字日期颜色
vardigitalDateColor ="Black";
//数字日期字体
vardigitalDateFont ="bold 18px Arial";
//数字时间距中心的垂直距离
vardigitalTimeMarginCenter = 100;
//数字时间颜色
vardigitalTimeColor ="white";
//数字时间背景颜色
vardigitalTimeBgColor ="DarkSlateBlue";
//数字时间字体
vardigitalTimeFont ="bold 25px Arial";
//数字时间高度的一半
vardigitalTime;
//数字时间分隔线宽度
vardigitalTimeSpanLine;
//时钟中心点内圆的半径
varclockCenterInnerDotRadius = 7;
//时钟中心点内圆的颜色
varclockCenterInnerDotColor ="FireBrick";
//时钟中心点外圆的半径
varclockCenterOuterDotRadius = 10;
//时钟中心点外圆的颜色
varclockCenterOuterDotColor ="Maroon";
//时针线宽
varclockNeedle;
//时针半径
varclockHourNeedleRadius = clockBorderShadowRadius - 120;
//时针颜色
varclockHourNeedleColor ="DarkGreen";
//分针半径
varclockMinuteNeedleRadius = clockBorderShadowRadius - 80;
//分针颜色
varclockMinuteNeedleColor ="DarkSlateGray";
//秒针半径
varclockSecondNeedleRadius = clockBorderShadowRadius - 40;
//秒针尾部半径
varclockSecondNeedleBottomRadius = -20;
//秒针颜色
varclockSecondNeedleColor ="FireBrick";
//画圆环
functionstrokeCircle(cx, cy, r) {
ctx.beginPath();
ctx.arc(cx, cy, r, 0, doublePI);
ctx.stroke();
}
//画圆
functionfillCircle(cx, cy, r) {
ctx.beginPath();
ctx.arc(cx, cy, r, 0, doublePI);
ctx.fill();
}
//绘制线条
functionstrokeLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
//根据角度和半径计算圆上相应位置的坐标(最右侧为起始角度,顺时针方向为正)
functioncirclePos(cx, cy, theta, radius) {
varpos = {
x: cx + radius * Math.cos(theta),
y: cy + radius * Math.sin(theta),
};
returnpos;
}
//在圆环上绘制刻度线
functionstrokeCircleLine(cx, cy, theta, r1, r2) {
varpos1 = circlePos(cx, cy, theta, r1);
varpos2 = circlePos(cx, cy, theta, r2);
strokeLine(pos1.x, pos1.y, pos2.x, pos2.y);
}
//设置默认阴影
functionsetShadow(type) {
ctx.lineline number222 index221 alt1"> ctx.shadowBlur = shadowBlur;
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
if(type === 1) {
ctx.shadowColor = shadowLightColor;
}else{
ctx.shadowColor = shadowDarkColor;
}
}
//取消阴影
functionclearShadow() {
ctx.shadowColor ="rgba(0,0,0,0)";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
//绘制时钟外圈的贝塞尔花纹
functionrenderBezierPattern() {
ctx.fillStyle = bezierPatternColor;
ctx.beginPath();
vartheta = 0;
//由于circlePos是顺时针方向正, 故圈也是顺时针方向
varbeginPos = circlePos(cx, cy, theta, bezierRadius);
ctx.moveTo(beginPos.x, beginPos.y);
while(theta < doublePI) {
//贝塞尔曲线控制点
varcontrolTheta = theta + bezierHalfSpan;
varcontrolPos = circlePos(cx, cy, controlTheta, bezierPeakRadius);
//贝塞尔曲线终止点
varendTheta = controlTheta + bezierHalfSpan;
varendPos = circlePos(cx, cy, endTheta, bezierRadius);
ctx.quadraticCurveTo(controlPos.x, controlPos.y, endPos.x, endPos.y);
theta = endTheta;
}
//绘制圆counterclockwise=false, 即默认是顺时针方向
ctx.arc(cx, cy, clockBorderRadius, 0, doublePI,true);
//注意: 两个相反方向的路径内部为填充范围
ctx.fill();
}
//绘制时钟边框
functionrenderClockBorder() {
//画外框
ctx.strokeStyle = clockBorderColor;
ctx.lineline number266 index265 alt1"> strokeCircle(cx, cy, clockBorderRadius);
//画外框的内阴影
ctx.strokeStyle = shadowLightColor;
setShadow(1);
strokeCircle(cx, cy, clockBorderShadowRadius);
clearShadow();
}
//绘制时钟圆周上的数字和刻度部分
functionrenderClockNums() {
ctx.textAlign ="center";
ctx.textBaseline ="middle";
ctx.font = clockNumFont;
varspan = doublePI / 60;
for(vari = 1, radian = -halfPI + span; i <= 60; i++, radian += span) {
if(i % 5 == 0) {
//绘制刻度
ctx.strokeStyle = clockScaleColor;
ctx.lineline number284 index283 alt1"> strokeCircleLine(cx, cy, radian, clockScaleInnerRadius, clockScaleOuterRadius);
//绘制数字
varpos = circlePos(cx, cy, radian, clockNumRadius);
varnum = i / 5;
ctx.fillStyle = clockNumColor;
ctx.fillText(num, pos.x, pos.y);
}else{
ctx.strokeStyle = clockScaleColor;
ctx.lineline number293 index292 alt2"> strokeCircleLine(cx, cy, radian, clockScaleMiddleRadius, clockScaleOuterRadius);
}
}
}
//绘制数字时钟
functionrenderDigital(date) {
//绘制日期
ctx.textAlign ="center";
ctx.textBaseline ="middle";
ctx.font = digitalDateFont;
ctx.fillStyle = digitalDateColor;
vartext = date.format("yyyy年MM月dd日 EEE");
ctx.fillText(text, cx, cy + digitalDateMarginCenter);
//绘制时间
ctx.font = digitalTimeFont;
text = date.format(" HH mm ss ");
ctx.fillStyle = digitalTimeBgColor;
vartextline number311 index310 alt2"> vartextBgX = cx - textWidth / 2;
vartextBgY = cy + digitalTimeMarginCenter - digitalTimeHeight / 2;
ctx.fillRect(textBgX, textBgY, textWidth, digitalTimeHeight);
ctx.fillStyle = digitalTimeColor;
ctx.fillText(text, cx, cy + digitalTimeMarginCenter);
//绘制事件中间的分隔线
ctx.lineline number318 index317 alt1"> ctx.strokeStyle = digitalTimeColor;
vartextSpan = textWidth / 6;
varleftLineX = cx - textSpan;
strokeLine(leftLineX, textBgY, leftLineX, textBgY + digitalTimeHeight);
varrightLineX = cx + textSpan;
strokeLine(rightLineX, textBgY, rightLineX, textBgY + digitalTimeHeight);
}
//绘制时钟中心最下方红点
functionrenderClockCenterOuterDot() {
ctx.fillStyle = clockCenterOuterDotColor;
fillCircle(cx, cy, clockCenterOuterDotRadius);
}
//绘制时钟中心最上方红点
functionrenderClockCenterInnerDot() {
ctx.fillStyle = clockCenterInnerDotColor;
fillCircle(cx, cy, clockCenterInnerDotRadius);
}
//绘制时钟指针
functionrenderClockNeedle(date) {
varhourRadian = date.getHours() % 12 / 12 * doublePI - halfPI;
varminuteRadian = date.getMinutes() / 60 * doublePI - halfPI;
varsecondRadian = date.getSeconds() / 60 * doublePI - halfPI;
setShadow();
ctx.lineCap ="round";
ctx.lineline number343 index342 alt2"> ctx.strokeStyle = clockHourNeedleColor;
strokeCircleLine(cx, cy, hourRadian, 0, clockHourNeedleRadius);
ctx.strokeStyle = clockMinuteNeedleColor;
strokeCircleLine(cx, cy, minuteRadian, 0, clockMinuteNeedleRadius);
ctx.strokeStyle = clockSecondNeedleColor;
strokeCircleLine(cx, cy, secondRadian, clockSecondNeedleBottomRadius, clockSecondNeedleRadius);
ctx.lineCap ="square";
clearShadow();
}
functionrender(date) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
renderBezierPattern();
renderClockBorder();
renderClockNums();
renderDigital(date);
renderClockCenterOuterDot();
renderClockNeedle(date);
renderClockCenterInnerDot();
}
varlastTime = 0;
varaudio = document.getElementById("ticktock");
functionloop() {
vardate =newDate();
varcurrentTime = date.getTime();
if(currentTime - lastTime >= 1000) {
lastTime = currentTime;
//注意:这里设0非常关键,否则虽然会循环播放,但会从上一次暂停的地方开始播放,造成延迟
audio.currentTime = 0;
audio.play();
render(date);
}
requestAnimationFrame(loop);
}
loop();