還記得曾經(jīng)秉燭夜讀嫌佑,抱著手機(jī)覽過一部又一部小說時(shí)的情形亥啦。我們用過多少種電子閱讀器缰揪,而你又鐘情于哪一款吶索抓?而如今作為程序員家肯,是不是應(yīng)該用一款自己做出來的閱讀器去瀏覽小說吶闷尿?這定然是必須的萎战。這一次媳否,讓我們動(dòng)手寫一個(gè)自己的電子閱讀器吧特恬。
我們用canvas來繪制電子屏执俩。主要完成了以下幾個(gè)功能
1.繪制下一頁
2.繪制上一頁
簡單吧,就這倆功能癌刽,代碼已上傳github役首,大家可以下載試試看效果。
下面還是要對整體思路來個(gè)介紹的:
1.我們把整個(gè)canvas看成一屏显拜,并且把這個(gè)屏劃分成一個(gè)網(wǎng)格衡奥,以一個(gè)中文字符所占寬來分列,以占高來分行远荠。還有一種情況矮固,當(dāng)一個(gè)字符的Unicode編碼小于128時(shí),它只占中文字符一半的寬度譬淳,高度不變档址。這時(shí),可以得知一行最多可以放置2倍的文字邻梆。
var len_char = 1;
if(str.charCodeAt(i) > 128){//檢查字符的Unicode編
len_char = 2;
}
2.把電子書的內(nèi)容分段守伸,以換行來劃分,然后一段一段繪制浦妄。
首先為每一頁設(shè)置兩個(gè)變量來表示頁首跟頁尾尼摹。這樣,后面繪制時(shí)剂娄,從頁尾開始向后繪制蠢涝,從頁頭開始向前繪制。
/*本頁開始*/
var page_begin = {
line: 0,//當(dāng)前行
offset: 0//當(dāng)前偏移量
};
/*本頁結(jié)束*/
var page_end = {
line: 0,//當(dāng)前行
offset: 0//當(dāng)前偏移量
}
3.繪制下一頁阅懦,從頁尾開始和二,取出一段文字,逐個(gè)檢查字符的Unicode編故黑,當(dāng)一行繪滿儿咱,開始下一行繪制庭砍,同時(shí)要檢測是否行占滿场晶,占滿標(biāo)示此頁繪制完成混埠。
/*開始真實(shí)繪制下一屏*/
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 ++ ){
//逐個(gè)檢查
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 ++;
}
/*未正常跳出诗轻,表示到達(dá)書尾钳宪,直接繪制*/
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 ++ ){
//逐個(gè)檢查
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))
}
/*此行為空行半醉,也單獨(dú)占一行*/
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);
}
/*重置頁尾數(shù)據(jù)*/
page_end.line = page_begin.line;
page_end.offset = page_begin.offset;
/*重置頁頭數(shù)據(jù)*/
page_begin.line = line;
page_begin.offset = str.length - offset;
break
}
tmp_all_write = tmp_write.concat(tmp_all_write);
line --;
}
}
其實(shí)這個(gè)向前翻頁的功能是多余的,可以在向后翻頁的時(shí)候把當(dāng)前頁的信息記錄下來劝术,這樣就不需要翻上一頁還要計(jì)算那么多東西了缩多。
結(jié)語:到目前為止只能算是寫了個(gè)電子閱讀器的demo,完成上一頁下一頁操作养晋。后面需要做的還很多衬吆,比如:美化,翻頁效果绳泉,文本目錄結(jié)構(gòu)逊抡,利用html5直接從本地讀文件等等。有興趣的在此基礎(chǔ)上加上你想要的效果零酪,如果你有好的作品冒嫡,記得@我一觀呦。