参加 D2 2015 技术论坛感想

上周末去杭州阿里巴巴西溪园区参加了 2015 年的 D2 论坛,正好是第十届。

我听了一天的主会场,并在知乎上实时直播: 参加第十届D2前端技术论坛,你有什么收获? – yuanyuanVivian 的答案 。在这里谈谈自己的心得感想吧!

NodeJs 开始绽放

主会场的几场分享都还算挺不错。其中 NodeJS 相关的两场分享,一个是 Tmall 的线上经验,一个是 QZone 的线上经验,都是耳熟能详的大网站。我认为这代表 NodeJs 的线上能力已经得到了足够的验证,可以加快推广了继续阅读参加 D2 2015 技术论坛感想

怎样制作中间镂空的遮罩

SVG 版

最方便,兼容性最好的办法。方便镂空多个洞。代码来自 www.zhoumingzhi.com

镂空一个洞的代码


<div id="container" style="position: relative; margin: 550px 0 0 50px;">
<svg style="position: absolute;" width="400" height="280">
<defs>
<mask id="mask3">
<rect x="0" y="0" width="100%" height="100%" style="stroke:none; fill: #ccc"></rect>
<circle id="circle1" cx="100" cy="100" r="50" style="fill: #000" />
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" style="stroke: none; fill: #ccc; mask: url(#mask3)"></rect>
</svg>
<img src="http://img6.cache.netease.com/cnews/2014/11/3/20141103100737855b7.jpg" />
</div>

镂空多个洞的代码


<div id="container" style="position: relative;">
<svg style="position: absolute;" width="400" height="280">
<defs>
<mask id="mask3">
<rect x="0" y="0" width="100%" height="100%" style="stroke:none; fill: #ccc"></rect>
<circle id="circle1" cx="100" cy="50" r="50" style="fill: #000" />
<circle id="circle1" cx="300" cy="100" r="50" style="fill: #000" />
<circle id="circle1" cx="100" cy="200" r="50" style="fill: #000" />
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" style="stroke: none; fill: #ccc; mask: url(#mask3)"></rect>
</svg>
<img src="http://img6.cache.netease.com/cnews/2014/11/3/20141103100737855b7.jpg" />
</div>

Canvas 版

当镂空的形状比较复杂时,用 Canvas 的 globalCompositeOperation 是最终极的办法。当然 Canvas 也会有兼容性问题。

CSS clip 版

优点是所有浏览器都支持。缺点是更像是裁剪,只能遮罩特定的对象,而且只能镂空一个洞。

CSS 代码


#img1
{
position:absolute;
clip:rect(0px 50px 200px 0px)
}

HTML 代码


<img border="0" id="img1" src="/i/eg_bookasp.gif" width="120" height="151">

-webkit-mask 版

有前缀,而且其作用就是相当于在上面盖了一个图片,鸡肋一样的属性。如果直接盖一个图片的话,可以兼容所有场景和所有浏览器。-webkit-mask 实例代码来自 http://webengine.sinaapp.com/jian/

{
-webkit-mask: url(http://webengine.sinaapp.com/jian/images/mask.png) no-repeat center -10px;
-webkit-mask-size: 500px 750px;
}

CSS3 版

用 box-shadow ,代码如下:

position: fixed;
left: 150px;
top: 35px;
width: 100px;
height: 100px;
border-radius: 100px;
box-shadow: rgba(0,0,0,.8) 0px 0px 0px 2005px;
z-index: 100;

缺点是只能镂空一个洞。

IE8 及以下的传统版

有点是兼容性最强,形式随意,可以镂空任意个洞。缺点是给人感觉这种工作很 low,而且事实上确实乏味、辛苦而又没有技术含量。
在镂空区域的左、上、右、下分别放一个半透明 div 作为遮罩。
div 背景用滤镜半透明, IE7+ 可以用 png8 半透明图片来代替。

锁定响应式设计的最小宽度

小于 480 等比缩放,大于 480 自适应布局。

不知道有什么用,大概只有在设计师和 pm 都没想好怎么响应式的时候,给临时没想到的细节作为善后用的吧!

function effectiveDeviceWidth() {
    var deviceWidth = window.orientation == 0 ? window.screen.width : window.screen.height;
    // iOS returns available pixels, Android returns pixels / pixel ratio
    // http://www.quirksmode.org/blog/archives/2012/07/more_about_devi.html
    if (navigator.userAgent.indexOf('Android') >= 0 && window.devicePixelRatio) {
        deviceWidth = Math.floor(deviceWidth / window.devicePixelRatio);
    }
    return deviceWidth;
}
var viewPortString = effectiveDeviceWidth() > 480 ?
'<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />'
: '<meta name="viewport" content="target-densitydpi=device-dpi,width=480,user-scalable=0" />';
document.write(viewPortString);

[笔记]原生 JavaScript + CSS3 模拟 iOS 触摸左右切换效果

要实现的 iOS 原生滚动特征

  1. 手指触摸移动 slider 主体时,主体随指尖滑动,可以用 JavaScript 做指尖跟随;
  2. 手指触摸移动距离不到 slider 一页的宽度一半时放开,slider 主体平滑归位,可以用 CSS3 的 transition;
  3. 接上条,反之,slider 平滑切换到上一页/下一页,也是用 CSS3 的 transition;
  4. 当手指在屏幕上快速向左/右移动(假设触摸时间为 0.2 秒以内),不论触摸距离的长短,都试图进行翻页操作。直接用 JavaScript 监听触摸开始和结束所用时间和位移即可
  5. 当 slider 到头,试图翻页失败时,平滑归位,还是用 CSS3 transition 就行。

实现要点分析

平滑归位和切换需要使用到 CSS3 的 transition,触摸事件用 ontouchstart 、 ontouchmove 和 ontouchend 。 slider 本身的移动用 CSS 的 margin-left 。

  1. 平滑归位/切换

    f-trans 类是 CSS3 transition 实现,代表 CSS 属性值改变都不是瞬间完成,包括 margin-left,而是在 0.2 秒内平滑过度完成。

    .f-trans {-moz-transition-property: all; -moz-transition-duration: .2s; -moz-transition-timing-function: linear; -webkit-transition-property: all; -webkit-transition-duration: .2s; -webkit-transition-timing-function: linear; -o-transition-property: all; -o-transition-duration: .2s; -o-transition-timing-function: linear; transition-property: all; transition-duration: .2s; transition-timing-function: linear;}

  2. Slider 主体跟随手指移动

    ontouchstart 是触摸开始那个时间点触发,ontouchmove 是触摸过程中连续触发,ontouchend 是触摸结束时触发,前两者都可以用 e.pageX 获取当前手指的水平位置。三个事件配合使用可以监测触摸状态。

    var t = this;
    var touchBeginHandler = function(e){
    t.touchStartX = e.pageX;
    t.touchStartTime = new Date().getTime();
    },
    /* 触摸开始 - 记录触摸开始时的水平位置和时间 */
    touchMoveHandler = function(e){
    e.preventDefault();
    NTES(t.slideContentBody).removeCss("f-trans"); //因为要频繁改动 marginLeft 所以暂时禁用 transition
    var deltaX = (e.pageX - t.touchStartX); //将 touchMove 过程中的 e.pageX 减去触摸开始时的 e.pageX 就是当前水平的位移
    t.slideContentBody.style.marginLeft = (t.currentMarginLeft + deltaX) + "px"; //移动过程中实时改变 marginLeft 值
    t.touchEndX = e.pageX; //记录触摸结束时的水平位置
    },
    /* 触摸过程中 - slider 主体随指尖移动,并记录触摸结束时的位置 */
    touchEndHandler = function(e){
    var deltaX = t.touchEndX - t.touchStartX, deltaTime = (new Date().getTime()) - t.touchStartTime;
    NTES(t.slideContentBody).addCss("f-trans"); //需要平滑归位或翻页,启用 transition
    if(deltaTime < 200){ //快速触摸移动时
    deltaX < 0 ? t.slideByStep(1) : t.slideByStep(-1);
    }else{
    deltaX < 0 ? t.slideByStep(Math.round(Math.abs(deltaX)/t.slideImageWidth)) : t.slideByStep(-Math.round(Math.abs(deltaX)/t.slideImageWidth)); //用 Math.round 四舍五入,即移动幅度超过 0.5 宽度时翻页
    }
    };
    /* 触摸结束 - 判断整个触摸过程的时间和水平位移,决定是否翻页并执行 */
    t.slideContentBody.ontouchstart = touchBeginHandler;
    t.slideContentBody.ontouchmove = touchMoveHandler;
    t.slideContentBody.ontouchend = touchEndHandler;
    /* 绑定以上事件 */

线上 DEMO

请用带触摸屏的设备访问:touch_slider_demo

完整 js 代码

注:NTES 是一个方便快速获取 DOM 元素的轻量库,其 Filter 语法和 jQuery 几乎相同。


/**
 *  适合 iPad 的滚动函数
 */
 (function (window, undefined) {
    var $ = window.NTES || {};
    if (!$.ui) {
        $.ui = {};
    }
    $.ui.iPadSlide = function (body, options){
        if (!arguments.length) {    // 空对象时跳出
            return;
        }

        var t = this;       
        t.body = body;
        for(var argument in options){
            t[argument] = options[argument];
        }

        t.init();
    }
    $.ui.iPadSlide.prototype = {
        body:null,
        ctrlBtnArr:[],
        tipTitleArr:[],
        slideImageArr:[],
        slideImageWidth:0,
        slideContentBody:null,
        prevBtn:null,
        nextBtn:null,
        currentClassName:"on",      
        currentIndex:0,
        currentMarginLeft:0,
        touchStartX:0,
        touchEndX:0,
        touchStartTime:0,
        MIN_INDEX:0,
        MAX_INDEX:0,
        slideByStep:function(step){
            this.currentIndex += step;
            this.currentIndex = this.currentIndex > this.MIN_INDEX ? (this.currentIndex < this.MAX_INDEX ? this.currentIndex : this.MAX_INDEX) : this.MIN_INDEX ;
            this.currentMarginLeft = 0 - this.slideImageWidth * this.currentIndex;
            this.slideContentBody.style.marginLeft = this.currentMarginLeft + "px";
            NTES(".on", scrollObj).removeCss("on");
            NTES(this.ctrlBtnArr[this.currentIndex]).addCss(this.currentClassName);
            NTES(this.tipTitleArr[this.currentIndex]).addCss("on");
        },
        init:function(){
            var t = this;

            t.slideImageWidth = t.slideImageArr[0].offsetWidth;
            t.MAX_INDEX = t.slideImageArr.length - 1;
            t.slideContentBody.style.width = t.slideImageWidth * (t.MAX_INDEX + 1) + "px";

            t.prevBtn.onclick = function(){
                t.slideByStep(-1);
            };
            t.nextBtn.onclick = function(){             
                t.slideByStep(1);
            };
            t.ctrlBtnArr.each(function(){
                this.onclick = function(){ 
                    t.slideByStep(parseInt(this.getAttribute("data-index")) - 1 - t.currentIndex);
                };
            });
            var touchBeginHandler = function(e){
                t.touchStartX = e.pageX;
                t.touchStartTime = new Date().getTime();
            },
            touchMoveHandler = function(e){
                e.preventDefault();
                NTES(t.slideContentBody).removeCss("f-trans");
                var deltaX = (e.pageX - t.touchStartX);
                t.slideContentBody.style.marginLeft = (t.currentMarginLeft +  deltaX) + "px";
                t.touchEndX = e.pageX;
            },
            touchEndHandler = function(e){
                var deltaX = t.touchEndX - t.touchStartX, deltaTime = (new Date().getTime()) - t.touchStartTime;        
                NTES(t.slideContentBody).addCss("f-trans");
                if(deltaTime < 200){
                    deltaX < 0 ? t.slideByStep(1) : t.slideByStep(-1);
                }else{
                    deltaX < 0 ? t.slideByStep(Math.round(Math.abs(deltaX)/t.slideImageWidth)) : t.slideByStep(-Math.round(Math.abs(deltaX)/t.slideImageWidth));                 }           };              t.slideContentBody.ontouchstart = touchBeginHandler;            t.slideContentBody.ontouchmove = touchMoveHandler;          t.slideContentBody.ontouchend = touchEndHandler;        }   };   })(window); var scrollObj = NTES("#slideshow"); new NTES.ui.iPadSlide(scrollObj, {     ctrlBtnArr:NTES("> .btns > ul > li", scrollObj),
    prevBtn:NTES("> .btns > .prev", scrollObj)[0],
    nextBtn:NTES("> .btns > .next", scrollObj)[0],
    tipTitleArr:NTES("> .tip-notice > .slideshow_info", scrollObj),
    slideImageArr:NTES("> .img > div > a", scrollObj),
    slideContentBody:NTES("> .img > div", scrollObj)[0]
});

顽固守旧者的福音:用 CSS3 MediaQueries JS 和 CSS3 做跨浏览器弹性布局

为什么

本文诠释一种纯 CSS3 + 现成 javaScript 的兼容旧浏览器的弹性布局的制作方法。

做弹性布局时,为了兼容性(IE6、IE7、IE8),一般都是用 JavaScript 绑定 window.onresize 。这样虽然行得通,却有点浪费支持 CSS3 的先进浏览器。

而引入 CSS3 MediaQueries JS ,就能使低级浏览器完美支持 css3 的 media query 。一套代码,处处可用,把兼容性问题都丢给轮子去解决吧!

CSS3 MediaQueries JS 开发于 2008 年。支持 IE 5+, Firefox 1+ and Safari 2 。IE9+, Firefox 3.5+, Opera 7+, Safari 3+ 和 Chrome 已经原生支持 media query 了。

使用方法

在 body 之前插入如下代码,条件性判断应该也可以省略不要:

<!--[if lt IE 9]><script src="css3-mediaqueries.js"></script><![endif]—>

注意事项

  1. 不支持 @import ;
  2. 对 <link> 和 <style> 标签内的 media 属性无效;
  3. 以上两条来自官方 readme ;
  4. 不要用一些奇怪的、明显冲突的 CSS;
  5. 如果布局乱了,先从自己 CSS 代码的浏览器兼容性找原因;
  6. 以上两条为博主 debug 的经验。

示例

网易博客时事二级页 http://blog.163.com/special/001264EF/sec-page-community.html
宽屏四列布局窄屏三列布局

地址

CSS3 MediaQueries JS 最早的项目地址是 http://code.google.com/p/css3-mediaqueries-js/ ,现在已经在 Github 上经过很多人的修改了,目前可以在此访问到使用说明和源代码: https://github.com/livingston/css3-mediaqueries-js

参考文档