Jquery实现抖音滑屏功能

636 阅读2分钟

效果预览

抖音滑屏.gif

技术栈

本文示例代码使用Jquery + ejs + scss编写。

页面结构

<!-- 父级 -->
<div class="wrapper">
    <div class="SlideMain">
        <!-- 子级 -->
        <div class="videoMain" >
            <video 
                id="video_{{ index }}"
                src="{{ src }}"
                muted="{{ muted }}"
                loop
                class="video"
                autoplay="{{ play }}"
            ></video>
            <div class="videoTitle" id="{{ index }}">
                <p class="title">{{ title }}</p>
            </div>
        </div>
        <!-- 子级 -->
    </div>
</div>
<!-- 父级 -->

每一个视频都占满整个可视区域,文字标题则定位在视频上面,css就不过多的进行说明。此示例代码是利用ejs模板引擎,配合tplReplace方法将 {{}} 中的内容进行替换。

注意上面注释的父级以及子级,在示例代码中我将这两块抽离成两个组件并组合,下面描述父级子级通信就是对此进行描述。

tpl( data ) {
    let list = '';
    data.forEach( ( item, index ) => {
        list += tplReplace( tpl, {
            src: item.src,
            title: item.title,
            muted: item.muted,
            play: index == 0 ? true : false,
            index
        })
    })
    // 将数据进行存储
    _obj = data;
    // 返回html结构到页面 进行渲染
    return list;
}

// 使用正则将扩号里面的内容进行替换 并返回
function tplReplace (template, replaceObject) {
    return template().replace(/{{(.*?)}}/g, (node, key) => {
        return replaceObject[key.trim()];
    });
}

实现页面滑动

实现页面滑动主要是利用touch事件计算出Y轴,操作父级的dom动态添加transform

渲染页面后我们需要获取video的dom将第一个展示的视频进行播放 其余未展示的视频都暂停,如果不进行此操作当用户进入页面所有的视频都会同时播放,声音也会外漏。

VideoStatus() {
    VideoCtx = $('video');
    for( let i = 0; i < VideoCtx.length; i++ ) {
        // 第一个视频默认播放 其他隐藏的视频则暂停 
        i == 0 ? VideoCtx[i].play() : VideoCtx[i].pause();
    }
}

touch事件如下

start( e ) {
    // 存储起始滑动的Y轴距离
    begin = e.touches[0].pageY;
    // 记录当前Y轴的距离
    recorBegin = wholeY;
},

move( e, Movefn ) {
    // 移动的距离 = 移动中的Y轴 - 起始的Y轴距离
    let MoveY = e.touches[0].pageY - begin;
    // wholeY 则是滑动页面的时候距离
    wholeY = MoveY + recorBegin;
    // 次方法是从父级传递过来的 在子组件执行并传递当前移动Y轴的坐标
    Movefn( wholeY );
}

注意: 子级做滑动的具体逻辑,父级则动态添加移动的距离。

  • 为什么需要recorBegin这个参数?

    在滑动切屏时当我们快速滑动然后放开时页面会定格在中间,既没切到下一个视频也不会回到上一个视频,所以在这里我们需要做个判断,比如这个视频分4份 只要我滑动超过了4/1则代表我可以滑到下一个视频,如果没有超过4/1则退回我当前的这条视频,就像效果示例的回弹。 那么我们要回弹到那个Y轴的坐标上呢?recorBegin就是我们要回弹的坐标。

  • 过渡属性的添加时机

    当切屏不成功页面回弹的时候如果不加过渡会闪的一下就回到原位了,如果我们一开始就加上过渡属性时在页面滑动的时候就会进行过渡 会导致滑动很卡。所以总结出:在滑动的时候移除transition在放开的时候加上transition

MoveBox( even ) {
    // 滑动前移除过渡样式
    SlideMain.css( "transition", "" );
    // 控制滑动距离
    SlideMain.css({ "transform": `translateY(${even}px)` });
},
EndBox( even ) {
    // 结束滑动时 添加过渡 解决生硬切换
    SlideMain.css({ "transition": "transform .2s" });
}

上下切屏是否成功?切到第一个或者最后一个时的边界处理?切屏成功时暂停当前视频播放下一条视频?都在touchend事件进行处理

end( e, Movefn, Endfn ) {
    // 滑动结束的距离 - 起始滑动的距离
    const endY = e.changedTouches[0].pageY - begin,
            Index = parseInt(e.target.id),                       // 当前滑动的下标
            clientY = $(document.body).height();                // 获取页面可视区域

    // 当滑动的距离超过整个页面可视区域的4/1 就切到下一个视频
    if ( Math.abs( endY ) < clientY / 4 ) {
        this.defaultInit( Movefn );
    } else {
        // 切换到下一屏  endY如果是负的 则代表在往下滑
        if ( endY < 0 ) {
            this.SlideDown( Movefn, Index, clientY );
        } 
        // 切换到上一屏 endY如果是正的 则代表在往上滑
        else {         
            this.SlideUp( Movefn, Index, clientY );
        }
    }
    // 触发父级方法
    Endfn();
},

SlideDown( Movefn, Index, clientY ) {
    // 如果还有下一条视频 就能切入下一条视频
    if ( _obj[Index + 1] ) {
        // 切屏后暂停当前视频
        VideoCtx[Index].pause();
        // 切屏后播放下一条视频
        VideoCtx[Index + 1].play();
        wholeY = recorBegin - clientY;
        Movefn( wholeY );
    } else {
        this.defaultInit( Movefn );
    }
},

SlideUp( Movefn, Index, clientY ) {
    // 如果还有上一条视频 就能切到上一条视频
    if ( _obj[Index - 1] ) {
        // 切屏后暂停当前视频
        VideoCtx[Index].pause();
        // 切屏后播放上一条视频
        VideoCtx[Index - 1].play();
        wholeY = recorBegin + clientY;
        Movefn( wholeY );
    } else {
        this.defaultInit( Movefn );
    }
},

// 切屏失败后初始化距离 往回弹
defaultInit( Movefn ) {
    wholeY = recorBegin;
    Movefn( recorBegin );
}

最后

文字可能表达的不是很清楚,大致的思路就是这样。源码在这有需要的朋友自提。