移动端H5页面 iphone6的适配技巧

iphone6 及 iphone 6 plus 已经出来一段时间了。很多移动端网站,以前写死body 为320px的,现在估计也忙着做适配了。

大屏幕手机其实一直有,只是以前大家没怎么重视,移动端的H5页面大部分都以320px为基准宽度进行布局,那些大屏屌丝android用户也懒得去理,而现在iphone也搞起多屏幕,老板们重视程度就不一样了。

回归正题,兼容iphone各版本机型最佳的方式就是 自适应

1、viewport 简单粗暴的方式:

<meta name="viewport" content="width=320,maximum-scale=1.3,user-scalable=no">

直接设置viewport为320px的1.3倍,将页面放大1.3倍。

为什么是1.3?

目前大部分页面都是以320px为基准的布局,而iphone6的宽度比是375/320 = 1.171875,iphone6+则是 414/320 = 1.29375
那么以1.29倍也就约等于1.3了。

这方式也是活动页面快速适配ip6+的不二选择!

2、ip6+ 的CSS media query

@media (min-device-width : 375px) and (max-device-width : 667px) and (-webkit-min-device-pixel-ratio : 2){
    /*iphone 6*/
}

@media (min-device-width : 414px) and (max-device-width : 736px) and (-webkit-min-device-pixel-ratio : 3){
    /*iphone 6 plus*/
}

PS: 也可以直接使用实际的device-width:如device-width : 375px

在原有页面的基础上,再针对相应的屏幕大小单独写样式做适配。

3、REM布局

REM是CSS3新增的一种单位,并且移动端的支持度很高,android2.x+,ios5+ 都支持。
REM是相对于dom结构的根元素来设置大小,也就是html这个元素。相较于em单位,rem使用上更容易理解及运用。

REM布局也是目前多屏幕适配的最佳方式

REM与PX的换算可以查看网址:https://offroadcode.com/prototypes/rem-calculator/

假设,html我们设置font-size:12px; 也就是说12px相对于1rem,那么18px也就是 18/12 = 1.5rem。

那么我们以320px的设计布局为基准,将html设置为font-size:100px,即100px = 1rem。(设置100px是为了方便计算)那么可以将大部分px单位除以100就可以直接改成rem单位了。

REM如何做响应式布局?

1、如果仅仅是适配ip6+设备,那么使用media query就行。
伪代码如下:

/*320px布局*/
html{font-size: 100px;}
body{font-size: 0.14rem /*实际相当于14px*/}

/* iphone 6 */
@media (min-device-width : 375px) and (max-device-width : 667px) and (-webkit-min-device-pixel-ratio : 2){
    html{font-size: 117.1875px;}
}
/* iphone6 plus */
@media (min-device-width : 414px) and (max-device-width : 736px) and (-webkit-min-device-pixel-ratio : 3){
    html{font-size: 129.375px;}
}

这样,在ip6下,也就将页面内的元素放大了1.17倍,ip6+下也就是放大了1.29倍。

2、如果是完全自适应,那么可以通过JS来控制。

(function (doc, win) {
    var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
        recalc = function () {
            var clientWidth = docEl.clientWidth;
            if (!clientWidth) return;
            docEl.style.fontSize = 100 * (clientWidth / 320) + 'px';
        };

    // Abort if browser does not support addEventListener
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

页面初始化的时候计算font-size,然后再绑定resize事件。这种效果就和百分比布局一样了。

3、那么用REM做单位与百分比做单位有什么优势?

主要优势在于能更好的控制元素大小【可控性强】。(一般百分比应用在布局层,一般常见设置为50%,33.3%,25%之类的整数居多,难以运用在复杂的页面小部件内)。
但是相比百分比布局,需要借助JS或media query实现,略有一点瑕疵。

DEMO地址

See the Pen Jojaqw by hugo (@baofen14787) on CodePen.

说明:使用REM布局必须选好基准宽度,如第2小点的JS实现,是以320px为基准设置的REM,若后续我们设计稿都改成以ip6为基准的375进行设计时,这个REM也要相应的做调整。

4、图片自适应

刚说完REM布局,那么用百分比布局也能实现一样的效果,但是用百分比布局,必须要面临一个问题:
图片宽度100%,页面加载时会存在高度塌陷的问题。

1

如图:页面加载时图片高度默认不存在。

那么可以用padding-top设置百分比值来实现自适应。

公式如下:

padding-top = (Image Height / Image Width) * 100%

原理:padding-top值为百分比时,取值是是相对于宽度的。

相关代码实现:

<div class="cover">
    ![](http://g.ald.alicdn.com/bao/uploaded/i1/TB1d6QqGpXXXXbKXXXXXXXXXXXX_!!0-item_pic.jpg_160x160q90.jpg)
</div>
.cover{position: relative; padding-top: 100%; height: 0; overflow: hidden;}
.cover img{position: absolute; top: 0; width: 100%;}

DEMO地址,缩放浏览器窗口看看。

See the Pen vEYzgv by hugo (@baofen14787) on CodePen.

5、图片高清化

大家都知道,iphone6 plus 是3倍高清图了,它的devicePixelRatio = 3。 关于DPR的介绍可以查看这篇文章《设备像素比devicePixelRatio简单介绍

在ios8下,已经开始支持img的srcset 属性了(目前移动端也就ios8开始支持),也就是说,可以对一张图片设置2个URL,浏览器自动加载对应的图片。

3

支持程度如下:

黄色表示仅支持旧的srcset规范,绿色表示支持全新的srcset规范,包括sizes属性,w描述符。 这里不展开,详细了解可自行google。

4

如下DEMO,请切换devicePixelRatio值进行查看:

See the Pen YPzOxB by hugo (@baofen14787) on CodePen.

不过目前前端这边图片的实现基本都用lazyload的方式实现。srcset的图片加载方式在实际项目中运用还比较少。

6、背景图高清化

media query 实现高清化

img标签的高清化,可以通过JS判断devicePixelRatio的值来加载不同尺寸的图片,但是对于背景图,写在CSS中的,用JS来判断就略麻烦了,还好CSS通过media query也能判断dpr。

目前兼容性最好的背景图高清化实现方式,使用media query的-webkit-min-device-pixel-ratio做判断:

/* 普通显示屏(设备像素比例小于等于1)使用1倍的图 */
        .css{
            background-image: url(img_1x.png);
        }

        /* 高清显示屏(设备像素比例大于等于2)使用2倍图  */
        @media only screen and (-webkit-min-device-pixel-ratio:2){
            .css{
                background-image: url(img_2x.png);
            }
        }

        /* 高清显示屏(设备像素比例大于等于3)使用3倍图  */
        @media only screen and (-webkit-min-device-pixel-ratio:3){
            .css{
                background-image: url(img_3x.png);
            }
        }

进一步,可以通过工具生成相应的3x,2x,1x的图片及css,在使用时直接引用即可。谁搞一个?

关于移动设备的-webkit-min-device-pixel-ratio值,可以查看该网页的整理:http://bjango.com/articles/min-device-pixel-ratio/

image-set 实现高清化

image-set,它是Webkit的私有属性,也是Css4的一个属性,它是为了解决Retina屏幕下的图像显示而生。

使用方式也很简单。伪代码如下:

.css {
            background-image: url(1x.png);    /*不支持image-set的情况下显示*/
            background: -webkit-image-set(
                    url(1x.png) 1x,/* 支持image-set的浏览器的[普通屏幕]下 */
                    url(2x.png) 2x,/* 支持image-set的浏览器的[2倍Retina屏幕] */
                    url(3x.png) 3x/* 支持image-set的浏览器的[3倍Retina屏幕] */
            );
        }

目前移动端的支持程度来看,ios7+,android 4.4+ 下已经支持了。如果仅仅是做ip6+的高清适配方案。image-set也是一种实现方案。

使用image-set 与 media query 实现有什么区别及好处?

这篇文章里面做了很详细的阐述,大家可以看看:http://blog.cloudfour.com/safari-6-and-chrome-21-add-image-set-to-support-retina-images/

大体的意思是:image-set不需要告诉浏览器使用什么图像,而是直接提供了图像让浏览器选择。这就意味着,如果在低网速下,浏览器可以选择加载低分辨率的图片。(PS:好智能的样子)

但是相比如media query的实现,image-set仅支持单个图片的高清化,不适合在css sprite下使用。 并且兼容性也是一大硬伤。

但是一般来说,用在LOGO区域,单个图片图标的区域下,也是个不错的选择。

7、图片列表的自适应

关于适配,也就是要让布局更灵活,在电商网站里面,商品列表是一个非常常见的结构。

一种比较智能的列表方式是: 两端对齐,间距自适应。

那么可以使用FLEXBOX布局来实现两端对齐的效果,也可以使用text-align:justify的方式实现。

先看个flex实现的例子,主要通过justify-content:space-between,来实现:

See the Pen YPzOLM by hugo (@baofen14787) on CodePen.

flexbox的布局方式,在PC端就不合适了,ie9以下都不支持,那么更友好的方式可以使用text-align:justify来实现,兼容各大主流浏览器,包括IE6。

详情请移步我以前写的博文:《inline-block + justify实现列表两端对齐

小DEMO:

See the Pen raNZKp by hugo (@baofen14787) on CodePen.

但是这2种布局方式都有一定的局限性。就是列表个数必须凑整。目前还没找到一种能够兼容不限个数的实现方案,如果各位看官有更好的实现方式,也欢迎提出,一起交流。

总结

  1. 活动页面快速适配可以使用修改viewport的方式快速适配。
  2. 目前我所知道的适配方案里面,使用REM单位做布局是目前最佳的实现方案,可优先考虑。
  3. 移动端ip6的适配方案有很多,没有固定的套路及方法,请根据自身业务的特点,选择其中的一些方法组合使用。

position:sticky 使用条件分析

哪些浏览器支持,可以查看:http://caniuse.com/#search=sticky

用这个属性,主要是ios下的safari已经支持了,在ios下可以免去js模拟,效果更平滑。

在移动端模拟sticky效果,还是非常蛋疼的,需要同时监听touchmove和scroll事件,页面惯性滚动时会有一定的延迟(scorll事件在滚动停止才触发),同时移动端对fixed定位支持也不大好。JS实现,效果还是大打则扣。

关于这个position:sticky的介绍,可以google一下,网上有很多介绍了,比如神飞的《position:sticky介绍》

今天主要讲的是,position:sticky的使用条件,很多同学经常遇到position:sticky失效的情况,今天对sticky这个属性做了一番调试,总结了一些规律。现分享下。

俗话说,弄不清楚原理,就熟记结论也是一样的,那就先抛结论,sticky满足以下条件才能生效:

1、具有sticky属性的元素,其父级高度必须大于sticky元素的高度。

2、sticky元素的底部,不能和父级底部重叠。(这条不好表述,文后详细说明)

3、sticky元素的父级不能含有overflow:hidden 和 overflow:auto 属性

4、必须具有top,或 bottom 属性。

同时要注意,sticky元素仅在他父级容器内有效,超出容器范围则不再生效了。

先看个正常生效的DEMO:

See the Pen fqdkF by hugo (@baofen14787) on CodePen.

然后在sticky元素外套个div,再看看效果,发现sticky效果失效了。原因是结论的第一点,sticky元素的父级高度刚好等于sticky的高度,所以失效

See the Pen cgHdq by hugo (@baofen14787) on CodePen.

继续看第3个DEMO,也是失效的。虽然父元素的高度大于sticky元素的高度,但是sticky元素的底部刚好也是父元素的最后一个元素。(和父元素的底部重叠了)
实际上,sticky元素仅在他父级容器内有效,而sticky刚好已经到了生效的最边缘了,所以看起来就失效了。

See the Pen lKgtC by hugo (@baofen14787) on CodePen.

继续第4个DEMO,给父级容器加上overflow:hidden,sticky效果又失效了。

See the Pen okgGn by hugo (@baofen14787) on CodePen.

最后再说一下,sticky仅在父元素内生效,看DEMO

See the Pen zqyog by hugo (@baofen14787) on CodePen.

好了,DEMO放完,最后给个作业,请将这个页面sticky效果修复.

See the Pen vamnz by hugo (@baofen14787) on CodePen.

CSS命名之姓氏命名法

背景:

由历史原因及个人习惯引起的DOM结构、命名不统一,导致不同成员在维护同一页面时,无从下手,效率低下,迭代、维护成本极高。

命名原则:

1.概述

1.1基于姓氏命名法(继承+外来),如下图:

1.2说明:

(1).简单模块:在子孙模块数量可预测的情况下,继承祖先模块的命名前缀

如上图的焦点图轮播模块,模块整体结构比较简单,子元素的内容相对较少,这种情况下,假设将模块命名为 : slider,那么其后续的所有子元素,均需要继承这个类名。

slider
├── slider-list
|    |
|    └── ....
└── slider-nav
|    |
|    └── ....

(2).复杂模块:当子孙模块数量较多,且无法预估时,可以选择采用继承“祖先+父”模块的命名前缀,以保证模块之间的独立性

在上图的模块中,一个大楼层里面包含一个子模块:tab切换。在这种结构复杂的模块中,为了保证模块类名使用简单(css起名也是一个体力活)和结构看起来更清晰,可以使用继承“祖先+父”模块的命名前缀的方式。

在这个楼层模块中,楼层的名字叫:floor ,里面的tab切换单个内容块名字叫 item,那么 tab切换内部的子元素命名规则为 : floor-item-xxx

如:

floor
├── floor-hd
|    |
|    ├── floor-tit
|
├── floor-item
|    |
|    ├── floor-item-tit
|    └── floor-item-desc

上面代码中使用了2次 tit 来表示标题,也就是说如果你自己有一组命名包,你就可以很轻松的进行命名了,比如描述相关:desc、info、extra等等。

(3).复合模块:子模块中,可以嵌套其他模块,可理解为“娶媳妇”

模块间本身会有嵌套情况,如上面的结构,在今日特惠这个模块中嵌套了一个公共组件。可以理解为娶媳妇。(媳妇一般是外姓的)。

如:

hotsales
├── hotsales_li
| |
| └── mod_goods
| |
| ├── mod_goods_price
| └── mod_goods_tit
| └── mod_goods_promo

当然,其实第二点也可以用第三点的方式来实现,那么这里就涉及到一个问题?什么时候用第三点提到的方法。

比较推荐的建议是,如果嵌套的模块是一个比较通用的组件,那么可以用第三点,将子模块抽出成一个独立的组件,而其他情况下建议使用第二点的方式。

基于以上的方式,可以完成基本的命名规则,但是无法区分模块的类型。(模块是否可复用?复用范围?)

于是我们在模块的命名规则上遵循以下原则:

  1. 全站公共模块以”mod-”开头
  2. 页面公共模块以”xx-mod-”开头(xx为页面名称缩写)
  3. 独立模块,命名为一个简短的单词,如”hot、floor、banner” 等

基于以下2点来区分全站公共模块及页面模块。

  1. 同一个页面出现2次及以上同一个类名
  2. 2个以上页面出现同一个类名

在这种规则下,可能会出现这个模块原先是独立模块的,然后发现可以复用了,然后就修改类名变成公共模块。这种变更成本比较低,可接受。(因为独立模块在页面上只有一处,修改类名不会影响到其他样式)

基于这个css姓氏命名法,我们的类名是长命名的方式了,只要保证了最外层模块的唯一性,那么里面的子模块类名是不可能会出现重复的。
于是,我们要求CSS以单层命名为主,嵌套一般不能操过3层。

这样在修改样式与html结构时,相互之间是耦合的,css不依赖于html的结构。(我知道很多同学写less或scss喜欢像html那样一层一层嵌套)

最后附上以前feeling写的代码给大家参考:

webkit浏览器渲染影响因素分析

前言:浏览器的渲染对性能影响非常大,特别是在移动端页面,在宏观上,我们可以参考雅虎那20几条军规来操作,但在微观渲染层面,实际还没有一套相对成型的理论做为依据。

本文只是抛砖引玉,带大家进入微观的优化领域,实际在渲染优化这块上,还有很多技巧及方法需要大家去挖掘。本文写的也比较凌乱,望包涵!!

先来看个chrome timeline 工具上的一个图:

rendering1

在timeline上,我们看到有6种颜色的柱子,这6个类型的柱子构建了整个webkit浏览器的渲染过程。

简单的分类一下:蓝色表示加载,黄色表示脚本执行,紫色表示计算样式及布局,绿色表示绘制合成,白色表示空闲时间,灰色表示其他时间。这里主要看的是前面三个。

如下图:

rendering2

蓝色loading包含各种资源加载,在页面初始化加载阶段可以看到蓝色部分是耗时最长的,因为正在加载资源(加载html、css、js、img、flash、mp3等。)

Chrome上各个渲染部分的实际含义:

Parse Html:

发送一个http请求,获取请求的内容,然后解析html的过程。

Recalculate Style:

重新计算样式,它计算的是Style,和Layout做的事情完全不同。Layout计算的一个元素绝对的位置和尺寸,或者说是“Compute Layout”。

Recalculate被触发的时候做的事情就是处理JavaScript给元素设置的样式而已。Recalculate Style会计算出Render 树(渲染树),然后从根节点开始进行页面渲染,将CSS附加到DOM上的过程。

任何企图改变元素样式的操作都会触发Recalculate 。同Layout一样,它也是在JavaScript执行完成后才触发的。

Layout:

计算页面上的布局,即元素在文档中的位置及大小。如上面所说,Layout计算的是布局位置信息。任何有可能改变元素位置或大小的样式都会触发这个Layout事件,如width、height

Rasterizer:

光栅化,一般的安卓手机都会进行光栅化,光栅主要是针对图形的一个栅格化过程。低端手机在这部分耗时还蛮多的。

Paint:

页面上显示东西有任何变动都会触发Paint 。包括拖动滚动条、鼠标选中文字,等这些完全不改变样式,只改变显示结果的动作都会触发Paint。

Paint的工作就是把文档中用户可见的那一部分展现给用户。Paint是把Layout和Recalculate的计算的结果直接在浏览器窗体上绘制出来,它并不实现具体的元素计算。

Image Decode:

图片解码,将图片解析到浏览器上显示的过程。

Image Resize:

图片的大小设置,图片加载解析后,若发现图片大小并不是实际的大小(CSS改变了宽高),则需要Resize。Resize越大,耗时越久,所以尽量以图片的原始大小输出。

Composite Layers:

最后合并图层,输出页面到屏幕。浏览器在渲染过程中会将一些含义特殊样式的DOM结构绘制于其他图层,有点类似于Photoshop的图层概念。一张图片在Photoshop是由多个图层组合而成,而浏览器最终显示的页面实际也是有多个图层构成的。

有哪些因素会导致新建图层:

1、进行3D或者透视变换的CSS属性

2、使用硬件加速视频解码的<video>元素

3、具有3D(WebGL)上下文或者硬件加速的2D上下文的<canvas>元素

4、组合型插件(即Flash)

5、具有有CSS透明度动画或者使用动画式Webkit变换的元素

6、具有硬件加速的CSS滤镜的元素

在CSS里面,不同的属性会触发不同的layout或者paint,所以通过JS改变css的属性时,应该考虑到这些方面。如下图:

2

再引用另外一张图来看看CSS不同属性所触发的情况:

3

关于CSS属性的一个渲染问题,可以看下表,需翻墙查看: https://docs.google.com/spreadsheet/pub?key=0ArK1Uipy0SbDdHVLc1ozTFlja1dhb25QNGhJMXN5MXc&single=true&gid=0&output=html

如何优化渲染时间

1、为了确保页面的流程,必须保证60fps内不发生2次渲染树更新。 如下图,16ms内只发生如下几个操作则是正常及正确的 :

QQ截图20140817214845

2、页面滚动时,需要避免不必要的渲染及长时间渲染。

不必要的渲染包括:

1)position:fixed

fixed定位在滚动时会不停的进行渲染,特别是如果是页面顶部有个fiexd,页面底部有个类似返回顶部的fixed,则在滚动时会整个页面进行渲染,效率非常低。可以加 transform :  translateZ(0) ; 解决。

7

2) overflow:scroll

3) hover effects

有些:hover伪类在页面滚动时会不小心就触发到,如hover效果有阴影、圆角等比较耗时的属性时,建议页面滚动时,先取消hover效果,滚动停止后再加上hover效果。这个可以通过在外层加类名进行控制。

4) touch listeners

6

长时间渲染包括:

1)复杂的CSS

2)Image Decodes

这里特别是图片的Image Decodes及 Images Resize 这2个过程在移动端是非常耗时的,如下图:

4

3)Large empty layers(大的空图层,DIV)

5

参考: https://speakerdeck.com/addyosmani/velocityconf-rendering-performance-case-studies

【译】chrome – GPU加速的图层组合操作

原文:http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome

译注:(专业词汇对应翻译)

Node(节点)
DOM Tree(DOM树)
RenderObject(渲染对象)
RenderTree(渲染树)
GraphicsContext(图形环境对象、图形上下文对象)
一个Graphics Context默示一个绘制目标。它包含绘制体系用于完成绘制指令的绘制参数和设备相干信息。Graphics Context定义了根蒂根基的绘制属性,如色彩、裁减区域、线条宽度和样式信息、字体信息、混淆模式等。来自 <http://www.byywee.com/page/M0/S772/772925.html>
Bitmap位图
RenderLayers渲染图层
Alpha mask透明遮罩
Css filtercss滤镜
Software Rendering path软件渲染模式。译成“软件渲染路径”、“软件渲染路线”都可以,它是webkit渲染页面时的传统模式,不依赖硬件加速。
HWND返回窗体或控件的句柄(注意 OLE容器控件不支持该属性。句柄:是由操作环境定义的一个唯一的整数值,它被程序用来标识或者切换到对象,如窗体或控件等。)来自 <http://baike.baidu.com/link?url=knrqRLDl2voMqg6N6pq97dWrZGaxwR7G7gwW4HQyHzn6WGTflGg0nnms9afq7oARZ1hDaduE-81jnuxXQaGziq>
IPCIPC(Inter-Process Communication,进程间通信) 来自 <http://baike.baidu.com/view/373.htm>

概要

本文档阐述chrome浏览器“硬件加速图层组合”的背景和实现细节。

介绍

一般情况下,web浏览器完全依赖CPU进行网页内容的渲染。随着GPU在硬件设备(即便是很小的硬设)中的集成应用,以及富媒体例如视频、3D效果等在网页体验中扮演着越来越重要的角色,人们开始注意想方设法高效利用底层硬件获取更好的性能、电源节能。诚然,利用GPU直接对网页内容进行图层组合可以带来显著的性能提升(译注:这里的性能应该是指渲染速度方面)。最明显的例子莫过于video元素,它利用硬件进行视频解码,利用WebGL为H5 canvas提供硬件3D加速图形绘制渲染,期间占用的内存区域是CPU无法快速存取的。(译注:WebGL参考百科文档)

将网页图层组合的操作扔给GPU还有别的诸多好处,在大多数情况下,GPU在涉及大量像素的图形绘制、组合操作方面比CPU快的多,因为GPU是专门设计善干这类事情的硬件。此外,充分利用GPU做图形绘制、组合这类工作的同时,CPU就有更多的可以并行地做其他方面的运算,这种并行工作的方式创建了一个高效的图形管道。

Part 1  Webkit渲染基础及软件渲染模式

Webkit渲染引擎的源码巨大且复杂,晦涩难懂,几乎没啥米注释!为了弄清楚GPU在chrome里面如何工作,我们得先了解下webkit渲染网页的基本构件流程。下面我们从webkit如何工作作为基本出发点,回头再慢慢引入GPU影响渲染的过程,最终了解GPU如何工作。

节点和DOM树

在Webkit里面,网页内容内部存储为一个节点对象的树,我们称之为DOM树。网页上的每个标签包括标签之间的文本,各自对应DOM树上的一个节点。DOM树最上面的节点即Document节点。

从节点到渲染对象

DOM树里面的每个视觉可见的节点,均有一个相对应的渲染对象。在webkit内,渲染对象被存储成树形数据结构,和DOM树并行,我们称之为渲染树。渲染对象知道如何在显示介质(译注:例如显示器)上呈现(绘制)对应DOM树节点的内容,具体的方式是调用图形环境对象的绘制指令。而图形环境对象负责将像素写入到位图中去,最终被显示器显示出来。在chrome里面,图形环境对象封装了一个名为Skia的2D绘图库,于是大部分绘制指令被委托给SkCanvas或者SkPlatformCanvas执行。(关于chrome如何使用Skia库,可以参考这篇文档

在软件渲染模式里,整个页面只有一个图形环境对象,所有的渲染对象被绘制到这个共享的图形环境对象中。

从渲染对象到渲染图层

每个渲染对象通过其父级渲染对象直接或间接地关联一个渲染图层。

共享相同坐标空间的渲染对象(如被相同css变形影响的哪些对象)一般情况下属于同一个渲染图层。渲染图层的存在,使得页面的元素可以按正确的顺序进行组合,使得重叠的元素、半透明的元素等等得以恰当的渲染显示。触发webkit为渲染对象创建新的渲染图层的条件有很多,具体可见源码,在方法RenderBoxModelObject::requiresLayer()或者该方法在其他类的重载方法里可以窥其一二。总的来说,对于渲染对象,只要满足下面情形之一,均会致使新的渲染图层的创建。

  1. 它是页面的根对象。(译注:应该就是document节点对应的渲染对象)
  2. 它有显式的css 位置属性。(relative,absolute,或transform)
  3. 它是透明的
  4. 它有overflow,透明遮罩或阴影(反光)
  5. 它有css滤镜
  6. 它对应的节点是3D环境或2D加速环境的canvas元素
  7. 它对应的节点是video元素

值得注意的是,渲染对象和渲染图层并不是1对1的关系。一个渲染对象所关联的渲染图层,要么是为它自身创建的渲染图层,要么是它父级对象所关联的渲染图层(如果有多个有渲染图层的父对象,取最近的那个)。(译注:同一个树路径上的多个渲染对象可能会关联同一个渲染图层)。

渲染对象是树形结构,显然渲染图层也会是树形的。渲染图层树的根节点对应页面的根元素,每个图层节点的下行图层节点(子孙节点)对应该图层视觉包含的图层(译注:视觉包含不一定是dom树上的父子、祖孙节点)。每个图层节点的子图层节点(译注:是子节点不是子孙节点)被分类存储于两个升序排序的队列(negZOrderList和posZOrderList)中,队列negZOrderList包含z轴次序比当前图层低的图层(因而这类图层在渲染图层树的位置在当前图层下面),posZOrderList反之亦然。

凑到一起:我们有很多的树

总结下前面的内容,从概念上来看我们一共有3棵并行的树结构,各自在渲染过程中的作用不尽相同:

  1. DOM树(Dom Tree),是我们最基本的模型
  2.  渲染对象树 (RenderObject Tree),其节点和DOM树的可见节点是一对一的关系。渲染过程中,渲染对象知道如果绘制其对应的Dom节点。
  3.  渲染图层树(RenderLayer Tree),由渲染图层构成。其节点和渲染对象树的节点是1对多的关系,因为一个渲染对象关联的渲染图层要么是它自身的,要么是它父级对象的。渲染图层树体现了图层之间的z轴顺序。

软件渲染模式

从根本上来说,webkit渲染页面的时候,是从根图层节点开始,进而逐级递归遍历渲染图层树。对于渲染页面的过程,Webkit的代码库里有两种迥异的编码实现过程,一个是软件渲染过程,另一个是硬件加速的渲染过程,前者是传统的渲染模式。

在软件渲染模式中,网页渲染的时候被按照渲染图层树从上而下进行,(译注:图层树体现了图层之间的z轴顺序,所以从上而下实则为从里到外,z轴越小越先被渲染。另外这里的z轴顺序和css里面的z-index有关系但并没有对应关系的,css里面的z-index是元素的,这里指的是渲染图层的。)图层被渲染的复杂过程可以参考下源码里面的RenderLayer::paintLayer()方法,为简明起见下面从这个方法里面简化提取了几个基本的步骤:

  1. 如果图层有效性校验失败,直接退出该方法。【译注:原文是图层与已坏矩形区域(the damage rect,没上下文不知道怎么翻译这玩意)发生交错,则及早退出该方法】
  2. 递归调用该图层的negZOrderList队列中的子图层的paintLayer()方法
  3. 请求该图层关联的渲染对象去绘制自身。【译注:别忘了前面提到的,渲染对象知道如何在显示介质上绘制其对应的dom节点的内容】
  4. 这个请求渲染对象绘制自身的过程,是从创建了该渲染图层的那个渲染对象开始,依据渲染对象树,对其子节点进行递归遍历绘制处理。一旦发现某个渲染对象所关联的渲染图层不是当前被渲染的图层,则停止递归遍历绘制处理。
  5. 递归调用该图层的posZOrderList队列中的子图层的paintLayer()方法

在这个模式中,渲染对象调用一个共享的图形环境对象的绘制方法,将自身绘制到目标位图中,最终被显示器显示出来。【译注:可回头看看前面的“从节点到渲染对象”小节】

要注意的是,图形环境对象本身并没有图层的概念,为了正确绘制半透明效果的图层,需要做些特别的处理:半透明效果的渲染图层在请求其关联的渲染对象绘制自身之前,会先调用图形环境对象的beginTransparencyLayer()方法。在Skia图形库实现的图形环境对象中,调用beginTransparencyLayer()方法,到导致接下来相关的渲染对象绘制自身至新的位图中,当这些渲染对象绘制完毕后,会调用一个对应的endTransparencyLayer()方法。最后,beginTransparencyLayer方法内创建的位图对象,会和paintLayer()方法创建的位图对象组合在一起,最终被显示器显示出来。

【译注:半透明效果的渲染图层,其渲染绘制的过程大概是这样的:paintLayer() => beginTransparencyLayer() => 调用关联的渲染对象的绘制方法 => endTransparenceLayer(),paintLayer和beginTransparencyLayer都会导致在内存中创建位图对象,最后两个位图对象组合在一起最终显示出来。】

从Webkit到显示器

当所有的渲染图层被绘制到一个共享位图之后,该位图需要在显示器上被显示出来。在chrome里面,位图存储于共享内存中,经由IPC(进程间通讯)传输至浏览器进程。浏览器进程负责调用操作系统的视窗API(例如windows操作系统中的HWND),将位图绘制到网页对应的标签页或窗口中去。

[转]如何组织大型JavaScript应用中的代码

本文转自: http://www.csdn.net/article/2013-04-27/2815077-code-organization-angularjs-javascript

在实际项目中,如果单纯的按model、view、controllers、template 等文件夹进行分类,等项目做大或者文件复杂后,就会出现混乱。因为不清楚引用关系。如本文所说的,找不到袜子,虽然知道在哪个抽屉里,但抽屉里内容太多,太类似。

在项目中,我们经常会修改或者维护单一的页面或者某个功能模块。如本文所说按功能模块或者页面进行文件组织确实是个不错的方法。

同时,也可以两者结合起来:先按项目功能分文件夹,里面再按MVC的架构细分文件夹。

每个项目都有一些common之类的功能模块,我建议是,如果一个功能或者函数,有超过2个以上的页面使用,就将它放于common文件夹。

===================================

本文作者Cliff Meyers是一个前端工程师,熟悉HTML5、JavaScript、J2EE开发,他在开发过程中总结了自己在应对JavaScript应用越来越庞大情况下的文件结构,深得其他开发者认可。以下为CSDN编译:

地板上堆放的衣服

首先,我们来看看angular-seed,它是AngularJS应用开发的官方入门项目,其文件结构是这样的:

  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/

看起来就像是把衣服按类型堆在地板上,一堆袜子、一堆内衣、一堆衬衫等等。你知道拐角的那堆袜子里有今天要穿的黑色羊毛袜,但你仍需要花上一段时间来寻找。

这种组织方式很凌乱。一旦你的代码中存在6、7个甚至更多的控制器或者服务,文件管理就会变得难以处理:很难找到想要寻找的对象,源代码控制中的文件也变更集变得难懂。

袜子抽屉

常见的JavaScript文件结构还有另一种形式,即按原型将文件分类。我们继续用整理衣服来比喻:现在我们买了有很多抽屉的衣柜,打算将袜子放在其中一个抽屉里,内衣放在另一个抽屉,再把衬衫整齐地叠在第三个抽屉……

想象一下,我们正在开发一个简单的电子商务网站,包括登陆流程、产品目录以及购物车UI。同样,我们将文件分为以下几个原型:models(业务逻辑和状态)、controllers以及services(HTTP/JSON端点加密),而按照Angular默认那样非笼统地归到“service”架构。因此我们的JavaScript目录变成了这样:

  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js

不错,现在已经可以通过树形文件目录或者IDE快捷键更方便地查找文件了,源代码控制中的变更集(changeset)也能够清楚地描述文件修改记录。虽然已经获得了极大的改进,但是仍有一定的局限性。

想象一下,你现在正在办公室,突然发现明天有个商务出差,需要几套干洗的衣服,因此给家里打电话告诉另一半把黑色和蓝色的西装交给清洁工,还有黑纹领带配灰色衬衫、白衬衫配纯黄领带。如果你的另一半并不熟悉衣柜,又该如何从三条黄色的领带中挑出你的正确需求?

模块化

希望衣服的比喻没有让你觉得过于陈旧,下面举一个实例:

  • 你的搭档是新来的开发者,他被要求去修补这个复杂应用中的一处bug。
  • 他扫过这些文件夹,看到了controllers、models、services等文件夹整齐地排列着,但是他仍然不清楚对象间的依赖关系。
  • 处于某些原因,他希望能够重用部分代码,这需要从各个文件夹中搜集相关文件,而且常常会遗漏某些文件夹中的对象。

信或不信,你确实很少会在新项目中重用很多代码,但你很可能需要重用登陆系统这样的整个模块。所以,是不是按功能划分文件会更好?下面的文件结构是以功能划分后的应用结构:

  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js

虽然现实世界中有空间限制,难以随意整理服装,但是编程中类似的处理却是零成本的。

现在即使是新来的开发者也能通过顶级文件夹的命名理解应用的功能,相同文件夹下的文件会存在互相依赖等关系,而且仅仅通过浏览文件组织结构就能轻易理解登录、注册等功能的原理。新的项目也可以通过复制粘贴来重用其中的代码了。

使用AngularJS我们可以进一步将相关代码组织为模块:

var userModule = angular.module('userModule',[]);
 
userModule.factory('userService', ['$http', function($http) {
  return new UserService($http);
}]);
 
userModule.factory('userModel', ['userService', function(userService) {
  return new UserModel(userService);
}]);
 
userModule.controller('loginController', ['$scope', 'userModel', LoginController]);
 
userModule.controller('registrationController', ['$scope', 'userModel', RegistrationController]);

如果我们将UserModule.js文件放到user文件夹,它就成了这个模块中使用到的对象的“manifest”,这也是适合RequireJS或者Browserify中放置某些加载指令的地方

如何处理通用代码

每个应用都会有某些代码广泛使用在多个模块中,我们常常使用名为“commom”或者“shared”的文件夹来存放这些功能代码。又该如何处理这些通用代码呢?

  1. 如果模块中的对象需要直接访问几个“通用”对象,为这些对象提供几个Facade(外观模式)。这有助于减少每个对象的依赖者,而过多的关联对象通常意味着糟糕的代码结构。
  2. 如果“通用”模块变得过于庞大,你需要将它按功能领域细分为多个子模块。确保每个应用模块只使用它需要的“通用”模块,这即是SOLID中“接口隔离原则”的变种。
  3. 在根范围($rootScope)添加实体,这样子范围也可以使用,适合多个控制器都依赖同一个对象(比如“PermissionsModel”)的情况。
  4. 在解耦两个不明确互相引用的组件时,请使用事件。Angular中Scope对象的$emit、$broadcast以及$on方法使得这种方式变得现实。控制器能够触发一个事件来执行某些动作,然后再动作结束后收到相应地通知。

原文链接: CLIFF MEYERS

backbone.marionette 资料整理

部分API说明:

上面的中文API转自 http://www.ituring.com.cn/article/31580

官方文档资料: https://github.com/marionettejs/backbone.marionette/tree/master/docs

网上的一些例子,有些文章了,可能和现有版本多少有些差异,但影响不大,对于了解marionette的用法有一定帮助:

1、http://www.smashingmagazine.com/2013/02/11/introduction-backbone-marionette/

2、这里有很多实用的例子可以参考,重点看看那个TODOMVC的那个例子:https://github.com/marionettejs/backbone.marionette/wiki/Projects-and-websites-using-marionette

3、与requireJs相结合:https://github.com/jsoverson/todomvc/tree/master/labs/dependency-examples/backbone_marionette_require

如果不熟requireJs的话 可以先看看中文API文档: http://makingmobile.org/docs/tools/requirejs-api-zh/

4、整理成PDF的文档: marionette-gentle-introduction-sample

php获取文件夹下所有文件,包含子文件夹

貌似php5.3新增了一些新的方法来实现,如 RecursiveIteratorIterator 、 scandir

不过这里还是用最土的方法实现。

/**
 * 获取文件夹下所有文件
 * @param $directory 需要获取的文件夹
 * @param bool $recursive 是否递归获取子文件夹
 * @return array
 */
function directoryToArray($directory, $recursive = false) {
	$array_items = array();
	if(!is_dir($directory)) return "$directory folder does not exist";
	if ($handle = opendir($directory)) {
		while (false !== ($file = readdir($handle))) {
			if ($file != "." &amp;&amp; $file != "..") {
				if (is_dir($directory. "/" . $file)) {
					if($recursive) {
						$array_items = array_merge($array_items, directoryToArray($directory. "/" . $file, $recursive));
					}
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				} else {
					$file = $directory . "/" . $file;
					$array_items[] = preg_replace("/\/\//si", "/", $file);
				}
			}
		}
		closedir($handle);
	}
	return $array_items;
}

调用方法:

$files = directoryToArray('../project/353438199e',true);
var_dump($files);

wampp的alias使用mod_rewrite

使用wampp新建alias的时候,默认是无法使用mod_rewrite模块。

需要做一些配置:

1、wampp开启mod_rewrite

1

2、在alias配置文件里加入如下代码,请自行根据情况修改。

Alias /labs/ "E:/ecc_ecd_rep/labs_proj/trunk/" 

<Directory "E:/ecc_ecd_rep/labs_proj/trunk/">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order deny,allow
    allow from all
</Directory>

其中关键的是这句 AllowOverride All 允许使用mod_rewrite模块

3、在.htaccess文件中,需要使用 RewriteBase ,不然rewrite无法生效。

举个例子:

RewriteEngine On

# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
RewriteBase /labs/jdpat/import/api/

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

加上这句 RewriteBase /labs/jdpat/import/api/ 即可。

jQuery使用 Animate + scrollTop 实现页面滑动效果

使用jquery的Animate 方法可以实现页面上下滑动,以往常用的写法是:

$('html, body').animate({
    scrollTop: '0px','fast', function(){
       
    }
});

前几天在写页面滑动插件的时候,需要在animate后执行回调。如下:

$('html, body').animate({
    scrollTop: '0px','fast', function(){
       //这里的代码执行了两次
       $('body').trigger('scrollDone');
    }
});

于是发现,回调内执行了两次。之前一直都没注意到这个问题。

其原因主要是使用了 $('html, body') 作为animate的dom,这样做的目的是为了兼容各浏览器。

webkit 内核的浏览器使用 body 进行滑动,而其他浏览器则使用 html 进行滑动。

这里偷懒的使用了 $('html, body') ,虽然解决了兼容性问题,但是却导致 animate 回调两次的问题。因此该方案并不完美。

于是,可以做下判断,解决兼容性及回调问题:

$($.browser.webkit ? "body": "html").animate({
    scrollTop: '0px','fast', function(){
        $('body').trigger('scrollDone');
    }
});

在jquery 1.9版本后,已经不支持 $.browser.webkit 的方法进行浏览器类型检测了,需要的话,自己通过ua判断下即可