<canvas> 是HTML中的一個元素,它可被用來通過 JavaScript(Canvas API 或 WebGL API)繪制圖形及圖形動畫。
Canvas API 提供了一個通過 JavaScript 和 HTML 的 <canvas> 元素來繪制圖形的方式。它可以用于動畫、游戲畫面、數(shù)據(jù)可視化、圖片編輯以及實時視頻處理等方面。
<canvas>標(biāo)簽本身沒有繪圖能力,它僅僅是圖形的容器。在HTML,一般通過Javascript語言來完成實際的操作。
本文通過Javascript操作Canvas制作一個簡單的顯示當(dāng)前時間的動畫時鐘,了解和學(xué)習(xí)簡單的canvas用法,僅以拋磚引玉。
首先創(chuàng)建一個HTML文件,為了方便管理,使用一個div標(biāo)簽包裹兩個canvas標(biāo)簽,并加上一些簡單的css樣式。
<!doctype html>
<html lang="zh-cn">
<head><title>Canvas繪制動畫時鐘</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>
本示例中使用了兩個canvas標(biāo)簽(為什么使用兩個,一個不是更簡單嗎?),一個用于繪制鐘面,一個根據(jù)當(dāng)前時間實時顯示和更新時針、分針和秒針的動態(tài)指向。好了,話不多說,開干。
一個簡單的時鐘,可以分為鐘面上的刻度和指針。其中刻度和12個數(shù)字是固定的,我們可以將它們繪制在當(dāng)作背景的canvas上(示例中id為clock-plate的canvas)。
(1)要使用canvas,首先必須通過容器獲取渲染上下文:
var $=function(id){return document.querySelector(id);}//這個函數(shù)只是為了方便獲取dom元素
const canvasbg=$("#clock-plate"),
canvas=$("#clock-ui"),
ctx = canvasbg.getContext("2d"),//背景容器上下文
ctxUI = canvas.getContext("2d");//指針容器上下文,后面代碼要用
//定義畫布寬度和高度,時鐘圓直徑,并設(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)畫鐘的邊框,為了好看,這里畫兩個圈:
//畫出時鐘外圓框
ctx.lineWidth = 12;
ctx.beginPath();
ctx.arc(oX, oY, r+14, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 8;
//畫出時鐘內(nèi)圓框(刻度圈)
ctx.beginPath();
ctx.arc(oX, oY, r, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
邊框效果圖
(3)繪制刻度線和數(shù)字,可以利用三角函數(shù)計算出每個刻度的坐標(biāo):
利用三角函數(shù)計算刻度線的坐標(biāo)位置
鐘面上有12個大格,從正上方12開始,它們的度數(shù)分別是270,300,330,0,30,60,90,120,150,180,210,240。然后利用JS的Math.sin和Math.cos分別計算出各大格的坐標(biāo)。注意:js中Math.sin()和Math.cos()的參數(shù)不是角度數(shù)是弧度。可以使用Math.PI/180*角度來轉(zhuǎn)化,比如將30度轉(zhuǎn)換成弧度=Math.PI/180*30
//繪制鐘表中心點
ctx.beginPath();
ctx.arc(oX, oY, 8, 0, 2 * Math.PI);//圓心
ctx.fill();
ctx.closePath();
//設(shè)置刻度線粗細(xì)度
ctx.lineWidth = 3;
//設(shè)置鐘面12個數(shù)字的字體、大小和對齊方式
ctx.font = "30px serif";
ctx.textAlign="center";
ctx.textBaseline="middle";
var kdx,kdy;
//繪制12個大刻度和12個數(shù)字
//為方便計算,先定義了0-11這12個刻度對應(yīng)的度數(shù),也可以直接定義對應(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();
//繪制刻度對應(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)前時間繪制指針
習(xí)慣上,時針粗短,分針略粗而長,秒針細(xì)長。為加大區(qū)別,示例中秒針細(xì)長并且繪制成紅色。
function drawHp(i){//繪制時針
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");
}
//抽取出繪制三種指針時共同的部分,注意指針繪制在渲染上下文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)前時間的時、分和秒,將根據(jù)它們的值確定指針的坐標(biāo)。不過,因為使用的是默認(rèn)的convas坐標(biāo)體系,0值實際指向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)的位置??纯葱Ч绾危?/p>
指針繪制情況與實際時間相符
貌似效果有了,截圖時電腦上的時間是10時17分,指針繪制上,時針指向10時,分針指向17。嗯,感覺有點別扭?對了,時針和分針怎么是端端正正地指向它們的整時整分刻度上呢?實際鐘表上時針和分針是展示動態(tài)進(jìn)度的,此時時針應(yīng)該越過10時的位置才對。沒關(guān)系,我們在繪制時針和分針時別點東西,讓它的角度值加上分針和秒針的值試試。
//修改后的繪制三種指針的方法
function drawHp(i,f,m){//繪制時針,參數(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繪制一個簡易時鐘就完成了。下面繼續(xù)優(yōu)化一下。剛才使用requestAnimationFrame方法即時更新繪制情況。這個方法與刷新率有關(guān),看看mdn上面怎么說的:
window.requestAnimationFrame() 方法會告訴瀏覽器你希望執(zhí)行一個動畫。它要求瀏覽器在下一次重繪之前,調(diào)用用戶提供的回調(diào)函數(shù)。
對回調(diào)函數(shù)的調(diào)用頻率通常與顯示器的刷新率相匹配。雖然 75hz、120hz 和 144hz 也被廣泛使用,但是最常見的刷新率還是 60hz(每秒 60 個周期/幀)。為了提高性能和電池壽命,大多數(shù)瀏覽器都會暫停在后臺選項卡或者隱藏的 <iframe> 中運行的 requestAnimationFrame()。
本示例中,更新指針的位置并不需要很高的刷新頻率,可以通過節(jié)流進(jìn)行一下優(yōu)化:
var fps = 5, fpsInterval = 1000 / fps,lastTime = new Date().getTime(); //記錄上次執(zhí)行的時間
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)然,實現(xiàn)時鐘的方法是很多,比如可以使用畫布的旋轉(zhuǎn)(rotate方法)來實現(xiàn)指針的動態(tài)轉(zhuǎn)動等等。
完整HTML+JS源碼:
移動設(shè)備的使用不斷增長的今天,移動優(yōu)先設(shè)計已經(jīng)成為前端開發(fā)的重要原則之一。移動優(yōu)先設(shè)計是一種思維方式,它要求開發(fā)者首先考慮移動設(shè)備的用戶體驗,然后再擴(kuò)展到桌面等大屏設(shè)備。這種方法有助于確保在小屏幕上提供核心功能和內(nèi)容,同時為大屏幕用戶提供增強(qiáng)的體驗。
移動優(yōu)先設(shè)計的實現(xiàn)通常依賴于媒體查詢(Media Queries),CSS的一項功能,它允許內(nèi)容的呈現(xiàn)根據(jù)設(shè)備的特定特征和參數(shù)來調(diào)整。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>移動優(yōu)先布局</title>
<style>
/* 基礎(chǔ)樣式:適用于移動設(shè)備 */
.container {
padding: 10px;
}
.menu {
background-color: #333;
overflow: hidden;
}
.menu a {
float: left;
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
/* 媒體查詢:適應(yīng)平板和桌面 */
@media (min-width: 600px) {
.menu a {
float: none;
display: inline-block;
}
}
</style>
</head>
<body>
<div class="container">
<div class="menu">
<a href="#home">首頁</a>
<a href="#news">新聞</a>
<a href="#contact">聯(lián)系我們</a>
<a href="#about">關(guān)于我們</a>
</div>
</div>
</body>
</html>
在這個例子中,我們?yōu)橐苿釉O(shè)備提供了一個基礎(chǔ)的導(dǎo)航菜單。當(dāng)屏幕寬度超過600px時,菜單項會水平排列,適應(yīng)平板和桌面設(shè)備。
<style>
img.responsive {
max-width: 100%;
height: auto;
}
</style>
<img src="image.jpg" alt="描述性文字" class="responsive">
在這個例子中,圖片會根據(jù)不同設(shè)備的屏幕寬度自動調(diào)整大小,確保在移動設(shè)備上不會超出屏幕。
有時候,我們希望只在桌面設(shè)備上加載某些資源。這可以通過JavaScript結(jié)合媒體查詢來實現(xiàn)。
<script>
if (window.matchMedia("(min-width: 800px)").matches) {
// 加載桌面特定的腳本或樣式
const desktopScript = document.createElement('script');
desktopScript.src = 'desktop.js';
document.head.appendChild(desktopScript);
}
</script>
在這個例子中,只有當(dāng)設(shè)備屏幕寬度至少為800px時,才會加載desktop.js。
移動優(yōu)先設(shè)計不僅僅是一種技術(shù)或策略,它是一種以用戶為中心的設(shè)計哲學(xué)。隨著移動設(shè)備的普及,它促使我們重新思考如何構(gòu)建我們的網(wǎng)站和應(yīng)用,以確保在任何設(shè)備上都能提供最佳體驗。通過實施移動優(yōu)先設(shè)計,我們可以確保網(wǎng)站的可訪問性、可用性和性能都能滿足用戶的期望。
下是一個 JavaScript 代碼段,它可以將傳入的字符串類型日期轉(zhuǎn)換為“剛剛”、“幾秒前”、“幾分鐘前”、“幾個小時前”、“昨天”、“前天”或“幾天前”等形式:
“想象一下,你正在設(shè)計一個社交網(wǎng)站的消息提示功能,需要將用戶發(fā)布的消息中的時間戳轉(zhuǎn)換為更加友好和易懂的時間表達(dá)方式。你可以使用下面這段 JavaScript 代碼來實現(xiàn)這個功能。無論是“剛剛”、“幾秒前”、“幾分鐘前”、“幾個小時前”、“昨天”、“前天”還是“幾天前”,這個函數(shù)都能夠準(zhǔn)確地將時間戳轉(zhuǎn)換為人性化的時間表達(dá)方式,讓用戶更加輕松地理解和使用你的社交網(wǎng)站。嘗試使用這個函數(shù)來提高你的網(wǎng)站用戶體驗吧!
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。