还记得曾经秉烛夜读,抱着手机览过一部又一部小说时的情形。我们用过多少种电子阅读器,而你又钟情于哪一款呐?而如今作为程序员,是不是应该用一款自己做出来的阅读器去浏览小说呐?这定然是必须的。这一次,让我们动手写一个自己的电子阅读器吧。
我们用canvas来绘制电子屏。主要完成了以下几个功能
1.绘制下一页
2.绘制上一页
简单吧,就这俩功能,代码已上传github,大家可以下载试试看效果。
下面还是要对整体思路来个介绍的:
1.我们把整个canvas看成一屏,并且把这个屏划分成一个网格,以一个中文字符所占宽来分列,以占高来分行。还有一种情况,当一个字符的Unicode编码小于128时,它只占中文字符一半的宽度,高度不变。这时,可以得知一行最多可以放置2倍的文字。
var len_char = 1;
if(str.charCodeAt(i) > 128){//检查字符的Unicode编
len_char = 2;
}
2.把电子书的内容分段,以换行来划分,然后一段一段绘制。
首先为每一页设置两个变量来表示页首跟页尾。这样,后面绘制时,从页尾开始向后绘制,从页头开始向前绘制。
/*本页开始*/
var page_begin = {
line: 0,//当前行
offset: 0//当前偏移量
};
/*本页结束*/
var page_end = {
line: 0,//当前行
offset: 0//当前偏移量
}
3.绘制下一页,从页尾开始,取出一段文字,逐个检查字符的Unicode编,当一行绘满,开始下一行绘制,同时要检测是否行占满,占满标示此页绘制完成。
/*开始真实绘制下一屏*/
function check(current){
ctx.clearRect(0,0,canvas.width,canvas.height);
var total = panel.col * panel.row * 2;//字符有占一位和两位的,一行最多可绘制2倍长度
var count = 0;
var tag = false;
var tmp_all_write = [];
var isBreak = false;//检查是否正常跳出
while(current < str_write_list.length){
var len = str_write_list[current].length;
var tmp_write = [];
var str = str_write_list[current];
var start = 0;
if(!tag){
start = page_end.offset;
tag = true;
}
var tmp = 0;
var begin = start;
for(var i = start; i < len ; i ++ ){
//逐个检查
if(tmp >= panel.col * 2 - 1){
tmp_write.push(str.substring(begin,i));
begin = i;
tmp = 0;
}
var len_char = 1;
if(str.charCodeAt(i) > 128){
len_char = 2;
}
tmp += len_char;
}
if(tmp > 0){
tmp_write.push(str.substring(begin,i))
}
if(str == "") {
tmp_write.push("");
};
var offset = 0;
if(tmp_all_write.length + tmp_write.length > panel.row) {
for(var i = 0 ; i < tmp_all_write.length ; i ++){
ctx.fillText(tmp_all_write[i],0, (i + 1) * font.size);
}
for(var j = 0 ; j < tmp_write.length && j + i < panel.row ; j ++){
offset += tmp_write[j].length;
ctx.fillText(tmp_write[j],0, (i + j + 1) * font.size);
}
page_end.line = current;
page_end.offset = offset;
isBreak = true;
break ;
}
tmp_all_write = tmp_all_write.concat(tmp_write);
current ++;
}
/*未正常跳出,表示到达书尾,直接绘制*/
if(!isBreak){
for(var i = 0 ; i < tmp_all_write.length ; i ++){
ctx.fillText(tmp_all_write[i],0, (i + 1) * font.size);
}
}
}
3.绘制上一页,从页头开始,倒着取出一段文字,绘制一行,检测行占满。
/*上一页*/
function write_before(){
var tag = false;
var total = panel.col * panel.row * 2;
var count = 0;
var line = page_begin.offset > 0 ? page_begin.line : page_begin.line - 1 ;
var t = 0;
var tmp_all_write = [];
while(line >= 0){
var len = str_write_list[line].length;
var tmp_write = [];
var str = str_write_list[line];
var start = len;
if(!tag){
if(page_begin.offset > 0 ){
start = page_begin.offset
}
tag = true;
}
var tmp = 0;
var begin = 0;
for(var i = 0; i < start ; i ++ ){
//逐个检查
if(tmp >= panel.col * 2 - 1){
tmp_write.push(str.substring(i,begin));
begin = i;
tmp = 0;
}
var len_char = 1;
if(str.charCodeAt(i) > 128){
len_char = 2;
}
tmp += len_char;
}
if(tmp > 0){
tmp_write.push(str.substring(begin,i))
}
/*此行为空行,也单独占一行*/
if(str == "") {
tmp_write.push("");
};
var offset = 0;
if(tmp_all_write.length + tmp_write.length >= panel.row) {
ctx.clearRect(0,0,canvas.width,canvas.height);
var y = panel.row;
for(var i = tmp_all_write.length - 1 ; i >= 0 ; i --){
ctx.fillText(tmp_all_write[i],0, (y--) * font.size);
}
i = tmp_all_write.length;
//
var k = 0;
for(var j = tmp_write.length - 1 ; j >= 0 && (k + i) < panel.row ; j --){
offset += tmp_write[j].length;
k++;
ctx.fillText(tmp_write[j],0, (y--) * font.size);
}
/*重置页尾数据*/
page_end.line = page_begin.line;
page_end.offset = page_begin.offset;
/*重置页头数据*/
page_begin.line = line;
page_begin.offset = str.length - offset;
break
}
tmp_all_write = tmp_write.concat(tmp_all_write);
line --;
}
}
其实这个向前翻页的功能是多余的,可以在向后翻页的时候把当前页的信息记录下来,这样就不需要翻上一页还要计算那么多东西了。
结语:到目前为止只能算是写了个电子阅读器的demo,完成上一页下一页操作。后面需要做的还很多,比如:美化,翻页效果,文本目录结构,利用html5直接从本地读文件等等。有兴趣的在此基础上加上你想要的效果,如果你有好的作品,记得@我一观呦。