3

最近公司要开发一套线上付费的会员App,是和原生一起混合式的开发,有一个会员权益模块是全部使用H5开发,想给大家看下需要做成的案例:

clipboard.png

其实没什么难点,主要就是功能有:
1、顶部导航栏可以左右滑动,点击某一个栏目按钮内容跟着切换;
2、并且加载一次之后,第二次就重新加载;
3、被点击的栏目按钮居中显示,左右会跟随点击的位置滚动
4、手机物理返回按键对路由的影响

感觉自己表达不是特别的清楚,来一张动图把:

图片描述

好了 废话不多说 开始说代码相关的东西了:

顶部导航的实现方案:

使用 swiper3 实现导航回弹和左右滑动效果

clipboard.png

html内容:

<div class="rightsProfile">
            <div class="rightsProfile_head">
                <div id="nav" class="swiper-container">
                    <ul class="swiper-wrapper">
                        <li :key="i" v-for="i in myNavBar" :class="{'active-nav':i.active}" class="swiper-slide">
                            {{i.title}}
                        </li>
                    </ul>
                </div>
            </div>
        </div>

我使用了vuejs的循环渲染li

swiper.js的调用:

               var myNav = new Swiper('#nav', {
                    freeMode: true,
                    freeModeMomentumRatio: 0.5,
                    slidesPerView: '3',
                });
                let swiperWidth = myNav.container[0].clientWidth
                let maxTranslate = myNav.maxTranslate();
                let maxWidth = -maxTranslate + swiperWidth / 2

                myNav.on("tap",function(swiper,e){
                    
                    let slide = swiper.slides[swiper.clickedIndex]
                    let slideLeft = slide.offsetLeft
                    let slideWidth = slide.clientWidth
                    let slideCenter = slideLeft + slideWidth / 2;

                    // 被点击slide的中心点
                    myNav.setWrapperTransition(300)
                    if (slideCenter < swiperWidth / 2) {
                        myNav.setWrapperTranslate(0)
                    } else if (slideCenter > maxWidth) {
                        myNav.setWrapperTranslate(maxTranslate)
                    } else {
                        let nowTlanslate = slideCenter - swiperWidth / 2
                        myNav.setWrapperTranslate(-nowTlanslate)
                    }

                })

上面的代码就可以实现1、3的要求了 我上传的代码不是很全,细节代码我会附上github地址。

我们接下来看第2个要求,就是点击加载一次之后,再次点击不会重新加载,这里我使用了vue-router

结构如下:

<template>
  <div id="page">

          <Loading :isLoading="isLoading"></Loading>

        <div class="rightsProfile">
            <div class="rightsProfile_head">
                <div id="nav" class="swiper-container">
                    <ul class="swiper-wrapper">
                        <li :key="i" v-for="i in myNavBar" :class="{'active-nav':i.active}" class="swiper-slide">
                            {{i.title}}
                        </li>
                    </ul>
                </div>
            </div>
        </div>

        <keep-alive>
            <router-view v-if="$route.meta.keepAlive">
                <!-- 这里是会被缓存的视图组件,比如 page1,page2 -->
            </router-view>
        </keep-alive>

        <router-view v-if="!$route.meta.keepAlive">
            <!-- 这里是不被缓存的视图组件,比如 page3 -->
        </router-view>
  </div>
</template>

<style lang="less">
    #nav{
        height: 100%;
        .swiper-wrapper{
            height: 100%;
            li{
                text-align: center;
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
                &.active-nav{
                    color:#fff;
                    background:#ddd !important;
                }
                &.active-nav a{
                    color:#fff;
                    background:#ddd !important;
                }
            }
        }
    }
    
</style>

<script>
    import Loading from "@/components/loading";
    export default{
        data(){
            return{
                myNavBar:[{
                        title:"移动机具",
                        active:true,
                        mark:"mpos"
                    },{
                        title:"8折话费",
                        active:false,
                        mark:"payPhoneBill"
                    },{
                        title:"5折看电影",
                        active:false,
                        mark:"movieTicket"
                    },{
                        title:"家用",
                        active:false
                    },{
                        title:"家用电器",
                        active:false
                    },{
                        title:"家用",
                        active:false
                    }],
                routerName:""
            }
        },
        components:{
            Loading
        },
        watch: {
            $route(to, from) {
                // console.log(this.$route.name);
                let routerName = this.$route.name;
                this.myNavBar.map((el)=>{
                    el.active = false;
                    if(el.mark==routerName){
                        el.active = true;
                    }
                });
            }
        },    
        mounted(){

            this.$nextTick(()=>{

                let that = this;
                var myNav = new Swiper('#nav', {
                    freeMode: true,
                    freeModeMomentumRatio: 0.5,
                    slidesPerView: '3',
                });
                let swiperWidth = myNav.container[0].clientWidth
                let maxTranslate = myNav.maxTranslate();
                let maxWidth = -maxTranslate + swiperWidth / 2

                myNav.on("tap",function(swiper,e){
                    
                    let slide = swiper.slides[swiper.clickedIndex]
                    let slideLeft = slide.offsetLeft
                    let slideWidth = slide.clientWidth
                    let slideCenter = slideLeft + slideWidth / 2;

                    // 被点击slide的中心点
                    myNav.setWrapperTransition(300)
                    if (slideCenter < swiperWidth / 2) {
                        myNav.setWrapperTranslate(0)
                    } else if (slideCenter > maxWidth) {
                        myNav.setWrapperTranslate(maxTranslate)
                    } else {
                        let nowTlanslate = slideCenter - swiperWidth / 2
                        myNav.setWrapperTranslate(-nowTlanslate)
                    }

                    that.myNavBar.map((el)=>{
                        el.active = false;
                    });
                    that.myNavBar[swiper.clickedIndex].active = true;
                    that.$router.push({
                        path:that.myNavBar[swiper.clickedIndex].mark
                    })

                })
            })    
        }
    }
</script>

头部导航在主路由里面,router-view显示每个栏目对应的内容,可以给每个路由设置keep-alive,实现条件3。

从事移动端web开发的小伙伴们应该对手机的物理返回键“深恶痛绝”把,同样我们做完了上面那个demo,当你点击安卓的物理返回键的时候,demo会一步一步的返回,显然,这不是我们要的结果,我们要的效果应该是“指哪打哪,自我掌控

1、首先,我们新建一个 global变量 这个用于记录路由的访问来源

clipboard.png

2、在对应的路由文件里面添加路由卫士:

clipboard.png

3、在mounted里面配置popstate和配置goBack方法:

clipboard.png

4、销毁组件的时候 移除popstate

clipboard.png

我的三个路由的名称分别为:mpos、payPhoneBill、movieTicket,接下来看动图:

图片描述

可以看到 我们只需要判断 global.beforeRouteName 和 路由的名称作为判断条件,就可以做任何操作和交互了,可以自己很好的控制物理键啦!

demo在线预览:

clipboard.png

demo的代码地址:https://github.com/yulongwuko...


dragonจุ๊บ
20 声望1 粉丝