写的时候遇到的下列几个问题:
1、api了解不深刻,如requestAnimationFrame
const fn =()=>{
//requestAnimationFrame(fn())//错误的写法
requestAnimationFrame(fn)//正确的写法
}
2、关于下面的clear函数(清除动画)与update(更新时间数据)函数,这两个函数都有用到requestAnimationFrame,因此我起初是打算将是合并为一个的,将clear放到update后面进行执行:
const clear =()=>{
//...
}
const update =()=>{
//...
clear()
//...
requestAnimationFrame(update)
}
update()
下面是我个人对这样写的看法:
(1)、虽然看起来是没有问题的,但是由于Demo中update函数中的获取画布像素颗粒循环次数较多,需要一定的时间,会延迟clear的执行。
(2)、获取你会想到将clear放到update最上面优先执行,这样的话就犯了最初级的错误,页面不会显示如何东西,在update中我们需要先获取画布颗粒,如果清除了,还怎么获得?
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>时钟</title>
<style>
* {
box-sizing: border-box;
}
.canvas {
border: 1px solid;
zoom: 0.5;
}
</style>
</head>
<body>
<canvas class="canvas" width="800" height="600"></canvas>
</body>
<script>
const random = (m, n) => {
return Math.random() * (n - m) + m
}
//拿到canvas标签
const canvas = document.querySelector(".canvas")
let ctx = canvas.getContext("2d", {
willReadFrequently: true
});
const cWidth = canvas.width
const cHeight = canvas.height
console.log(cWidth, cHeight);
//draw绘制圆的函数,传递x,y,size {x轴位置 , y轴位置 , size 圆半径}
const draw = (x, y, size) => {
ctx.beginPath();
//填充颜色
ctx.fillStyle = "#333"
//描边颜色
ctx.strokeStyle = "#333"
ctx.arc(x, y, size, 0 * Math.PI, 2 * Math.PI);
ctx.closePath();
//描边
ctx.stroke();
//填充
ctx.fill();
}
//构造圆圈对象
class circle {
constructor() {
const r = Math.min(cWidth, cHeight) / 2
const wid = cWidth / 2
const hei = cHeight / 2
const rad = random(0, 360) * Math.PI / 180
const x = Math.cos(rad) * r + wid
const y = Math.sin(rad) * r + hei
const size = random(1, 4)
this.x = x
this.y = y
this.wid = wid
this.hei = hei
this.size = size
this.r = r
}
drawArc() {
draw(this.x, this.y, this.size)
}
move(duringX, duringY) {
//总运动时间
const speed = 500
const x = this.x
const y = this.y
//每毫秒运动的距离
const xLen = duringX - x
const yLen = duringY - y
//?这里之所以不用 当前位置 + 速度 = 下一次的位置
// 而是用原来的位置+ 时间*速度 =当前的位置
//是因为requestAnimationFrame并非是按照时间进行调用的,而是根据刷新率
const xSpeed = xLen / speed
const ySpeed = yLen / speed
const time = (new Date()) - 0
//_move,更改当前圆对象的 x坐标与y坐标
const _move = () => {
const now_time = (new Date()) - time
this.x = x + now_time * xSpeed
this.y = y + now_time * ySpeed
if (now_time < speed) {
requestAnimationFrame(_move)
} else {
this.x = duringX
this.y = duringY
}
}
_move()
}
}
//text 时间文本
let text = null
//返回当前日期 yyyy - MM - dd
function getText() {
return new Date().toTimeString().substring(0, 8)
}
const list = []
function unpadte() {
const now_text = getText()
if (text == now_text) {
requestAnimationFrame(unpadte)
return
}
text = now_text
ctx.fillStyle = "#000"
ctx.textBaseline = "middle"
ctx.font = '100px serif'
const wid = ctx.measureText(text).width
ctx.fillText(text, (cWidth - wid) / 2, cHeight / 2)
const points = getPoints()
if (points.length < list.length) {
list.splice(points.length)
}
// clear()
for (let i = 0; i < points.length; i++) {
let p = list[i]
if (!p) {
p = new circle()
list.push(p)
}
const [x, y] = points[i]
p.move(x, y)
}
requestAnimationFrame(unpadte)
}
//clear,定时清除画布内容,实习动画效果的次要关键内容
const clear = () => {
ctx.clearRect(0, 0, cWidth, cHeight)
list.forEach(item => {
item.drawArc()
})
requestAnimationFrame(clear)
}
clear()
//获取画布所有像素坐标
const getPoints = () => {
const points = []
const {
width,
height,
data
} = ctx.getImageData(0, 0, canvas.clientWidth, canvas.clientHeight)
//gap表示间隔跨度
const gap = 3;
for (let i = 0; i < width; i += gap) {
for (let j = 0; j < height; j += gap) {
const index = (i + j * width) * 4
const r = data[index]
const g = data[index + 1]
const b = data[index + 2]
const a = data[index + 3]
if (r == 0 && g == 0 && b == 0 && a == 255) {
points.push([i, j])
}
}
}
return points
}
unpadte()
</script>
</html>
代码是根据网络上的文章实现的(渡一),按照自己的思路又变化了一些