Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
<canvas> 是HTML中的一個(gè)元素,它可被用來通過 JavaScript(Canvas API 或 WebGL API)繪制圖形及圖形動(dòng)畫。
Canvas API 提供了一個(gè)通過 JavaScript 和 HTML 的 <canvas> 元素來繪制圖形的方式。它可以用于動(dòng)畫、游戲畫面、數(shù)據(jù)可視化、圖片編輯以及實(shí)時(shí)視頻處理等方面。
<canvas>標(biāo)簽本身沒有繪圖能力,它僅僅是圖形的容器。在HTML,一般通過Javascript語言來完成實(shí)際的操作。
本文通過Javascript操作Canvas制作一個(gè)簡(jiǎn)單的顯示當(dāng)前時(shí)間的動(dòng)畫時(shí)鐘,了解和學(xué)習(xí)簡(jiǎn)單的canvas用法,僅以拋磚引玉。
首先創(chuàng)建一個(gè)HTML文件,為了方便管理,使用一個(gè)div標(biāo)簽包裹兩個(gè)canvas標(biāo)簽,并加上一些簡(jiǎn)單的css樣式。
<!doctype html>
<html lang="zh-cn">
<head><title>Canvas繪制動(dòng)畫時(shí)鐘</title>
<style>
html,body{margin:0;padding:0}
#clockWrap {
position: relative;
}
canvas {
position: absolute;
}
#clock-ui {
z-index: 2;
}
#clock-plate {
z-index: 1;
}
</style>
</head>
<body>
<div id="clockWrap">
<canvas id="clock-plate"></canvas>
<canvas id="clock-ui"></canvas>
</div>
<script></script>
</body></html>
本示例中使用了兩個(gè)canvas標(biāo)簽(為什么使用兩個(gè),一個(gè)不是更簡(jiǎn)單嗎?),一個(gè)用于繪制鐘面,一個(gè)根據(jù)當(dāng)前時(shí)間實(shí)時(shí)顯示和更新時(shí)針、分針和秒針的動(dòng)態(tài)指向。好了,話不多說,開干。
一個(gè)簡(jiǎn)單的時(shí)鐘,可以分為鐘面上的刻度和指針。其中刻度和12個(gè)數(shù)字是固定的,我們可以將它們繪制在當(dāng)作背景的canvas上(示例中id為clock-plate的canvas)。
(1)要使用canvas,首先必須通過容器獲取渲染上下文:
var $=function(id){return document.querySelector(id);}//這個(gè)函數(shù)只是為了方便獲取dom元素
const canvasbg=$("#clock-plate"),
canvas=$("#clock-ui"),
ctx = canvasbg.getContext("2d"),//背景容器上下文
ctxUI = canvas.getContext("2d");//指針容器上下文,后面代碼要用
//定義畫布寬度和高度,時(shí)鐘圓直徑,并設(shè)置畫布大小
const oW=1000,oH=800,cW=400,r=cW/2,oX=oW/2,oY=oH/2;
canvas.width=oW;
canvas.height=oH;
canvasbg.width=oW;
canvasbg.height=oH;
(2)畫鐘的邊框,為了好看,這里畫兩個(gè)圈:
//畫出時(shí)鐘外圓框
ctx.lineWidth = 12;
ctx.beginPath();
ctx.arc(oX, oY, r+14, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 8;
//畫出時(shí)鐘內(nèi)圓框(刻度圈)
ctx.beginPath();
ctx.arc(oX, oY, r, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
邊框效果圖
(3)繪制刻度線和數(shù)字,可以利用三角函數(shù)計(jì)算出每個(gè)刻度的坐標(biāo):
利用三角函數(shù)計(jì)算刻度線的坐標(biāo)位置
鐘面上有12個(gè)大格,從正上方12開始,它們的度數(shù)分別是270,300,330,0,30,60,90,120,150,180,210,240。然后利用JS的Math.sin和Math.cos分別計(jì)算出各大格的坐標(biāo)。注意:js中Math.sin()和Math.cos()的參數(shù)不是角度數(shù)是弧度。可以使用Math.PI/180*角度來轉(zhuǎn)化,比如將30度轉(zhuǎn)換成弧度=Math.PI/180*30
//繪制鐘表中心點(diǎn)
ctx.beginPath();
ctx.arc(oX, oY, 8, 0, 2 * Math.PI);//圓心
ctx.fill();
ctx.closePath();
//設(shè)置刻度線粗細(xì)度
ctx.lineWidth = 3;
//設(shè)置鐘面12個(gè)數(shù)字的字體、大小和對(duì)齊方式
ctx.font = "30px serif";
ctx.textAlign="center";
ctx.textBaseline="middle";
var kdx,kdy;
//繪制12個(gè)大刻度和12個(gè)數(shù)字
//為方便計(jì)算,先定義了0-11這12個(gè)刻度對(duì)應(yīng)的度數(shù),也可以直接定義對(duì)應(yīng)的弧度。
const hd=Math.PI/180,degr=[270,300,330,0,30,60,90,120,150,180,210,240];
for(var i=0;i<12;i++){
kdx=oX+Math.cos(hd*degr[i])*(r-3);
kdy=oY+Math.sin(hd*degr[i])*(r-3);
ctx.beginPath();
ctx.arc(kdx, kdy, 6, 0, 2 * Math.PI);//畫圓形大刻度
ctx.fill();
//繪制刻度對(duì)應(yīng)的數(shù)字
ctx.strokeText(i==0? 12 : i,oX+Math.cos(hd*degr[i])*(r-24),oY+Math.sin(hd*degr[i])*(r-24));
ctx.closePath();
}
//繪制小刻度
ctx.lineWidth = 2;
for(var i=0;i<60;i++){
if(i % 5 == 0) continue;//跳過與刻度重疊的刻度
x0=Math.cos(hd*i*6);
y0=Math.sin(hd*i*6);
ctx.beginPath();
ctx.moveTo(oX+x0*(r-10), oY+y0*(r-10));
ctx.lineTo(oX+x0*r, oY+y0*r); //畫短刻度線
ctx.stroke();
ctx.closePath();
}
效果如圖:
鐘面效果圖
(4)根據(jù)當(dāng)前時(shí)間繪制指針
習(xí)慣上,時(shí)針粗短,分針略粗而長(zhǎng),秒針細(xì)長(zhǎng)。為加大區(qū)別,示例中秒針細(xì)長(zhǎng)并且繪制成紅色。
function drawHp(i){//繪制時(shí)針
const x0=Math.cos(hd*i*30),y0=Math.sin(hd*i*30);
drawPointer(oX,oY,oX+x0*(r-90),oY+y0*(r-90),10,"#000000");
}
function drawMp(i){//繪制分針
const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
drawPointer(oX,oY,oX+x0*(r-60),oY+y0*(r-60),5,"#000000");
}
function drawSp(i){//繪制秒針
const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
drawPointer(oX,oY,oX+x0*(r-20),oY+y0*(r-20),2,"#FF0000");
}
//抽取出繪制三種指針時(shí)共同的部分,注意指針繪制在渲染上下文ctxUI中
function drawPointer(ox,oy,tx,ty,width,color){
ctxUI.strokeStyle = color;
ctxUI.lineCap = "round";
ctxUI.lineWidth = width;
ctxUI.beginPath();
ctxUI.moveTo(ox, oy);
ctxUI.lineTo(tx,ty);
ctxUI.stroke();
ctxUI.closePath();
}
現(xiàn)在已經(jīng)有了繪制三種指針的方法,參數(shù)是當(dāng)前時(shí)間的時(shí)、分和秒,將根據(jù)它們的值確定指針的坐標(biāo)。不過,因?yàn)槭褂玫氖悄J(rèn)的convas坐標(biāo)體系,0值實(shí)際指向3的位置,需要小小的修正一下。
window.requestAnimationFrame(function fn(){
var d = new Date();
ctxUI.clearRect(0,0,oW,oH);
//度數(shù)從0開始,而0在3刻度(15分/秒位置),修正為全值減15,如果小于0則修正回來
var hour=d.getHours(),minute=d.getMinutes()-15,second=d.getSeconds()-15;
hour=hour>11? hour-15 : hour-3;
drawHp(hour>=0? hour : 12+hour);
drawMp(minute>=0? minute : 60+minute);
drawSp(second>=0? second : 60+second);
window.requestAnimationFrame(fn);
});
接下來,調(diào)用window.requestAnimationFrame,在其中繪制并更新指標(biāo)的位置。看看效果如何:
指針繪制情況與實(shí)際時(shí)間相符
貌似效果有了,截圖時(shí)電腦上的時(shí)間是10時(shí)17分,指針繪制上,時(shí)針指向10時(shí),分針指向17。嗯,感覺有點(diǎn)別扭?對(duì)了,時(shí)針和分針怎么是端端正正地指向它們的整時(shí)整分刻度上呢?實(shí)際鐘表上時(shí)針和分針是展示動(dòng)態(tài)進(jìn)度的,此時(shí)時(shí)針應(yīng)該越過10時(shí)的位置才對(duì)。沒關(guān)系,我們?cè)诶L制時(shí)針和分針時(shí)別點(diǎn)東西,讓它的角度值加上分針和秒針的值試試。
//修改后的繪制三種指針的方法
function drawHp(i,f,m){//繪制時(shí)針,參數(shù):時(shí),分,秒
const x0=Math.cos(hd*(i+(f/60)+(m/600))*30),y0=Math.sin(hd*(i+(f/60)+(m/600))*30);
drawPointer(oX,oY,oX+x0*(r-90),oY+y0*(r-90),10,"#000000");
}
function drawMp(i,f){//繪制分針,參數(shù),分,秒
const x0=Math.cos(hd*(i+(f/60))*6),y0=Math.sin(hd*(i+(f/60))*6);
drawPointer(oX,oY,oX+x0*(r-60),oY+y0*(r-60),5,"#000000");
}
function drawSp(i){//繪制秒針
const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
drawPointer(oX,oY,oX+x0*(r-20),oY+y0*(r-20),2,"#FF0000");
}
再來看看效果,嗯,立竿見影呀:
指針指向更合理了
到此為止,canvas繪制一個(gè)簡(jiǎn)易時(shí)鐘就完成了。下面繼續(xù)優(yōu)化一下。剛才使用requestAnimationFrame方法即時(shí)更新繪制情況。這個(gè)方法與刷新率有關(guān),看看mdn上面怎么說的:
window.requestAnimationFrame() 方法會(huì)告訴瀏覽器你希望執(zhí)行一個(gè)動(dòng)畫。它要求瀏覽器在下一次重繪之前,調(diào)用用戶提供的回調(diào)函數(shù)。
對(duì)回調(diào)函數(shù)的調(diào)用頻率通常與顯示器的刷新率相匹配。雖然 75hz、120hz 和 144hz 也被廣泛使用,但是最常見的刷新率還是 60hz(每秒 60 個(gè)周期/幀)。為了提高性能和電池壽命,大多數(shù)瀏覽器都會(huì)暫停在后臺(tái)選項(xiàng)卡或者隱藏的 <iframe> 中運(yùn)行的 requestAnimationFrame()。
本示例中,更新指針的位置并不需要很高的刷新頻率,可以通過節(jié)流進(jìn)行一下優(yōu)化:
var fps = 5, fpsInterval = 1000 / fps,lastTime = new Date().getTime(); //記錄上次執(zhí)行的時(shí)間
function runStep() {
requestAnimationFrame(runStep);
var d=new Date(),now = d.getTime()
var elapsed = now - lastTime;
if (elapsed > fpsInterval) {
ctxUI.clearRect(0,0,oW,oH);
lastTime = now - (elapsed % fpsInterval);
//度數(shù)從0開始,而0在3刻度(15分/秒位置),修正為全值-15,如果小于0則用60減回
var hour=d.getHours(),minute=d.getMinutes()-15,second=d.getSeconds()-15;//console.log(d.getSeconds(),second);
hour=hour>11? hour-15 : hour-3;
drawHp(hour>=0? hour : 12+hour,minute+15,second+15);
drawMp(minute>=0? minute : 60+minute,second+15);
drawSp(second>=0? second : 60+second);
}
}
runStep();
當(dāng)然,實(shí)現(xiàn)時(shí)鐘的方法是很多,比如可以使用畫布的旋轉(zhuǎn)(rotate方法)來實(shí)現(xiàn)指針的動(dòng)態(tài)轉(zhuǎn)動(dòng)等等。
完整HTML+JS源碼:
用canvas做的一個(gè)時(shí)鐘,先看效果
繪制時(shí)鐘圓盤,其實(shí)就是繪制一個(gè)圓,這個(gè)比較簡(jiǎn)單,代碼如下:
標(biāo)繪刻度的難點(diǎn)在于確定刻度點(diǎn)的位置,這需要用到一些的幾何知識(shí),圓弧和三角函數(shù)。
如上圖,要求圓上任意一點(diǎn)A的位置,通過三角函數(shù)可以推出:
x1=BC=cos(a)*AC
y1=BC=sin(a)*AC
其中AC就是半徑,是已知的
現(xiàn)在我們要來確定角a的度數(shù):
小時(shí)刻度:12個(gè)刻度,每一小時(shí)的角度為:360/12,弧度為:2π/12
分鐘刻度:60個(gè)刻度,每一分鐘的角度為:360/60,弧度為:2π/60
有了半徑和角度我們就可以算出坐標(biāo),這樣就可以繪制刻度了。注意js里面用的是弧度。
為了方便,我們用ctx.translate()把畫布原點(diǎn)重定向到了圓心,這改變了畫布的狀態(tài),為了不影響后續(xù)的繪制,每次繪完之后都要把畫布恢復(fù)為原來的狀態(tài),具體操作就是:重定向畫布原點(diǎn)之前,用ctx.save()保存畫布狀態(tài),繪制完成之后,用ctx.restore()恢復(fù)之前保存的畫布狀態(tài)。
//保存畫布當(dāng)前環(huán)境狀態(tài) ctx.save(); //重定向畫布原點(diǎn) ctx.translate(250,250); //繪制代碼...... //恢復(fù)之前保存的狀態(tài) ctx.restore();
繪制小時(shí)刻度代碼
同理,繪制分鐘刻度就簡(jiǎn)單,把12換成60就可以了。
繪制分鐘刻度代碼
時(shí)針、分針、秒針都是和當(dāng)前時(shí)間有關(guān),所以先要獲取當(dāng)前時(shí)間的小時(shí)數(shù)、分鐘數(shù)、秒數(shù)。
//得到當(dāng)前時(shí)間 var now=new Date(); //當(dāng)前小時(shí)數(shù) var hour=now.getHours(); //當(dāng)前分鐘數(shù) var minute=now.getMinutes(); //當(dāng)前秒數(shù) var second=now.getSeconds(); hour=hour+minute/60;
畫時(shí)針時(shí),我們先畫出指向12點(diǎn)的時(shí)針:
//指向12點(diǎn)的時(shí)針 ctx.beginPath(); ctx.lineWidth=8; ctx.lineCap="round"; ctx.moveTo(0,10); ctx.lineTo(0,-120); ctx.stroke()
之后每一個(gè)時(shí)刻度點(diǎn)的時(shí)針就是把指向12點(diǎn)的時(shí)針旋轉(zhuǎn)一定的度數(shù):
//旋轉(zhuǎn)度數(shù) ctx.rotate(hour*2*Math.PI/12);
繪制時(shí)針代碼
同理,時(shí)針繪制好之后,分針和秒針也就簡(jiǎn)單,直接看代碼。
繪制分針代碼
繪制秒針代碼
為了好看我們?cè)佼嬕粋€(gè)中心點(diǎn)。
繪制中心點(diǎn)代碼
最后,為了讓時(shí)鐘實(shí)時(shí)轉(zhuǎn)動(dòng),需要加一個(gè)定時(shí)任務(wù),每秒執(zhí)行一次。
//一秒鐘執(zhí)行一次 setInterval(clock,1000);
還要注意,每次重繪都要清除畫布:
ctx.clearRect(0,0,canvas.width,canvas.height);
最終完整的代碼邏輯如下:
這樣一個(gè)時(shí)鐘就完成了,如果想看更詳細(xì)的操作,推薦觀看視頻版。
家我呀,我是yangyang,此刻看到一個(gè)小特效,是國(guó)外一哥們做的,特來分享給大家.
數(shù)字時(shí)代,數(shù)字發(fā)光時(shí)鐘體現(xiàn)了形式與功能的融合。在這篇文章中,我們將深入研究如何使用 HTML、CSS 和 JavaScript 來構(gòu)建一個(gè)。
構(gòu)建一個(gè)結(jié)構(gòu)化基礎(chǔ),其中包含小時(shí)、分鐘、秒和可選的 AM/PM 指示器元素。可訪問性至關(guān)重要,因此我們將使用語義標(biāo)簽并提供有意義的描述。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Digital clock</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="hero">
<div class="box">
<style></style>
<div class="clock">
<span id="hrs">00</span>
<span>:</span>
<span id="min">00</span>
<span>:</span>
<span id="sec">00</span>
</div>
</div>
</div>
<script src="js/index.js"></script>
</body>
</html>
利用 box-shadow 和 text-shadow 等屬性,我們將為時(shí)鐘注入活力。通過微調(diào)顏色和發(fā)光強(qiáng)度,我們將確保它吸引用戶。
* {
margin: 0;
padding: 0;
font-family: "Poppins", sans-serif;
box-sizing: border-box;
}
.hero {
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #2f363e;
position: relative;
}
.box {
position: relative;
width: 800px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.clock {
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(40px);
color: #6e7f92f6;
z-index: 10;
}
.clock span {
font-size: 80px;
width: 110px;
display: inline-block;
text-align: center;
position: relative;
}
.clock span::after {
font-size: 16px;
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
}
#hrs::after {
content: "HOURES";
color: #0f0;
font-weight: 600;
margin-bottom: -10px;
}
#min::after {
content: "MINS";
color: #0ff;
font-weight: 600;
margin-bottom: -10px;
}
#sec::after {
content: "SEC";
color: #ff0;
font-weight: 600;
margin-bottom: -10px;
}
/*------Anemated Border---------*/
.box::before {
content: "";
position: absolute;
inset: 0;
background: repeating-conic-gradient(
from var(--a),
#0f0,
#ff0,
#0ff,
#f0f,
#0ff
);
border-radius: 20px;
animation: rotate 6s linear infinite;
}
.box::after {
content: "";
position: absolute;
inset: 0;
background: repeating-conic-gradient(
from var(--a),
#0f0,
#ff0,
#0ff,
#f0f,
#0ff
);
border-radius: 20px;
animation: rotate 4s linear infinite;
filter: blur(40px);
opacity: 0.75;
}
.box style {
position: absolute;
inset: 4px;
background: #2f363e;
border-radius: 16px;
color: #ff0;
font-size: 20px;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
}
@property --a {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@keyframes rotate {
0% {
--a: 0;
}
0% {
--a: -360deg;
}
}
JavaScript 為我們的時(shí)鐘注入了活力,實(shí)現(xiàn)了小時(shí)、分鐘和秒的實(shí)時(shí)更新。我們還將考慮添加時(shí)區(qū)支持和用戶偏好的自定義選項(xiàng)等功能。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。