
如上图,也是博客首页右侧模块中的一个模块。这是一个由js+canvas的动态时钟效果,是不是很炫酷呢,实现原理其实很简单。
点阵数字
首先,定义一个三维数组digit,它是0到9以及冒号的二维点阵,每个数字都是4*7大小的二维数组,通过遍历二维数组,当值为1时绘制圆形。
如图:
所以0-9以及冒号如下所定义:
var digit =
[
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//0
[
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//1
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1]
],//2
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//3
[
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//4
[
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//5
[
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//6
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//7
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//8
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//9
[
[0],
[0],
[1],
[0],
[1],
[0],
[0]
],//:
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],//null它们的下标值就是数字,10就是冒号“:”,11就是null;
数字渲染:
通过for二次循环,将数字值为1的时候,进行canvas绘制。注意,要把握好点阵粒子的位置,
function showDigitCtx(num, posx = 0, poxy = 0, bc = '#ddd', color = 'green') {
for (var i = 0; i < 7; i++) {
for (var j = 0; j < (num===10?1:4); j++) {
ctx.beginPath();
ctx.arc(posx+j*9,poxy+i*9,R,0,Math.PI*2,true);
ctx.fillStyle=digit[num][i][j] === 1?color:bc;
ctx.closePath();
ctx.fill();
}
}
}参数分为别数字,点阵数字起始x坐标和y坐标。背景色和数字颜色。如果符号为冒号,点阵数组变1列减省空间。
时间数据:
通过new Date()获取当前时间的时分秒,以及冒号,存储在newdate数组里:
function showTime() {
var newdate = [];
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
newdate.push(Math.floor(h / 10), h % 10, 10, Math.floor(m / 10), m % 10, 10, Math.floor(s / 10), s % 10);
showDigitGroup(newdate, 0, 0);
}
展示时钟:
时间获取到了,下面就是展示时钟了,先看代码:
function showDigitGroup(d, x, y) {
var xx = x;
var changedate = [];
//console.log(date.length)
for (var i = 0; i < d.length; i++) {
//console.log(xx);
var dd = d[i];
if (dd === 0 && i === 0) {
dd = 11;
}
showDigitCtx(dd, xx, y);
if (d[i] === 10) {
xx += 15;
} else {
xx += 45;
}
}
// console.log(d +'--'+date)
for (var k = 0; k < d.length; k++) {
if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
changedate.push(k + '#' + date[k]);
}
}
changedates(changedate);
date = [];
date = d.concat();
}传入的参数有d时间,数字矩阵起始的相对坐标。
遍历newdate数组,如果小时的个位数是0的话,那就不显示,用digit[11]的null显示,为了将数字时钟清晰的显示,数字之间要控制好间距也就是显示的位置了,每个数字宽40px,冒号宽10px,数字间间距5px。根据冒号判断,如果有冒号,后一位就+15px,否则+45px。
将当前时间与上一次时间(date数组)比较,将不同的时间数据存入changedates中,以便后面的例子特效。
最后通过定时器间隔来刷新每一秒的时间。
粒子特效:
粒子弹跳效果就是一个随机的抛物线,利用canvas实现小球作随机抛物线运动。将小球拆分为x,y轴,通过随机数组让小球作随机运动,y轴上,当小球落地时,弹跳起来,控制好减速系数,如图:

小球开始向上或者向下都是随机的,向左向右也是随机的,但是因为左边空间大,所以控制小球向左几率大一些。
时间改变:
如何触发粒子特效呢,当时钟的数字刷新一次,就会把当前数字上的粒子小球作分散随机运动。比如:秒数的个位是每一秒都刷新的,秒数的十位是十秒刷新一次等等,因此,每一秒的秒的个位都会触发粒子特效。再例如23:59:59时候,下一秒就是0:00:00了,每一位的数字都改变了,所以都触发了。那么这些数据怎么来呢?我们知道,刚刚有个changedates数组,里面就存放了当前时间相对于上一秒的变化,以及变化数字的位置。通过“#”隔开。
for (var k = 0; k < d.length; k++) {
if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
changedate.push(k + '#' + date[k]);
}
}date就是存放上一秒的数据。然后将这些数据(位置+数字)取用for出来,为小球特效做准备。
function changedates(changedate) {
var t = []
for (var i = 0; i < changedate.length; i++) {
// console.log(changedate[i]);
t = changedate[i].split('#');
// console.log(t)
doballs(t[0], t[1], 0, 0);
}
}
function doballs(index, num, x, y) {
var xx = x, yy = y;
switch (Number(index)) {
case 0:
xx = x;
break;
case 1:
xx = x + 45;
break;
case 2:
xx = x + 90;
break;
case 3:
xx = x + 105;
break;
case 4:
xx = x + 150;
break;
case 5:
xx = x + 195;
break;
case 6:
xx = x + 210;
break;
case 7:
xx = x + 255;
break;
}
var ramNumY=[1,2,3,4];
var ramNumX=[1,2,3,4];
var t=[-1,1];
for (var i=2;i<12;i++){
ramNumX.push(i*-1);
}
var tt=t[Math.floor(Math.random()*t.length)];
for (var i = 0; i < 7; i++) {
for (var j = 0; j < 4; j++) {
if (digit[num][i][j]===1){
var ball = {
x: xx+j*9,
y: yy+i*9,
stepX: 0.8*(ramNumX[Math.floor(Math.random()*ramNumX.length)]),
stepY: -4*(ramNumY[Math.floor(Math.random()*ramNumY.length)]),
k: 0.6,//摩擦系数
lastY:tt===1?Math.random()*(H-30)+30:Math.random()*(yy+i*9-R)+R,//小球向上边界
t:tt//小球方向
}
balls.push(ball);
}
}
}
// console.log(balls )
}doballs()方法是小球弹跳的初始数据,x和y:小球粒子坐标,stepx和stepy:定时器更新后的运动步长,k:摩擦系数或者说减速系数,lasty:小球向上的边界,每一次弹跳控制好随机的上边界,以至于每一次都不一样,tt:小球的方向,向上还是向下,也是随机控制的。
最后就是每一秒的更新,小球的变化方法:
function updateballs() {
for (var i = 0; i < balls.length; i++) {
// if (balls[i].y === H - R) {
// balls[i].y = balls[i].lastY ;
// balls[i].x= balls[i].x+balls[i].stepX*10;
//
//
// }
balls[i].x = balls[i].x + balls[i].stepX ;
balls[i].y = balls[i].y + balls[i].stepY ;
if (balls[i].t===1){
balls[i].stepY = balls[i].stepY * balls[i].k +2;
}else if(balls[i].t===-1){
balls[i].stepY = balls[i].stepY * balls[i].k -2;
}
if (balls[i].y > H - R) {
balls[i].y =H-R;
balls[i].stepY = -balls[i].stepY* balls[i].k;
balls[i].t=-1;
}else if (balls[i].y < R) {
balls[i].y =R;
balls[i].stepY = -balls[i].stepY* balls[i].k;
console.log(balls[i].stepY)
balls[i].t=1;
}
if (Math.abs(balls[i].x )> W - R ||Math.abs(balls[i].x )<R*1.5) {
balls.splice(i,1);
i--;
}
}
}
上面的两个if组控制小球的y轴方向,下面的两个if控制边界,小球触碰上边界或小边界反弹,最后一个控制小球的消失,当小球出了左右边界时,数组就删除出去小球的数据,不至于死循环。
最后完整代码:
<canvas id="canvas" width="310" height="150" ></canvas>
<script>
var digit =
[
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//0
[
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//1
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1]
],//2
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//3
[
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//4
[
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//5
[
[1, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//6
[
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1]
],//7
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1]
],//8
[
[1, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[1, 1, 1, 1]
],//9
[
[0],
[0],
[1],
[0],
[1],
[0],
[0]
],//:
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],//null
];
var date = [];
var balls = [];
var canvas = document.getElementById('canvas');
$(function () {
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
// date.push(Math.floor(h / 10), h % 10,10, Math.floor(m / 10), m % 10,10, Math.floor(s / 10), s % 10);
});
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.translate(5,15)
var W = 300;
var H = 130;
var R = 3.5;
showTime();
clearInterval(inter);
var inter = setInterval(function () {
render();
updateballs();
showTime();
}, 16);
}
function render() {
ctx.clearRect(0,0,W,H)//清除原始图形
for (var i = 0; i < balls.length; i++) {
ctx.beginPath();
ctx.arc(balls[i].x, balls[i].y, R, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.closePath();
ctx.fill();
}
}
function showTime() {
var newdate = [];
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
// console.log(Math.floor(h/10)+" "+m%10+" "+s)
newdate.push(Math.floor(h / 10), h % 10, 10, Math.floor(m / 10), m % 10, 10, Math.floor(s / 10), s % 10);
showDigitGroup(newdate, 0, 0);
//showDigitGroup(1,2,3,4,5,6)131654
}
function showDigitGroup(d, x, y) {
var xx = x;
var changedate = [];
//console.log(date.length)
for (var i = 0; i < d.length; i++) {
//console.log(xx);
var dd = d[i];
if (dd === 0 && i === 0) {
dd = 11;
}
showDigitCtx(dd, xx, y);
if (d[i] === 10) {
xx += 15;
} else {
xx += 45;
}
}
// console.log(d +'--'+date)
for (var k = 0; k < d.length; k++) {
if (d[k] != date[k] && d[k] != undefined && date[k] != undefined) {
changedate.push(k + '#' + date[k]);
}
}
changedates(changedate);
date = [];
date = d.concat();
}
function changedates(changedate) {
var t = []
for (var i = 0; i < changedate.length; i++) {
// console.log(changedate[i]);
t = changedate[i].split('#');
// console.log(t)
doballs(t[0], t[1], 0, 0);
}
}
function doballs(index, num, x, y) {
var xx = x, yy = y;
switch (Number(index)) {
case 0:
xx = x;
break;
case 1:
xx = x + 45;
break;
case 2:
xx = x + 90;
break;
case 3:
xx = x + 105;
break;
case 4:
xx = x + 150;
break;
case 5:
xx = x + 195;
break;
case 6:
xx = x + 210;
break;
case 7:
xx = x + 255;
break;
}
var ramNumY=[1,2,3,4];
var ramNumX=[1,2,3,4];
var t=[-1,1];
for (var i=2;i<12;i++){
ramNumX.push(i*-1);
}
var tt=t[Math.floor(Math.random()*t.length)];
for (var i = 0; i < 7; i++) {
for (var j = 0; j < 4; j++) {
if (digit[num][i][j]===1){
var ball = {
x: xx+j*9,
y: yy+i*9,
stepX: 0.8*(ramNumX[Math.floor(Math.random()*ramNumX.length)]),
stepY: -4*(ramNumY[Math.floor(Math.random()*ramNumY.length)]),
k: 0.6,//摩擦系数
lastY:tt===1?Math.random()*(H-30)+30:Math.random()*(yy+i*9-R)+R,//小球向上边界
t:tt//小球方向
}
balls.push(ball);
}
}
}
// console.log(balls )
}
function updateballs() {
for (var i = 0; i < balls.length; i++) {
// if (balls[i].y === H - R) {
// balls[i].y = balls[i].lastY ;
// balls[i].x= balls[i].x+balls[i].stepX*10;
//
//
// }
balls[i].x = balls[i].x + balls[i].stepX ;
balls[i].y = balls[i].y + balls[i].stepY ;
if (balls[i].t===1){
balls[i].stepY = balls[i].stepY * balls[i].k +2;
}else if(balls[i].t===-1){
balls[i].stepY = balls[i].stepY * balls[i].k -2;
}
if (balls[i].y > H - R) {
balls[i].y =H-R;
balls[i].stepY = -balls[i].stepY* balls[i].k;
balls[i].t=-1;
}else if (balls[i].y < R) {
balls[i].y =R;
balls[i].stepY = -balls[i].stepY* balls[i].k;
console.log(balls[i].stepY)
balls[i].t=1;
}
if (Math.abs(balls[i].x )> W - R ||Math.abs(balls[i].x )<R*1.5) {
balls.splice(i,1);
i--;
}
}
}
function showDigitCtx(num, posx = 0, poxy = 0, bc = '#ddd', color = 'green') {
for (var i = 0; i < 7; i++) {
for (var j = 0; j < (num===10?1:4); j++) {
ctx.beginPath();
ctx.arc(posx+j*9,poxy+i*9,R,0,Math.PI*2,true);
ctx.fillStyle=digit[num][i][j] === 1?color:bc;
ctx.closePath();
ctx.fill();
}
}
}
</script>
苏ICP备16040035号-5