前端实现网络阅读软件之分页原理与算法(一)

688 查看

昨天晚上在群里交流各种脑动大开的题目,我顺手也提了一个问题: JS如何做“字符分页“

原意是源于我4年前公司项目,我负责开发1年的样子,后来各种原因就没有然后了…

http://reader.appcarrier.com/

image image  image

以上图片是手机上的截图,Metro风格当前可是风靡一时,软件本身是类似现在的”追书神器”

通过书名,在网络上搜索到对应的内容,之后保存到本地数据库。在通过JS获取数据再处理

自己装好测了下,貌似下载服务器已经挂了~

 

程序采用PhoneGap打包的,数据采集是用底层完成的,其余的都是通过前端处理

规定:采集到一本书内容,按照书的章节分数据块,写入到本地数据库中,数据库可以用SQLite,webkit是支持的

前端层面需要处理的几个重要问题:

  • 字符分页:一个页面到底可以容纳多少字符(文字,符号,空格等等)
  • 性能问题:如何快速生成指定页面
  • 模拟翻页:类似ibooks翻页效果页正反页面都有文字

 

由于时间太久了,我也没仔细去查阅代码了,这里只能凭记忆描述下

 

原理:

要实现类似于图书的效果,首先要进行的,就是分页操作。也就是说,需要把一段长长的文字,分解成若干个页面。

 

分页背景知识:

表面上来看,分页操作并不复杂,但实际上分页是非常复杂的功能,这个想靠js去计算文字占用的空间,难,非常难

我在问的时候,大家脑洞打开,比如:用一个不换行的宽度100%的容器,计算什么时候scrollwidth 大于width,那么就可以计算出  标点需要多少个填满,等等之类的答案

现在站在我的理解层次简单描述下:

纯文本:

纯文本是最简单的情况。纯文本的高度是固定的,因此,只要能计算出每一行的高度,就可以进行分页。但是,中文排版也不是一件简单的事情,因为中文的标点是很有讲究的:

  • 某些标点不能出现在行的开头,例如“逗号”就不应该出现在行首。有些标点不能出现在行的结尾,例如“正引号”就不应该出现在行的结尾。这个叫做标点的“避头尾”。
  • 如果出现连续的标点,例如冒号后面跟着引号,那么这两个标点不应该占据2个字符的位置,而应该合并起来占据一个字符的位置。这个叫做标点的“压缩”。

html格式:

对于html格式,情况就复杂很多,因此此时行距不固定了,段前、段后间距可能是任意值,而且每行的文字的字体、字号都有可能不一样,这样,计算每一行的高度,就要考虑到种种细微的因素。

如果中间再有图像,情况就会进一步复杂。图像的高度,图像和文字的边距等等。

服务器计算:

如果在服务器里面,提前计算好分页呢?也有问题, 因为要适合不同的手机分辨率,软件本身还可以设置字体的大小,等等

 

因此:

  •  即使是纯文本,高度的计算也是有一个复杂度的。当然,有些软件可能不考虑这些中文特性,胡乱计算。
  •  不管采用底层的Java语言,还是采用客户端的Javascript语言,要实现精致的分页算法都是很难的。
  •  可能有人想问,如果分页分得不好会出现什么情况。最常见的,是最下面以行,某些文字只能显示“半行”。而且显而易见,出现半行的几率,远远大于正好是整行的几率。

总结:通过纯理论去计算分页,和自己写一个文字排版软件区别已经不大了,这绝不是短期的工作能够完成的。况且我也写不出~

 

HTML对分页的支持

HTML并不直接支持分页。

Adobe正在建议一个新的CSS样式,称为CSS Region(http://www.adobe.com/devnet/html5/articles/css3-regions.html)。

如果这个功能开发成功,就能实现类似于排版软件的排版效果。据说这个功能在新的Chrome测试版中正在开发中,但何时能投入使用并稳定运行,还是未知数。

html5新增加了一个功能,就是分栏(columns)。分栏功能可以制作类似于Word中的分栏效果

html5的分栏效果以及CSS代码

clip_image002

如果还不了解分栏,则请阅读如下文章:

分栏虽然不如CSS region那样灵活,但勉强也能够在不同的栏之间,实现文字的拆分。如果我们把每个栏设置成刚好一页的话,就初步模拟了分页的效果

 

分栏功能的性能及动态结构

虽然分栏功能初步能够模拟分页效果,但还存在着不少问题:

  1. 分栏形成的页面,是连续排列的。也就是说,可以支持滑动操作,但并不能支持“翻页”效果。
  2. 如果栏目过多时,性能会很差。(大约20~30个栏目会有明显性能下降)

我们先讨论第2个问题,就是如何动态切换分栏中的内容。

由于分栏存在性能问题,因此,我们不可能把很长的内容,全部用分栏排列(请注意,这是我们后面进行了复杂设计的原因。如果分栏没有性能问题,自然也不需要那么复杂的设计)。

由于性能问题,我们不能把所有需要的内容,全部放在分栏结构中,只能一段一段地显示在栏目中。也就是说,用于构建分栏的段落,是动态变化的

在这种情况下,对于分页的方式而言,最大的问题,是前面的段落,会影响后面的段落的排列。

考虑如下图所示的情况。

clip_image001

图中两个段落在分栏状态下排列

图中,蓝色段落充满了一个栏后,会挤出一些内容到下一栏。而绿色段落,会从蓝色段落结束的地方继续排列。

现在假设动态切换时,蓝色段落被删除,而在绿色段落后面,增加了红色段落。此时,不能让绿色段落从栏首开始排列,必须在绿色段落的开始,给出一个空白的间隔出来,

如图所示。

clip_image002[5]

图中红色段落的排版

也就是说,虽然蓝色段落被删除,但是其对分栏排版的影响,仍然需要计算在内。就上图来说,第1个栏可以完全忽略,因为不影响后面的排版。第2栏的上半部分,也就是蓝色段落的“剩余影响”,在排版仍然要考虑在内。

如何计算每个段落对后面的段落的“剩余影响”呢?幸好在分栏模式下,提供了获取每个段落的高度的功能,就是用zepto的height()函数就可以获取高度。获取了高度之后,除以栏目高度的余数,就是“剩余影响”。

因此,精确地计算出每个段落的高度,就可以实现动态的分栏排版。

 

以上就是基于对分栏实现排版的理论,之后涉及

  • 分栏功能对翻页的支持
  • 分页的实现
  • 翻页的效果
  • 性能优化

等等这些知识点,等下一节发布吧