网易聚合阅读的出现的确让人惊艳了一下。流畅的交互,使碎片化的新闻资讯像报纸一样摊在眼前,这是信息大爆炸的时代产物。不管你能不能接受这种新阅读形式,事实上,每天越来越多的新闻层出不穷,还没被人看到就已成为了“旧闻”。回归报纸的版面设计,堆叠新闻模块,让用户告别一条接一条整齐的新闻链接,而是在一个版面上提供更多的信息,不失为一种尝试。
而抛去产品的层面,从前端方向来看,“聚合阅读”也有许多值得学习的地方。这几天研究了一下源码,谈谈它随机布局的实现,以及一些优化的措施。
##demo
点击按钮可以改变布局:
##一.怎样定义格子对象
每个格子对象,都至少应该有这5个属性:
- left:距离左边界的位置
- top:距离上边界的位置
- width:格子宽度
- height:格子高度
- background:格子颜色
逐个去定义对象无疑是愚蠢的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function Block(o){ return{ left: o.left, top: o.top, width: o.width, height: o.height, bg: o.bg } } var block1 = new Block({ left: 0, top: 0, width: 50, height: 50, bg: "#3f3" }); var block2 = … … var block37 = …
|
这样不仅不利于后期维护,而且构造格子的嵌套关系也相对麻烦。
网易的做法是使用“交替切割”的方式来做:
- 将大块先切成两列。
- 左列(红色部分)再切成三行,右列(褐,黄,蓝部分)也切成三行。
- 对形成的6个小块,再进行列的切割
整个做法就是“列-行-列-行-……”这样的交替切割。所以它的对象是这样设置的,其中random属性可以约定同级的cols或rows是否可以随机变换位置,width和height的值是规定一个父级块的分割比例。其设置的形式例如(例子与demo的设定无关):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| window.tagConfig.pageLayout = { top: 0, left: 0, width: 100, height: 100, random: !1, cols: [{ width: 30, rows: [{ height: 40, cols: [{ width: 30, rows:[{ height:100 }] },{ width: 70, rows:[{ height:100 }] }] }, { height: 30 },{ height: 30 }] },{ width: 70, rows:[{ height:100 }] }] }
|
##二.递归调用切割函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function _getGrids(tag) { var domArr = [], _cutGrid(tag.pageLayout, function(tag) { if (tag.rows || tag.cols) { _cutGrid(tag, arguments.callee); } else { … domArr.push(xxx) … } } ); return domArr; }
|
##三.切割函数
切割函数挺有意思的,下次想再用一篇文章来详细写一下,在这里仅贴出参考的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| function _shadowClone(e) { var t = {}; for (var n in e) e.hasOwnProperty(n) && (t[n] = e[n]); return t; } (function() { function ranOrNot(e, t) { var n = []; typeof t == "undefined" && (t = e, e = 0); for (; e < t; e++) n.push(e); return n } Array.prototype.randomEach = function(t) { console.log(3); if (typeof t != "function") throw new TypeError; var n = this.length, r = ranOrNot(n); while (n) { var i = Math.floor(Math.random() * n--); if (t(this[r[i]]) === !1) break; r[i] = r[n] } }, Array.prototype.forEach || (Array.prototype.forEach = function(e) { var t = this.length; if (typeof e != "function") throw new TypeError; var n = arguments[1]; for (var r = 0; r < t; r++) r in this && e.call(n, this[r], r, this) }) } )(); function _cutGrid(tag, funcJudge) { function a(a) { function h(cutLength) { var u, a = _shadowClone(cutLength); c++, u = c === l ? tag[cutType2.measure] - s: Math.floor(cutLength[cutType2.measure] * tag[cutType2.measure] / 100), a[cutType1.offset] = i + tag[cutType1.offset], a[cutType2.offset] = s + tag[cutType2.offset], a[cutType1.measure] = f, a[cutType2.measure] = u, a.colorPattern = tag.colorPattern, funcJudge(a), s += u } var f, l = a[cutType2.name].length, c = 0; u++, f = u === cutLength ? tag[cutType1.measure] - i: Math.floor(a[cutType1.measure] * tag[cutType1.measure] / 100), a.random === !1 ? a[cutType2.name].forEach(h) : a[cutType2.name].randomEach(h), s = 0, i += f } var cutType1, cutType2; tag.rows ? (cutType1 = { name: "rows", measure: "height", offset: "top" }, cutType2 = { name: "cols", measure: "width", offset: "left" }) : (cutType1 = { name: "cols", measure: "width", offset: "left" }, cutType2 = { name: "rows", measure: "height", offset: "top" }); var i = 0, s = 0, cutLength = tag[cutType1.name].length, u = 0; tag.random === !1 ? tag[cutType1.name].forEach(a) : tag[cutType1.name].randomEach(a) }
|
##四.最后
完成到这一步,我已不记得声明对象时出了多少次错误。各种尖括号,方括号,逗号和分号翩翩起舞时,你一定跟我一样很想念coffee的语法糖…