canvas學習
先上效果圖吧,最後大擺錘有幾點不會實現,就用其他的頂替了一下下,嘿嘿。大擺錘的gif我放到下面了哈
<div class="container">
<div class="box">
<div class="img1"></div>
<canvas id="canvas" width="375" height="650" style="margin: 0 auto;"></canvas>
<canvas id="canvas2" width="300" height="200"></canvas>
</div>
</div>
// 錶盤半徑
const R = 150
// 弧度
const deg = Math.PI / 180
// 計時器變量
let timer = null
var clockRadius = 250;
// 獲取canvas對象
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
// 園中心的xy坐標
// const centerPoint = [0, 0]
const centerPoint = [canvas.width / 2, 185]
console.log(canvas.width, canvas.height)
// 設置畫布的位置,重新映射畫布上的 (0,0) 位置
ctx.translate(centerPoint[0], centerPoint[1])
// 清除計時器
timer && clearInterval(timer)
// 繪製畫布
drawClock()
// 更新畫布
timer = setInterval(() => {
drawClock()
}, 1000);
// 繪製底部時鐘擺錘
drawBob()
// 獲取當前時間函數
function getNowTime() {
const date = new Date()
let [h, m, s] = [date.getHours(), date.getMinutes(), date.getSeconds()]
if (h > 12) {
h -= 12
}
return [h, m, s, date]
}
// 清除畫布
function clearDraw() {
ctx.clearRect(-centerPoint[0], -centerPoint[1], canvas.width, canvas.height)
}
// 繪製時鐘函數
function drawClock() {
// 首先清除畫布
clearDraw()
// 重新繪製畫布
// 獲取當前時間
const [h, m, s, date] = getNowTime()
// 創建漸變填充
const my_gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
my_gradient.addColorStop(0, "#afd");
my_gradient.addColorStop(1, "#adf");
ctx.fillStyle = my_gradient;
// 繪製矩形 開始的xy坐標,寬高
ctx.fillRect(-centerPoint[0], -centerPoint[1], canvas.width, canvas.height);
// 繪製圓形,並填充顔色
ctx.beginPath();
ctx.arc(0, 0, R, 0, 2 * Math.PI);
// 創建漸變
const gradient = ctx.createRadialGradient(0, 0, R / 6, 0, 0, R);
// gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "pink");
gradient.addColorStop("1.0", "white");
ctx.fillStyle = gradient
ctx.fill();
// 繪製園中心(指針中心)
ctx.beginPath()
ctx.arc(0, 0, 5, 0, 2 * Math.PI)
ctx.fillStyle = '#000'
ctx.fill()
// 繪製錶盤刻度
drawScale()
// 绘制表盘刻度数字
drawNum()
// 繪製日期文字
drawDate(date)
// 繪製指針 時針
drawPointer(.4, 5, 'black', h * 5)
// 繪製指針 分針
drawPointer(.46, 4, 'green', m)
// 繪製指針 秒針
drawPointer(.52, 3, 'red', s)
}
// 繪製錶盤刻度函數
function drawScale() {
for (let i = 1; i <= 60; i++) {
ctx.beginPath();
ctx.save();
ctx.rotate(6 * i * deg);
ctx.moveTo(0, -150);
// 區分刻度
if (i % 15 == 0) {
ctx.strokeStyle = '#afd';
ctx.lineWidth = 3;
ctx.lineTo(0, -135);
} else if (i % 5 == 0) {
ctx.strokeStyle = '#adf';
ctx.lineWidth = 2;
ctx.lineTo(0, -140);
} else {
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.lineTo(0, -145);
}
ctx.stroke();
ctx.restore();
ctx.closePath()
}
}
// 绘制表盘刻度数字函数
function drawNum() {
ctx.save()
// 創建漸變
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
// 設置字體
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (let n = 1; n <= 12; n++) {
const theta = (n - 3) * (Math.PI * 2) / 12;
if (n % 3 === 0) {
ctx.fillStyle = gradient;
} else {
ctx.fillStyle = '#000';
}
const x = clockRadius * 0.5 * Math.cos(theta);
const y = clockRadius * 0.5 * Math.sin(theta);
ctx.fillText(n, x, y);
}
ctx.restore()
}
// 绘制指针
function drawPointer(longRate, width, color, i = 0) {
ctx.save();
ctx.rotate((6 * i - 90) * deg);
ctx.beginPath();
ctx.fillStyle = color;
ctx.moveTo(-15, -width);
ctx.lineTo(-15, width);
ctx.lineTo(clockRadius * longRate, 1);
ctx.lineTo(clockRadius * longRate, -1);
ctx.fill();
ctx.restore();
}
// 繪製日期函數
function drawDate(date) {
ctx.save()
ctx.fillStyle = '#fff'
ctx.lineJoin = "round";
ctx.lineWidth = 140
ctx.fillRect(-55, 55, 110, 30)
// 創建漸變
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
// 設置字體
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = gradient
ctx.fillText(dateFormatter(date, 'yyyy年MM月dd日'), 0, 70)
ctx.restore()
}
// 繪製底部時鐘擺錘函數
function drawBob() {
const canvas = document.getElementById('canvas2')
const ctx = canvas.getContext('2d')
// 設置畫布的位置,重新映射畫布上的 (0,0) 位置
// ctx.translate(centerPoint[0], centerPoint[1])
ctx.save()
// 繪製方形空洞用來存放擺錘
// 創建漸變
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop("0", "rgba(255, 0, 0, .3)");
gradient.addColorStop("0.5", "rgba(0, 255, 0, .3)");
gradient.addColorStop("1.0", "rgba(0, 0, 255, .3)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, R * 2, 200)
// 绘制摆锤
// 绘制方形玻璃遮罩
// ...
ctx.restore()
}
// 日期格式化
function dateFormatter(str, fmt = 'yyyy-MM-dd hh:mm:ss') {
const date = new Date(str || Date.now());
const map = {
'y+': date.getFullYear(),
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (const key in map) {
if (Object.prototype.hasOwnProperty.call(map, key)) {
fmt = fmt.replace(new RegExp(key), (a, b, c) => {
// a: 匹配到的字符串,b:index索引,c:模板字符串
// console.log(a, b, c)
const target = String(map[key]);
if (a.length < target.length && a.length >= 2) {
return target.substr(-a.length);
}
return target.padStart(a.length, '0');
});
}
}
return fmt;
}
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
/* background: #cccccc; */
}
.container {
display: flex;
justify-content: center;
align-items: center;
}
.box {
position: relative;
margin: 0 auto;
}
#canvas2 {
position: absolute;
bottom: 30px;
left: 0;
right: 0;
margin: 0 auto;
}
.img1 {
width: 300px;
height: 200px;
position: absolute;
bottom: 30px;
left: 0;
right: 0;
margin: 0 auto;
background-size: cover;
background-position-x: -10px;
background-image: url(./a.gif);
}