核心提示:canvas绘图,倒计时特效时钟:参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入。function clock(canvas_id){ ...
canvas绘图,倒计时特效时钟:参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入。
function clock(canvas_id){ !function(canvas_id){ "begin of api"; var width = 1000;//绘制主要以width为参考来决定弹球半径 var height = 200;//height只用来计算绘制数字的基线 var margin_horiz = 150;//水平方向的时钟绘制区域与左右两个边界的保留距离 var margin_vertic = 50;//暂时不用,只是保留下来 var gapCalcByRad = 1.0;//数字位之间的间隙 var median = height / 2;//基线高度 var ball_radius = (width - 2 * margin_horiz) / (100 + 14.0 * gapCalcByRad);//弹球圆球的半径 var fillRem = 0.9;//弹球,圆的绘制半径,实际上只填充圆的0.8个半径范围 var lossFactor = 0.6;//速度的损益因子,每次碰撞后速度为原来的lossFactor倍,保持位正数,切不能大于1 const deltaTime = 80;//刷新时间, 以毫秒计, 影响帧率 var countdown_switch = true;//倒计时开关,设置为true表示使用倒计时功能,false时,只作为时钟显示时间 var destTime = undefined;//用于倒计时的目标时间 setDestTime(2016, 11, 2, 0, 7, 7);//1-12月,从1开始计数,屏蔽了Date对象的从0开始计数 //目标时间跟现在的时间的 差值,不暴露,但和destTime放在一起,方便查找 //当不适用倒计时功能时,设置为现在的时间,绘制钟表 var diffTime = undefined;//看上面两行注释 //偏好的颜色预置选项 const colors = ["#33B5E5","#0099CC","#AA66CC","#9933CC","#99CC00","#669900","#FFBB33","#FF8800","#FF4444","#CC0000"]; var digit_color = colors[5];//绘制数位的颜色 //开启 function start(){ //定时刷新 setInterval(function(){ updateClock(); updateMarbles(); addMarbleArray(); renderClock(); renderMarbles(); }, deltaTime); }; "end of api"; //以下数据不暴露 const $2PI = 2 * Math.PI; var digit = [ [ [0,0,1,1,1,0,0], [0,1,1,0,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,0,1,1,0], [0,0,1,1,1,0,0] ],//0 [ [0,0,0,1,1,0,0], [0,1,1,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [1,1,1,1,1,1,1] ],//1 [ [0,1,1,1,1,1,0], [1,1,0,0,0,1,1], [0,0,0,0,0,1,1], [0,0,0,0,1,1,0], [0,0,0,1,1,0,0], [0,0,1,1,0,0,0], [0,1,1,0,0,0,0], [1,1,0,0,0,0,0], [1,1,0,0,0,1,1], [1,1,1,1,1,1,1] ],//2 [ [1,1,1,1,1,1,1], [0,0,0,0,0,1,1], [0,0,0,0,1,1,0], [0,0,0,1,1,0,0], [0,0,1,1,1,0,0], [0,0,0,0,1,1,0], [0,0,0,0,0,1,1], [0,0,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,1,1,0] ],//3 [ [0,0,0,0,1,1,0], [0,0,0,1,1,1,0], [0,0,1,1,1,1,0], [0,1,1,0,1,1,0], [1,1,0,0,1,1,0], [1,1,1,1,1,1,1], [0,0,0,0,1,1,0], [0,0,0,0,1,1,0], [0,0,0,0,1,1,0], [0,0,0,1,1,1,1] ],//4 [ [1,1,1,1,1,1,1], [1,1,0,0,0,0,0], [1,1,0,0,0,0,0], [1,1,1,1,1,1,0], [0,0,0,0,0,1,1], [0,0,0,0,0,1,1], [0,0,0,0,0,1,1], [0,0,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,1,1,0] ],//5 [ [0,0,0,0,1,1,0], [0,0,1,1,0,0,0], [0,1,1,0,0,0,0], [1,1,0,0,0,0,0], [1,1,0,1,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,1,1,0] ],//6 [ [1,1,1,1,1,1,1], [1,1,0,0,0,1,1], [0,0,0,0,1,1,0], [0,0,0,0,1,1,0], [0,0,0,1,1,0,0], [0,0,0,1,1,0,0], [0,0,1,1,0,0,0], [0,0,1,1,0,0,0], [0,0,1,1,0,0,0], [0,0,1,1,0,0,0] ],//7 [ [0,1,1,1,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,1,1,0] ],//8 [ [0,1,1,1,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [0,1,1,1,0,1,1], [0,0,0,0,0,1,1], [0,0,0,0,0,1,1], [0,0,0,0,1,1,0], [0,0,0,1,1,0,0], [0,1,1,0,0,0,0] ],//9 [ [0,0,0,0], [0,0,0,0], [0,1,1,0], [0,1,1,0], [0,0,0,0], [0,0,0,0], [0,1,1,0], [0,1,1,0], [0,0,0,0], [0,0,0,0] ]//: ]; //数字位的锚点,以此为中心绘制数字点阵 var anchors = []; //画布 var canvas = undefined; //画布的设备上下文,内涵绘制图形的接口 var ctx = undefined; //还要绘制的digit,包括冒号 var charListToRender = []; //上一次绘制的digit,用于和新digit作比较 var expiredCharList = []; //比较charListToRender expiredCharList的不同之处,并把不同之处的索引存放在crackedNumberIndex中 //据此来绘制跳动的小球 var crackedNumberIndex = []; //要绘制的彩色小球,保存在数组里 var marbles = []; //定义一个marble类构造器 function marble(posx, posy, vx, vy, color){ this.positionX = posx; this.positionY = posy; this.velocityOnX = vx; this.velocityOnY = vy; this.color = color; this.update = function(){ this.positionX += this.velocityOnX * this.$deltaTime + this.accelOnX * this.$halfSqureTime;//s = a*t*t/2 + vt this.positionY += this.velocityOnY * this.$deltaTime + this.accelOnY * this.$halfSqureTime; //this.velocityOnX += this.accelOnX * this.$deltaTime; this.velocityOnY += this.accelOnY * this.$deltaTime; if(this.positionY > this.bottomLimit){ this.positionY = 2 * height - this.positionY; this.velocityOnY *= (Math.abs(this.velocityOnY) > 30 ? -this.lossFactor : 1); } else if(this.positionY < this.topLimit){ this.positionY = -this.positionY; this.velocityOnY *= -this.lossFactor; } }; }; //marble的原型,可以通过某种方式暴露出去,让用户修改,当然要在开始循环绘制之前才会有效果 marble.prototype.accelOnX = 0.0;//x方向的加速度,向右为正 marble.prototype.accelOnY = 50.0;//y轴加速度,向下为正 marble.prototype.maxInitVelX = 65;//x轴方向最大初始速度绝对值, marble.prototype.minInitVelX = 40;//x轴方向最小初始速度绝对值 marble.prototype.maxInitVelY = 65;//y轴方向最大初始速度绝对值 marble.prototype.minInitVelY = 40;//y轴方向最小初始速度绝对值 marble.prototype.topLimit = 0;//顶部边界,超出边界的小球被弹回 marble.prototype.bottomLimit = height;//底部边界 marble.prototype.leftLimit = 0;//左边界 marble.prototype.rightLimit = width;//右边界 marble.prototype.$deltaTime = deltaTime / 1000;//刷新时间,以秒为单位 marble.prototype.$halfSqureTime = 0.5 * (deltaTime * deltaTime / 1000 / 1000);//a*t*t/2,单独保存,节省计算量 marble.prototype.horiz_dir_diff_switch_off = false;//是否关闭弹球在水平方向上随即向左右两边分流 marble.prototype.vertical_dir_diff_switch_off = true;//是否关闭弹球在垂直方向上的初始速度上下分流 marble.prototype.lossFactor = lossFactor;//发生碰撞时,速度的折损系数 //网页打开时候回调函数 window.onload = function(){ canvas = document.getElementById(canvas_id); ctx = canvas.getContext("2d"); canvas.width = width; canvas.height = height; var acc = margin_horiz; //计算数字位的锚点,以此为中心绘制数字 for(let i = 0; i< 8; i++){ if( i == 2 || i == 5){ anchors[i] = (acc + 4.0 * ball_radius + gapCalcByRad * ball_radius); acc += 8.0 * ball_radius + gapCalcByRad * ball_radius; } else{ anchors[i] = (acc + 7.0 * ball_radius + gapCalcByRad * ball_radius); acc += 14.0 * ball_radius + gapCalcByRad * ball_radius; } } ctx.fillStyle = digit_color; start(); }; //绘制弹球 function renderMarbles(){ let length = marbles.length; let radius = ball_radius; for(let i = 0; i < length; i++){ drawBall(radius, marbles[i].positionX, marbles[i].positionY, marbles[i].color); } } //计算出变成marbles的数字为的索引,存放于crackedNumberIndex数组 function updateClock(){ expiredCharList = charListToRender; getCharListToRender(); var length = expiredCharList.length; crackedNumberIndex.length = 0;//防止内存溢出 for(let i = 0; i< length; i++){ if(expiredCharList[i] != charListToRender[i]){ crackedNumberIndex.push(i); } } } //遍历更新弹球的状态,包括速度,位置等 function updateMarbles(){ let length = marbles.length; for(let i = 0; i < length; i++){ marbles[i].update(); } } //每次数字位发生变化时会增加弹球数量 function addMarbleArray(){ let length = crackedNumberIndex.length, i = 0, j = 0; let colorsCount = colors.length; let maxInitVelX = marble.prototype.maxInitVelX; let maxInitVelY = marble.prototype.maxInitVelY; let minInitVelX = marble.prototype.minInitVelX; let minInitVelY = marble.prototype.minInitVelY; let horiz_dir_diff_switch_off = marble.prototype.horiz_dir_diff_switch_off; let vertical_dir_diff_switch_off = marble.prototype.vertical_dir_diff_switch_off; //marbles.length = 0;//重置数组,防止内存溢出 for(let index = 0; index < length; index++){ //如果不是两个冒号符号位,事实上冒号位发生变化不可能的,index是存放改变的数位的数组的索引, //number得到clock上变化的数字位的索引 let number = crackedNumberIndex[index]; if(number != 2 && number != 5){ for(i = 0; i < 10; i++){ for(j = 0; j < 7; j++){ if(digit[expiredCharList[number]][i][j] == 1){ marbles.push(new marble(2 * (j-3.0)*ball_radius+anchors[number], 2 * (i-4.5)*ball_radius+median, (Math.random() * (maxInitVelX - minInitVelX) + minInitVelX) * (horiz_dir_diff_switch_off ? -1 : (Math.random() * 2 > 1 ? 1 : -1)), (Math.random() * (maxInitVelY - minInitVelY) + minInitVelY) * (vertical_dir_diff_switch_off ? -1 : (Math.random() * 2 > 1 ? 1 : -1)), colors[Math.ceil(Math.random() * colorsCount)] )); } } } }else{ alert("冒号位不应发生变化!"); } } if(marbles.length > 1000){ for( let i = 0; i < marbles.length - 1000; i++){ marbles.shift() } } } //绘制时钟数字 function renderClock(){ ctx.clearRect(0, 0, canvas.width, canvas.height); for(let i = 0; i< charListToRender.length; i++){ drawChar(charListToRender[i], anchors[i], median); } } //计算要绘制的符号,考虑到是否禁用倒计时功能 function getCharListToRender(){ var diffTime = (countdown_switch ? (parseInt((destTime.getTime() - new Date().getTime()))) : (new Date().getTime() - new Date(0, 0, 0)) ) / 1000;//以秒计数 var diffHours = parseInt(diffTime / 3600); var diffMinutes = parseInt((diffTime % 3600) / 60); var diffSeconds = parseInt(diffTime % 60); if(diffTime < 0.005){ charListToRender = [0,0,10,0,0,10,0,0]; return; } //小时数超过两位数,就会出问题,做10进制求余运算,解决这种情况 charListToRender = [ countdown_switch ? parseInt(diffHours/10%10) : parseInt(diffHours%24/10), countdown_switch ? parseInt(diffHours%10) : parseInt(diffHours%24%10), 10, parseInt(diffMinutes/10), parseInt(diffMinutes%10), 10, parseInt(diffSeconds/10), parseInt(diffSeconds%10)]; } //可以暴露的接口,用于设置目标时间,用于倒计时 function setDestTime(year, month=1, day=1, hour=0, minute=0, second=0){ destTime = new Date(year, month - 1, day, hour , minute, second); } //绘制数字位,通过调用drawBall函数实现 function drawChar(index, x, y){ let i = 0, j = 0; let radius = ball_radius * fillRem; if(index != 10){ for(i = 0; i < 10; i++){ for(j = 0; j < 7; j++){ if(digit[index][i][j] == 1){ drawBall(radius, 2 * (j-3.0)*ball_radius+x, 2 * (i-4.5)*ball_radius+y); } } } }else{ for(i = 0; i < 10; i++){ for(j = 0; j < 4; j++){ if(digit[index][i][j] == 1){ drawBall(radius, 2 * (j-1.5)*ball_radius+x, 2 * (i-4.5)*ball_radius+y); } } } } } //绘制圆球 function drawBall(radius, x, y, color = digit_color){ ctx.save(); ctx.fillStyle = color; ctx.beginPath(); ctx.arc(x, y, radius, 0, $2PI) ctx.closePath(); ctx.fill(); ctx.restore(); } }(canvas_id);//参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入 }