导航

TreeSaver 模板 Grid匹配算法

Posted on 2011-08-01 10:46  蝈蝈俊  阅读(472)  评论(0编辑  收藏  举报

简单来说,TreeSaver有个函数:treesaver.layout.Grid.best = function(content, grids, breakRecord) {  这个函数根据内容,选择最合适的模板。

这个函数的逻辑就是TreeSaver的模板匹配算法。

 

下面是对这个函数以及调用他的分析:

对于一篇文章来说,分页运算的逻辑在下面文件的 treesaver.ui.Article.prototype.paginateAsync 函数中。

https://github.com/Treesaver/treesaver/blob/master/src/ui/article.js

treesaver.ui.Article.prototype.paginateAsync 函数延迟2秒后使用调度程序对文章解析分页。

在TreeSaver中,Grid的定义就是模板的定义。(其他部分每个分页,每篇文章都是一样的。)

在TreeSaver的资源定义中,我们可以看到有很多个Grid定义,那个Grid适合我们的模板,是根据下面逻辑进行判断的。

https://github.com/Treesaver/treesaver/blob/master/src/layout/grid.js

 

一、知道浏览器可以显示区域的大小。

这里的区域不是浏览器的内容区域,而是资源定义中viewer的区域,viewer区域显示的内容会同一篇文章每个页面都不一样。

二、根据浏览器特征,对候选模板Grid进行过滤,排序

这部分的代码如下:

 

/**
 * Stretch the grids into appropriate heights, and filter out any grids
 * which do not fit. Return the stretched subset of grids in an array
 * @param {{ w: number, h: number }} size
 * @return {Array.<treesaver.layout.Grid>}
 */
treesaver.ui.Article.prototype.stretchGrids = function(size) {
  this.eligible_grids = this.grids.filter(function(grid) {
    return grid.capabilityFilter() && grid.sizeFilter(size);
  }).map(function(grid) {
    // Now stretch to the space
    return grid.stretch(size.h);
  });
  // Are there any grids?
  if (!this.eligible_grids.length) {
    treesaver.debug.error('No eligible grids at ' + size.w + 'x' + size.h);
  }
  // Sort by highest text height (helps with shortcutting in scoring)
  this.eligible_grids.sort(treesaver.layout.Grid.sort);
};

过滤策略:

  1. 如果内容对 data-requires 有限制,对应的Grid是否适合这种数据约束,比如内容是Flash的,模板Grid中也应该是能用来显示这种data-requires的模板。data-requires有哪些枚举,是自己配置的,只需要你保证模板和内容上都有你新增的数据约束即可。
    treesaver.layout.Grid.prototype.capabilityFilter = function() { 函数就是做这个判断逻辑的。
  2. View区域大小限制了只能用那些模板。
    这个逻辑判断在下面函数中:treesaver.layout.Grid.prototype.sizeFilter = function(size) {, 输入参数为View区域的大小。
  3. 对通过上面两个过滤的模板Grid数组进行排序,根据Grid的column和container格式降序排列。

三、遍历文章内容,产生分页,下面就是这个逻辑的主要框架部分代码。

遍历逻辑的核心代码如下,即依次建立每一个Page对象。

/**

 * Paginate the article asynchronously
 * @param {boolean} bg Paginate remainder of article in background.
 * @param {number} index Paginate synchronously until this index.
 * @param {?treesaver.layout.ContentPosition|number} pos Paginate synchronously until this position.
 * @private
 */
treesaver.ui.Article.prototype.paginate = function(bg, index, pos) {
  //.......
  var page;
  index = index || 0;
  while (!this.br.finished) {
    page = new treesaver.layout.Page(
      /** @type {!treesaver.layout.Content } */ (this.content),
      this.eligible_grids,
      /** @type {!treesaver.layout.BreakRecord} */ (this.br)
    );
  //.......
  }
//.......
}

Page对象的构造函数中,第一句就是挑选合适的模板Grid,然后根据挑选的模板组织页面,代码如下:

https://github.com/Treesaver/treesaver/blob/master/src/layout/page.js

/**
  * Page class
  * @constructor
  * @param {!treesaver.layout.Content} content
  * @param {!Array.<treesaver.layout.Grid>} grids
  * @param {!treesaver.layout.BreakRecord} br The current breakRecord.
  */
treesaver.layout.Page = function(content, grids, br) {
  var best = treesaver.layout.Grid.best(content, grids, br),
      host = document.createElement('div'),
      originalBr = br.clone(),
      containerFilled = false;
  // ........
}

计算最佳模板,就涉及到了模板权重评分计算。

对于一个模板,它的权重计算逻辑如下:

  • Column 文本显示的分栏,一栏一个Column。计算模板权重时,一个Column,权重多加50分。模板中有几个Column,权重分数就增加几倍的50。
  • 如果模板Grid的lineHeight和内容的lineHeight不一样,则权重分数扣除2000分。
  • 如果模板Grid的colWidth和内容的colWidth不一致,则权重分数扣除无限大(Infinity)。
  • 如果模板中标识了onlypage(用于文章的封面,只能用于唯一且第一页),如果当前页面是第一页,权重加4000,否则权重减无穷大。
  • 如果模板强制指定了适合那一页,发现当前是该页时,权重增加3000,如果定义了,但是不是该页则权重减无穷大。
    模板定义页面的匹配正则是:treesaver.layout.Grid.pageFlagRegex = /^(no-)?page-(\d+)$/;
  • 如果模板定义了不适合于那一页,发现不适合时,权重减无穷大;不适合的模板class 是 no-page- 开头的。
  • 如果模板定义了适合偶数页(even),则在偶数页时权重增加2000,奇数页时权重减无穷大。
  • 如果模板定义了适合奇数页(odd),则在奇数页时权重增加2000,偶数时权重减无穷大。

以上模板权重评分分数计算在 treesaver.layout.Grid.prototype.score = function(content, breakRecord) { 函数中实现。注意以上只算了 模板的 Column 信息,没有计算模板的 Container 信息。
下一步我们遍历模板中每一个container,看内容中定义的的figure是否适合它。
这个算法在下面几个函数中实现:
treesaver.layout.Grid.prototype.mapContainers = function(content, br) {

  • 如果模板中定义了固定部分(container),则模板权重增加 2000+最小高度×5 .优先使用有图片的模板。
  • 如果内容页,figure是可选择的图片的,则权重增加 4000. 图片具有自适应,优先出现。
  • 如果模板中定义没有使用了fixed,( !container.flexible) 权重增加 5000.

 

Grid模板描述语言中的子元素:

  • Container, 图片及非自动填充的内容。For positioning images and other non-flowing content,
    在内容中,figure控件对应的就是Container。
  • Column   需要自动填充的内容。For flowing content
  • runningtitle ?

内容集合

https://github.com/Treesaver/treesaver/blob/master/src/layout/content.js

  • figures 集合(不能自动填充的内容的集合,图片,引用等)
  • blocks 集合(分段的文本)