想法是在 Canvas 上绘制由小方块组成的数字。
第一步是实现绘制小方块的方法,先画出一个边长为 5 的 10x10 个方块,使用两个 for 循环很简单就能完成。
1 for (let i = 0; i < 10; i++) { 2 for (let j = 0; j < 10; j++) { 3 context.fillRect(5 * i, 5 * j, 5, 5); 4 } 5 }
效果如下:
但是这样一大块黑色不是很好看,可以把小方块的边长减一 ,有显示网格的效果,即: context.fillRect(5 * i, 5 * j, 4, 4);
效果如下:
接下来就是把数字转换成像素风,我的想法是创建一个数组,用 0 和 1 来表示是否填充,然后遍历数组。
如 1 为: ["0010", "0110", "0010", "0010", "0111"] ,下图所示方便理解:
为之前的循环增加一个判断,遍历字符串的索引判断是否要在相应位置绘制小方块。
x,y 是整个数字的原点(数字的左上角)。
1 function draw_single_word(context, x, y, numberList) { 2 for (let i = 0; i < numberList.length; i++) { 3 for (let j = 0; j < numberList[i].length; j++) { 4 if (numberList[i][j] === "1") { 5 context.fillRect(x + 5 * y + j, 5 * i, 4, 4); 6 } 7 } 8 } 9 }
显示效果:
类似的其他数字也可以显示,还可以加上冒号和破折号。
1 function pixelNumber(number) { 2 let _number = number; 3 switch (_number) { 4 case "0": 5 _number = ["1111", "1001", "1001", "1001", "1111"]; 6 break; 7 case "1": 8 _number = ["0010", "0110", "0010", "0010", "0111"]; 9 break; 10 case "2": 11 _number = ["1111", "0001", "1111", "1000", "1111"]; 12 break; 13 case "3": 14 _number = ["1111", "0001", "1111", "0001", "1111"]; 15 break; 16 case "4": 17 _number = ["1001", "1001", "1111", "0001", "0001"]; 18 break; 19 case "5": 20 _number = ["1111", "1000", "1111", "0001", "1111"]; 21 break; 22 case "6": 23 _number = ["1111", "1000", "1111", "1001", "1111"]; 24 break; 25 case "7": 26 _number = ["1111", "0001", "0001", "0001", "0001"]; 27 break; 28 case "8": 29 _number = ["1111", "1001", "1111", "1001", "1111"]; 30 break; 31 case "9": 32 _number = ["1111", "1001", "1111", "0001", "1111"]; 33 break; 34 case ":": 35 _number = ["0", "1", "0", "1", "0"]; 36 break; 37 case "-": 38 _number = ["000", "000", "111", "000", "000"]; 39 break; 40 case ".": 41 _number = ["0", "0", "0", "0", "1"]; 42 break; 43 case " ": 44 _number = ["0", "0", "0", "0", "0"]; 45 break; 46 } 47 return _number; 48 }
如下所示为数字 1-9 和 0
多个字符的话,可以遍历字符逐一调用刚才的 draw_single_word 函数画出。
只要在每个字符的 X 轴坐标上增加上一个字符的宽度,就可以防止几个字符重叠在一起。为了让字符不显得太挤,我增加了一个格子的宽度。
需要注意的是,我设置的 “:” 和 “-” 字符宽度只有三格,因此要在循环外创建 width 函数。
1 function draw_words(context, x, y, text) { 2 let width = 0; 3 for (let i = 0; i < text.length; i++) { 4 let numberList = pixelNumber(text[i]); 5 draw_single_word(context, x + width, y, numberList); 6 width += (numberList[0].length + 1) * 5; 7 } 8 }
效果如下:
1 function getTime() { 2 let date = new Date(); 3 let year = date.getFullYear(); 4 let month = zeroNumber(date.getMonth() + 1); // 注意月份默认从 0 开始,所以加一才是正确的 5 let day = zeroNumber(date.getDate()); 6 let hour = zeroNumber(date.getHours()); 7 let minutes = zeroNumber(date.getMinutes()); 8 let seconds = zeroNumber(date.getSeconds()); 9 return { 10 date: year + "-" + month + "-" + day, 11 time: hour + ":" + minutes + ":" + seconds 12 } 13 }
为了美观,我创建了一个 zeroNumber 函数来给不足十的数字前加一个零。
1 function zeroNumber(number) { 2 number = number < 10 ? "0" + number : number; 3 return number; 4 }
最后,使用 requestAnimationFrame 函数让它动起来。
1 function time_animate() { 2 ctx_animate.clearRect(0, 0, 200, 60); 3 draw_words(ctx_animate, 0, 0, getTime().time); 4 window.requestAnimationFrame(time_animate); 5 } 6 time_animate();
其实修改一下,也可显示其他东西,比如字母和简单符号。
最后贴一下我左上角的时间的代码,这是最初的版本,可能有些许问题,顺便说一句,IE不支持 class 。
1 class PixelTime { 2 constructor(canvas) { 3 this.canvas = canvas; 4 this.context = this.canvas.getContext("2d"); 5 this.dateShow = 1; // 是否显示年 6 this.timeShow = 1; // 是否显示时间 7 this.pixelSide = this.canvas.width < 102 ? 2 : Math.floor(this.canvas.width / 51); 8 } 9 /** 10 * 确定字符的点阵数组。 11 */ 12 pixelNumber(number) { 13 switch (number) { 14 case 0: 15 number = ["1111", "1001", "1001", "1001", "1111"]; 16 break; 17 case 1: 18 number = ["0010", "0110", "0010", "0010", "0111"]; 19 break; 20 case 2: 21 number = ["1111", "0001", "1111", "1000", "1111"]; 22 break; 23 case 3: 24 number = ["1111", "0001", "1111", "0001", "1111"]; 25 break; 26 case 4: 27 number = ["1001", "1001", "1111", "0001", "0001"]; 28 break; 29 case 5: 30 number = ["1111", "1000", "1111", "0001", "1111"]; 31 break; 32 case 6: 33 number = ["1111", "1000", "1111", "1001", "1111"]; 34 break; 35 case 7: 36 number = ["1111", "0001", "0001", "0001", "0001"]; 37 break; 38 case 8: 39 number = ["1111", "1001", "1111", "1001", "1111"]; 40 break; 41 case 9: 42 number = ["1111", "1001", "1111", "0001", "1111"]; 43 break; 44 case ":": 45 number = ["0", "1", "0", "1", "0"]; 46 break; 47 case "-": 48 number = ["000", "000", "111", "000", "000"]; 49 break; 50 case ".": 51 number = ["0", "0", "0", "0", "1"]; 52 break; 53 case " ": 54 number = ["0", "0", "0", "0", "0"]; 55 break; 56 } 57 return number; 58 } 59 60 /** 61 * 绘制点阵。 62 */ 63 drawPixelWord(x, y, words) { 64 let width = 0; 65 for (let i = 0; i < words.length; i++) { 66 let word = parseInt(words[i]) || words[i] === "0" ? parseInt(words[i]) : words[i]; 67 word = this.pixelNumber(word); 68 for (let r = 0; r < word.length; r++) { 69 for (let c = 0; c < word[r].length; c++) { 70 if (word[r][c] === "1") { 71 let side = this.pixelSide - 1; 72 this.context.save(); 73 this.context.translate(x + width + this.pixelSide * 2, y + this.pixelSide * 2); 74 this.context.fillRect(c * (side + 1), r * (side + 1), side, side); 75 this.context.restore(); 76 } 77 } 78 } 79 width += (1 + word[0].length) * this.pixelSide; 80 } 81 } 82 83 /** 84 * 给个位数前面加个零,如:01 85 * @param number {number} 86 * @returns {number} 87 */ 88 zeroNumber(number) { 89 number = number < 10 ? "0" + number : number; 90 return number; 91 } 92 93 /** 94 * 获取当前时间。 95 * @returns {{date: string, time: string}} 96 */ 97 getTime() { 98 let date = new Date(); 99 let year = date.getFullYear(); 100 let month = this.zeroNumber(date.getMonth() + 1); // 月份默认从 0 开始 101 let day = this.zeroNumber(date.getDate()); 102 let hour = this.zeroNumber(date.getHours()); 103 let minutes = this.zeroNumber(date.getMinutes()); 104 let seconds = this.zeroNumber(date.getSeconds()); 105 return { 106 date: year + "-" + month + "-" + day, 107 time: hour + ":" + minutes + ":" + seconds 108 } 109 } 110 111 drawTime() { 112 this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); 113 let now = this.getTime(); 114 if (this.dateShow && this.timeShow) { 115 this.drawPixelWord(0, 0, now.date); 116 this.drawPixelWord(this.pixelSide * 14, this.pixelSide * 6, now.time); 117 } else if (this.dateShow) { 118 this.drawPixelWord(0, 0, now.date); 119 } else if (this.timeShow) { 120 this.drawPixelWord(0, 0, now.time); 121 } 122 } 123 }