SVG 可能非常适合您的文本分页
SVG 文本实际上是文本——与仅显示文本图片的画布不同。
SVG 文本是可读的、可选择的、可搜索的。
SVG 文本本身不会自动换行,但使用 javascript 很容易解决这个问题。
灵活的页面大小是可能的,因为页面格式是在 javascript 中完成的。
分页不依赖于浏览器的格式。
文本下载小而高效。只需要下载当前页面的文本。
以下是如何进行 SVG 分页的详细信息和一个 Demo:
http://jsfiddle.net/m1erickson/Lf4Vt/
第 1 部分:从服务器上的数据库中有效地获取大约一页的单词
将整个文本存储在数据库中,每行 1 个单词。
每行(单词)按单词的顺序顺序索引(单词#1 的索引==1,单词#2 的索引==2,等等)。
例如,这将以正确的词序获取整个文本:
// select the entire text of Romeo and Juliet
// “order by wordIndex” causes the words to be in proper order
Select word from RomeoAndJuliet order by wordIndex
如果您假设任何页面在格式化时包含大约 250 个单词,那么此数据库查询将获取第 1 页的前 250 个单词的文本
// select the first 250 words for page#1
Select top 250 word from RomeoAndJuliet order by wordIndex
现在好的部分!
假设第 1 页在格式化后使用了 212 个单词。然后,当您准备好处理 page#2 时,您可以从 word#213 开始再获取 250 个单词。这导致快速有效的数据获取。
// select 250 more words for page#2
// “where wordIndex>212” causes the fetched words
// to begin with the 213th word in the text
Select top 250 word from RomeoAndJuliet order by wordIndex where wordIndex>212
第 2 部分:将获取的单词格式化为适合指定页面宽度的文本行
每行文本必须包含足够的单词来填充指定的页面,但不能更多。
以单个单词开始第 1 行,然后一次添加 1 个单词,直到文本适合指定的页面宽度。
安装第一行后,我们向下移动一个行高并开始第 2 行。
将单词放在一行上需要测量一行上添加的每个额外单词。当下一个单词超出行宽时,多余的单词将移至下一行。
可以使用 Html Canvasescontext.measureText
方法测量一个单词。
此代码将采用一组单词(例如从数据库中获取的 250 个单词),并将格式化尽可能多的单词以填充页面大小。
maxWidth
是一行文本的最大像素宽度。
maxLines
是适合一页的最大行数。
function textToLines(words,maxWidth,maxLines,x,y){
var lines=[];
while(words.length>0 && lines.length<=maxLines){
var line=getOneLineOfText(words,maxWidth);
words=words.splice(line.index+1);
lines.push(line);
wordCount+=line.index+1;
}
return(lines);
}
function getOneLineOfText(words,maxWidth){
var line="";
var space="";
for(var i=0;i<words.length;i++){
var testWidth=ctx.measureText(line+" "+words[i]).width;
if(testWidth>maxWidth){return({index:i-1,text:line});}
line+=space+words[i];
space=" ";
}
return({index:words.length-1,text:line});
}
第 3 部分:使用 SVG 显示文本行
SVG 文本元素是一个真正的 html 元素,可以阅读、选择和搜索。
SVG Text 元素中的每一行文本都使用 SVG Tspan 元素显示。
此代码采用在第 2 部分中格式化的文本行,并使用 SVG 将这些行显示为文本页面。
function drawSvg(lines,x){
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
sText.setAttributeNS(null, 'font-family', 'verdana');
sText.setAttributeNS(null, 'font-size', "14px");
sText.setAttributeNS(null, 'fill', '#000000');
for(var i=0;i<lines.length;i++){
var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
sTSpan.setAttributeNS(null, 'x', x);
sTSpan.setAttributeNS(null, 'dy', lineHeight+"px");
sTSpan.appendChild(document.createTextNode(lines[i].text));
sText.appendChild(sTSpan);
}
svg.appendChild(sText);
$page.append(svg);
}
这是完整的代码,以防 Demo 链接中断:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
.page{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.font="14px verdana";
var pageWidth=250;
var pageHeight=150;
var pagePaddingLeft=10;
var pagePaddingRight=10;
var approxWordsPerPage=500;
var lineHeight=18;
var maxLinesPerPage=parseInt(pageHeight/lineHeight)-1;
var x=pagePaddingLeft;
var y=lineHeight;
var maxWidth=pageWidth-pagePaddingLeft-pagePaddingRight;
var text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
// # words that have been displayed
//(used when ordering a new page of words)
var wordCount=0;
// size the div to the desired page size
$pages=$(".page");
$pages.width(pageWidth)
$pages.height(pageHeight);
// Test: Page#1
// get a reference to the page div
var $page=$("#page");
// use html canvas to word-wrap this page
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
// create svg elements for each line of text on the page
drawSvg(lines,x);
// Test: Page#2 (just testing...normally there's only 1 full-screen page)
var $page=$("#page2");
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
drawSvg(lines,x);
// Test: Page#3 (just testing...normally there's only 1 full-screen page)
var $page=$("#page3");
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
drawSvg(lines,x);
// fetch the next page of words from the server database
// (since we've specified the starting point in the entire text
// we only have to download 1 page of text as needed
function getNextWords(nextWordIndex){
// Eg: select top 500 word from romeoAndJuliet
// where wordIndex>=nextwordIndex
// order by wordIndex
//
// But here for testing, we just hardcode the entire text
var testingText="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
var testingWords=testingText.split(" ");
var words=testingWords.splice(nextWordIndex,approxWordsPerPage);
//
return(words);
}
function textToLines(words,maxWidth,maxLines,x,y){
var lines=[];
while(words.length>0 && lines.length<=maxLines){
var line=getLineOfText(words,maxWidth);
words=words.splice(line.index+1);
lines.push(line);
wordCount+=line.index+1;
}
return(lines);
}
function getLineOfText(words,maxWidth){
var line="";
var space="";
for(var i=0;i<words.length;i++){
var testWidth=ctx.measureText(line+" "+words[i]).width;
if(testWidth>maxWidth){return({index:i-1,text:line});}
line+=space+words[i];
space=" ";
}
return({index:words.length-1,text:line});
}
function drawSvg(lines,x){
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
sText.setAttributeNS(null, 'font-family', 'verdana');
sText.setAttributeNS(null, 'font-size', "14px");
sText.setAttributeNS(null, 'fill', '#000000');
for(var i=0;i<lines.length;i++){
var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
sTSpan.setAttributeNS(null, 'x', x);
sTSpan.setAttributeNS(null, 'dy', lineHeight+"px");
sTSpan.appendChild(document.createTextNode(lines[i].text));
sText.appendChild(sTSpan);
}
svg.appendChild(sText);
$page.append(svg);
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Text split into "pages"<br>(Selectable & Searchable)</h4>
<div id="page" class="page"></div>
<h4>Page 2</h4>
<div id="page2" class="page"></div>
<h4>Page 3</h4>
<div id="page3" class="page"></div>
</body>
</html>