微信H5页面框架设计 h5_slide.js用法介绍

作者:袖梨 2022-06-25


1. 习惯所致,通常开始写代码之前写一点准备代码, 完全是为了减少代码量方便阅读


$ = function(sel, holder){ 
    return [].slice.call( (holder||document).querySelectorAll(sel) );
};
$.on = function(dom, eventType, f){
    [].concat(dom).forEach(function(d){
        eventType.split(/W+/).forEach(function(type){
            d.addEventListener(type, f ,false);
        });
    });
    return $;
};


2.  第一步创建一个类似swiper的切换效果,当然我们只是操作纵向,在CSS3中使用 transform 位置变换 + 过渡效果,很容易实现,关键是需要在事件绑定时候进行计算。另外,如果所有页面都是100%宽100%高的话自然不需要考虑内部滚动事件,我们这里需要考虑更多的事情,所以最好使用额外的一个透明层做事件绑定,然后操作与真实的section内容。

container = $("#container")[0]
holder = document.createElement("div"),
holder.style.cssText = "position: absolute;width: 100%;height: 100%;left: 0;top: 0;z-index: 1;";
container.appendChild(holder);


3. 切换的方法我暂且定做  transform_set(dom/*当前section*/, i/*前移还是后移i个section*/); 大概就是酱紫了

var H = document.documentElement.clientHeight;
        if( "undefined" === typeof i ){
            i = dom | 0;
        }else if( "number" != typeof dom ){
            i = sections.indexOf(dom) + i;
        }
        i = Math.min( Math.max( 0, i ), sections.length - 1);
        
css_model = function(h,l){
            return "-webkit-transform: translate3d("+(l|0)+"px, "+(h|0)+"px, 0); transform: translate3d("+(l|0)+"px, "+(h|0)+"px, 0);";
        };
        
sections.forEach(function(section, index){
                switch(index){
                    case i:
                        current_index = i;
                        section.style.cssText = css_model();
                        break;
                    case i+1:
                        section.style.cssText = css_model(H);
                        break;
                    case i-1:
                        section.style.cssText = css_model(-H);
                        break;
                    default:
                        section.style.cssText = css_model( (index > i) ? H : -H );
                }
            });


4. 注意事件绑定的时候需要判断方向,来确定切换是前移还是后移


// 事件
    var startTy, curTy, endTy, autoStep;
    $.on(holder, "touchstart", function(e){
        var touch = e.touches[0];
            startTy = curTy = touch.clientY;
    }).on(holder, "touchend", function(e){
        var touch = e.changedTouches[0],  //只判断单点触控
                endTy = touch.clientY;
        transform_set(sections[current_index], startTy > endTy ? 1 : -1);
        startTy = curTy = endTy = 0;
    }).on(document, "keydown", function(e){    // key 和 mouse 事件只是为了方便PC浏览器
        switch(e.keyCode){
            case 32:
            case 39:
            case 40: transform_set(sections[current_index], 20); break;
            case 37:
            case 38: transform_set(sections[current_index], -20); break;
        }
    }).on(document, "mousewheel", function(e){
        transform_set(sections[current_index], e.wheelDeltaY < 0 ? 20: -20);
    });


这节先到这里,事实上已经完成了一个方便的swiper效果。  然并卵,复杂的内容还木有开始(scrollTop位置检测,内置元素的相对transform设置和参数设计,动画元素分组,图片loading,按钮元素模拟穿透等), 详情关注本系列文章。

在前文中,我们简单讲了一个移动端整页切换的效果处理,本文主要来识别滚屏动画的进度接口设置。

 

考虑到单页面内滚动高度可能随着不同屏幕分辨率有所不同,我们设计的接口只是提供一个滚动百分比的参数,这个参数的值可以从 0 ~ 1.

run(per/*滚动百分比*/,dir/*滚动方向*/) // 滚动方向不一定用得上,暂且留着。

我们将这个run假定注册在原生的dom元素上面,再加一个run的className来识别,那么相关代码就会如下:


$.on(holder, "touchstart", function(e){
        var touch = e.touches[0];
            startTy = curTy = touch.clientY;
}).on(holder, "touchmove", function(e){
        var touch = e.changedTouches[0],
                endTy = touch.clientY;
        run( curTy - endTy, e);
        curTy = endTy;
});
    function run(dir, e){
        e.stopPropagation();
        e.preventDefault();
        var _this = sections[current_index];
        var st = _this.scrollTop,
            ch = _this.clientHeight,
            sh = _this.scrollHeight;
        scrollTy = st;
        st = _this.scrollTop = _this.scrollTop + dir;
        
        // 这里还有判断跳切换页面的逻辑
        
        $(".run", _this).forEach(function(r){  // 对当前section中的所有.run循环,判断是否存在run方法
            if( typeof r.run === "function" ){
                var per = _this.scrollTop == (sh-ch) ? 1 : _this.scrollTop / (sh-ch);
                r.run(per, dir);  // 存在这个方法就执行它, 把per带上。 per计算来自于当前滚动高度÷最大滚动高度
            }
        });
    }


完事儿,我们需要达到的效果其实就是酱紫的:


document.getElementById('logo').run = function(per, dir){
            var W = document.documentElement.clientWidth;
            this.style.cssText =
                "-webkit-transform: translate3d("+( (140+W) * per |0)+"px, "+(this.parentNode.scrollTop|0)+"px, 0); "
                +"transform: translate3d("+((140+W) * per |0)+"px, "+(this.parentNode.scrollTop|0)+"px, 0);";
        };


绑定一个run,就能按照规则执行滚动动画咯。

 

demo: http://cardistry.cn/hero/demo.html

建议用手机查看,或者使用chrome的移动端调试控制台。

查看源代码的时候你会发现如下代码:


.set = function(set,w,h,W,H){
                set.begin({x:0,y:0,rotate: 0, scale:1}).then({
                    per: .3,
                    x: w-W,
                    y: 300,
                    rotate: 180,
                    scale: .5
                }).then({
                    per: .5,
                    x: -100,
                    y: 200,
                    opacity: 1,
                    rotate: 360,
                    scale: 1
                }).then({
                    per: .7,
                    x: -100,
                    y: 200,
                    opacity: 1,
                    rotate: 240,
                    scale: 1.5
                }).then({
                    per: .8,
                    x: -100,
                    y: 200,
                    opacity:.2,
                    rotate: 120,
                    scale: 1
                }).end({x:w-W,y:H-h,rotate:0, scale:1});
            };


看起来很高大上的样子, 写动画好像更加方便了。这个其实就是基于上面run的一个线性封装,具体实现

前文提到,在一版页面内部滚动时候我们使用一个run方法传入参数per执行,期望接口使用者方便调用。

然而事实上, 还有一些个通用的执行过程可以进行抽离,例如我们期望通过如下方式书写动画:

.set = function(set,w,h,W,H){
                set.begin({x:0,y:0,rotate: 0, scale:1}).then({
                    per: .3,
                    x: w-W,
                    y: 300,
                    rotate: 180,
                    scale: .5
                }).then({
                    per: .5,
                    x: -100,
                    y: 200,
                    opacity: 1,
                    rotate: 360,
                    scale: 1
                }).then({
                    per: .7,
                    x: -100,
                    y: 200,
                    opacity: 1,
                    rotate: 240,
                    scale: 1.5
                }).then({
                    per: .8,
                    x: -100,
                    y: 200,
                    opacity:.2,
                    rotate: 120,
                    scale: 1
                }).end({x:w-W,y:H-h,rotate:0, scale:1});
            };


看起来像CSS3-animation实现过程呶。  建立在run方法已经可以被检测执行的基础上,我们可以想办法把这些数据构建一个run方法来:

 

1. 处理过程存储和参数部分:

var trace = [],
                        set = {
                            begin: function(d){d.per = d.per || 0;trace.push(d);return this;},
                            end: function(d){d.per = d.per || 1;trace.push(d);return this;},
                            then: function(d){trace.push(d);return this;}
                        },
                        w = r.offsetWidth,
                        h = r.offsetHeight,
                        W = document.documentElement.clientWidth,
                        H = document.documentElement.clientHeight;


2. 执行dom的set方法,将过程存储到trace数组中:

r.set(set,w,h,W,H);

3. 封装成为run方法(过程中依次循环数组,当属于某区间时候计算区间内属性的线性变换并且赋值)支持的属性有限,位置变换移动端不能使用left、top:


r.run = function(per, dir){
                        for (var i = 1; i < trace.length; i++) {
                            if( trace[i-1].per <= per && trace[i].per >= per ){
                                var prev = trace[i-1],
                                    next = trace[i],
                                    rate = (per - prev.per) / (next.per - prev.per),
                                    _x = (prev.x+(next.x-prev.x)*rate) || 0,
                                    _y = (prev.y+(next.y-prev.y)*rate) + sections[current_index].scrollTop || 0,
                                    _o = (prev.opacity+(next.opacity-prev.opacity)*rate),
                                    _s = (prev.scale+(next.scale-prev.scale)*rate),
                                    _r = (prev.rotate+(next.rotate-prev.rotate)*rate),
                                    _s = isNaN(_s) ? 1 : _s;
                                    _r = isNaN(_r) ? 0 : _r;
                                var cssText =
                                    "-webkit-transform: scale("+_s+") translate3d("+_x+"px, "+_y+"px, 0) rotate("+_r+"deg);"
                                    +"transform: scale("+_s+") translate3d("+_x+"px, "+_y+"px, 0) rotate("+_r+"deg);"
                                    + ( isNaN(_o) ? "" : ("opacity: " + _o+ ";") );
                                this.style.cssText = cssText;
                                return ;
                            }
                        };
                    };

相关文章

精彩推荐