首先,这不是一个广告贴。近期我们封闭开发一个新闻类的产品,产品的理念要突出每天发生新闻的热度,需要以标签纬度聚合腾讯网的新闻内容。类似与虎嗅标签的重要TAG列表,然后在网友们面前突出TAG的热度,吸引潜在用户订阅,点击TAG,进入TAG类的新闻。产品的同事找到了一个网易新闻出品的“聚合阅读”做借鉴和分析。
体验地址:news.tag.163.com
项目源代码地址:http://mat1.gtimg.com/joke/js/boqiu/juhe.7z
打开后,满屏的方格新闻块儿铺满屏幕,浓厚的Metro风格。在高级浏览器下,刷新会有整个方格平滑重排定位的效果,点击下方按钮也会有左右平滑切换的效果。我估计身为对UI层比较敏感的前端的同学条件反射式的想会去分析这个案例是如何实现的。
从产品角度上看,本例子可以说是典型的门户型网络产品。强调新闻时效性,热度,信息展示。并以一种全新的方式去展示新闻的内容的聚合。
从前端技术角度上,本例子也可列为典型的webApp技术实现案例,涉及的技术实现包括HTML5, CSS3 , JSONP, JS Template。粗略的分析一下,需要前端开发同学实现的需求有:
1. 当用户刷新页面,切换到不同天数选项对应方格填充的数据需要改变或者重新定位,
2. 页面一共放置37个方格新闻块儿,方格儿排序规则如下:方格块儿分左右两测,每列分三行,左侧左下角内部需要分成4列,第一列三行,第二列一行,第三列三行,第四列两行。右侧分三行,每行也依照左侧左下角的逻辑进行分块。需要注意的是,以行为单位的大块儿有明显的颜色区分
3. 高级浏览器上,用户触发鼠标事件,转场切换需要有缓动,定位重排效果。
4. 能适应不同的PC机不同浏览器窗口的分辨率,适配不同的移动设备,包括手机,平板。需要能运行在不同分辨率的移动设备上。
5. 与后端定制数据接口。展示出新闻大标题,副标题,链接。
需求理解完之后,下面就要考虑实现了。
先做需求2:
因为做过一段时间重构,所以拿到这个设计稿,脑子里第一反是用绝对定位去搞定这个满是方格的PSD稿子了。以此为思路,做出demo。但是改变浏览器的窗口大小之后,方格会浏览器窗口截断,高度也延伸不到底部,这样就铺满不了整个窗口,因为绝对定位的定宽定高,被截断是必然。那么换一个方法,每个CSS方格的width, height, top,left 均设置为百分数,来达到此种效果,但CSS百分数虽然是参照屏幕的宽度进行计算,还是不太精确,会出现定位不准问题,如要做如此多的方格铺排,自然是不行。
不过,思路是有了,把用百分数计算换成获取到客户端clientWidth, clientHeight,然后把宽高度动态分配到37个方格新闻块儿里。当客户端浏览器窗口大小改变之后,重复以上动作,方格铺排完成。
用JSON来表示方格的排列逻辑再好不过。具体为window.tagConfig.pageLayout,上代码
方格按层级包含逻辑,从最外层的pageLayout到最底层cols包含的rows,一级一级描述方格的包含关系。数字100表示铺满屏幕,45,55表示本列所占屏幕的百分比。Random设置是否随机铺排。下面是第一列的铺排逻辑代码。可以对照右侧图进行分析
数据准备好之后,调用对应的函数进行铺排,下面的AutoLoader是一个数组操作类,用来数组保存内部函数_getGrids执行后返回的结果。自己封装了get, regenerate操作。
--------------此处为预设点1---------------
上面代码可直观的看出,_cuteGrid函数负责迭代方格新闻块对象,赋值每个子方格新闻块
儿的属性。具体为若方格新闻块存e.row , e.cols等数据信息,_cuteGrid就递归自身,计算网格布局。否则就附加颜色,字体,字体颜色,背景颜色。
关于如何计算布局,理解一下_cuteGrid函数就可以。传入全部布局信息pageLayout对象,
以及控制布局callback。
例如,初次传入的e为window.tagConfig.pageLayout,进入函数后,程式会初次判断e对象是否存在rows属性,显然第一层之存在cols属性并不存在rows属性。通过三目运算符赋值n,r对应属性(也可以对比一下存在rows属性情况n,r赋值,两者完全相反)
以下为_cuteGrid函数最核心的执行部分,使用数组方法forEach,或者randomEach,
程序执行到此e[n.name]为e.cols,然后数组附加a函数进行迭代新闻块儿定位。
关于a函数,看下图,现在传入的a参数就是我们通过e[n.name].forEach(a)传入的数组元素了,第一此传入则为cols的第一列数据。
执行到下面代码片段时候, l通过赋值a[r.name].length就获取到其内部rows数组长度,
然后通过对比u===o?是否相等,u开始为0,判断u是否等于e.cols.length(2)是否相当,来检测此次铺排的次数是否已到,因为是第一次,所以f = Math.floor( a[n.measure] * e[n.measure] / 100 ),
解释一下,a[n.measure]=45 (本次传入数组元素的模拟width数值), e[n.measure]=100(本次传入数组元素的夫级元素的模拟width数组),这些数值均写在预设置的json数据中。
此时f=45, 下面通过判断a.random是否为!1,通过调用forEach(h),randomEach(h)两个方法调用内部定位和颜色赋值函数h,来继续迭代子元素。
h函数负责给一个新建对象a, 也就是当前子元素rows, 赋值定位信息的函数。
让面的代码可以理解为
a.left=0+e.left,
a.top=0+e.top,
a.width=45,
a.height=1 ,
a.colorPattern=’#ce5f52’ (颜色信息在调用_getGrids已经设置)
当遇到t(a) 然后再次调用下面的function(e){ ….. } 内部的函数进行递归。
文章您读到这里,恭喜,程序就已经成功的绘制了下面的一个最大的蓝色方框。
如果想再次绘制里面的一个很多小方框,
原理就是再次调用_cutGrid函数。对于本文,您的目光就再次返回到文章的预设点1,然后回到这里,蓝色方框里就绘制好了另外的蓝色方框了。循环往复,最后整个页面的布局信息就完全OK了。
绘制完成所有信息之后,在控制台里打印出t,就可以直观的看出37个新闻布局块儿的属性。这些结果被保存在名为t的数组里。由函数_getGrids返回。并由AutoLoader类进行初始化操作。保存在AutoLoader对象的pool的属性里。然后统一进行管理。
getTagData原理与_getGrids类型,通过迭代与后端定制json数据接口。也保存在初始化过后的AutoLoader对象的pool的属性里。随后就可以方便的与新闻布局块儿进行迭代计算,把数据信息动态的填充到37个布局信息块儿中。
到此,需求2实现完毕
再看需求1和3。当用户刷新页面,点击刷行按钮,点击下方切换按钮天数,37个方格块儿重新定位,在支持CSS3的浏览器上,37方格块儿转场切换需要有缓动进入进出的效果,以及归位重排效果。
(重排) (缓动)
为实现这两个关键的交互效果 ,
这里把方格中一二级标题填充HTML代码,转场缓动进入进出的css3语法逻辑分离出来,使用micro template(为追求模版引擎速度推荐使用artTemplate ) 编写其对应的模版规则。
如下,这样做,使得逻辑更加清晰,节省代码量,方便代码的管理与后期维护升级。
(一二级标题填充模版)
(转场进入进出模版Prefix为["-moz-", "-webkit-", "-o-", "-ms-", ""]的缩略简写,offset滑动多少距离)
这里分析一下转场动作,本项目两页之间的切换为整屏幕切换。因为先前37个新闻布局块儿铺排已经完成,所以在转场的时候,只需在每一个新闻块儿的tag设置translateX属性,也就是设置offset为window.clientWidth。比如页面在第一屏切换到第二屏幕时,所有方格新闻块儿使用transform:translateX(offset) 进行位移,因为第一屏中所有新闻方格已经有各自的top,left定位值,再加上各自transform的translateX(offset)值。就可以完成位移操作。若实现缓动效果,设置transition-property:left,top两个属性值,在transition,animation属性上设置duration运动持续时间就行。
然后是点击刷新按钮,打乱当前所有新闻块儿,产生定位重排的效果。如图
这个实现原理简单,重新定位当前屏幕所有的新闻块儿,重新获取当前屏JSON数据,然后参照需求2重新进行37个新闻块儿的定位计算,完成后,统一插入到当前屏幕中央区域。缓动归位的效果的话,同样,设置transition-property:left,top布局属性,transition,animation设置duration动画持续运动持续时间的属性。达到此效果。
到这里,就剩下需求4,5了,关于需求5:能适应不同的PC机不同浏览器窗口的分辨率,适配不同的移动设备,包括手机,平板。需要能运行在不同分辨率的移动设备上。
原理上,只要在用户改变浏览器窗口大小的时候,中间方格块儿能够铺满任意大小窗口的浏览器,那么各种设备的分辨率,无论是平板,各种分辨率的手机都可以完全兼容。原因是37个新闻布局块儿是布局信息是根据当前屏幕的clientWidth,clientHeight进行计算的。
所以,有当window有onresized动作触发的情况下,再次按照需求2,重新按照当前浏览器窗口大小迭代计算37个新闻布局块儿的布局信息,填充整个屏幕,再把数据信息动态的填充到37个布局信息块儿中。从此,各种分辨率设备完美兼容。
项目分析总结:
1,在项目中可以采取针对不同浏览器开发的思路,在支持CSS3的浏览器上多做尝试,不支持的做降级处理,拥抱webapp化。
2,webApp时代对前端的技能要求越来越高。开发者针对高级动画需求的实现需要有更加严谨的逻辑和扎实的编码能力。
3,多分析一些经典项目源代码,譬如github上的源代码,找些实际项目多练,才是技术进步的阶梯。
----------------扩展阅读------------------
Javascript 1.6中数组已经支持forEach方法,
forEach(Function)
对数组的每个元素调用一个函数,此函数掺入三个参数:对象,下标和数组引用。
ie9以下的浏览器需要扩展Array.prototype.forEach
randomEach对数组中打乱顺序进行迭代。
----------------扩展阅读结束------------------