写的时候遇到的下列几个问题:
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.widthconst cHeight = canvas.heightconsole.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) / 2const wid = cWidth / 2const hei = cHeight / 2const rad = random(0, 360) * Math.PI / 180const x = Math.cos(rad) * r + widconst y = Math.sin(rad) * r + heiconst size = random(1, 4)this.x = xthis.y = ythis.wid = widthis.hei = heithis.size = sizethis.r = r}drawArc() {draw(this.x, this.y, this.size)}move(duringX, duringY) {//总运动时间const speed = 500const x = this.xconst y = this.y//每毫秒运动的距离const xLen = duringX - xconst yLen = duringY - y//?这里之所以不用 当前位置 + 速度 = 下一次的位置// 而是用原来的位置+ 时间*速度 =当前的位置//是因为requestAnimationFrame并非是按照时间进行调用的,而是根据刷新率const xSpeed = xLen / speedconst ySpeed = yLen / speedconst time = (new Date()) - 0//_move,更改当前圆对象的 x坐标与y坐标const _move = () => {const now_time = (new Date()) - timethis.x = x + now_time * xSpeedthis.y = y + now_time * ySpeedif (now_time < speed) {requestAnimationFrame(_move)} else {this.x = duringXthis.y = duringY}}_move()}}//text 时间文本let text = null//返回当前日期 yyyy - MM - ddfunction 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_textctx.fillStyle = "#000"ctx.textBaseline = "middle"ctx.font = '100px serif'const wid = ctx.measureText(text).widthctx.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) * 4const 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>
代码是根据网络上的文章实现的(渡一),按照自己的思路又变化了一些