美文网首页Vue.js
Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

作者: e20a12f8855d | 来源:发表于2019-06-03 10:04 被阅读49次

三、实现 Header 渐隐渐显效果

可以打开 去哪网 看一下城市的详情页,首先他的左上角定位了一个返回图标,当页面向下滑动过的时候,这个返回图标消失,会渐渐出现一个头部,和首页的头部样式一样,所以我们先来完成头部这两个部分的布局样式。

首先在 details/components 目录下新建一个 header.vue 组件,并在 Details.vue 中引用并使用这个组件,然后编写 header.vue 组件的布局与样式。例:

detail/header.vue

<template>
<div class="detail-header">
    <router-link class="header-abs" to="/">
        <span class="iconfont">&#xe624;</span>
    </router-link>
    <div class="header-fix">
        <div class="header-left">
            <span class="iconfont">&#xe624;</span>
        </div>
        <div class="header-center">北京世界园艺博览会</div>
    </div>
</div>
</template>

<script>
export default {
    name: "DetailHeader"
};
</script>

<style lang="stylus" scoped>
@import '~style/varibles';

.detail-header {
    .header-abs {
        position: absolute;
        top: .2rem;
        left: .2rem;
        width: 0.68rem;
        height: 0.68rem;
        background-color: rgba(0, 0, 0, 0.6);
        border-radius: 50%;
        text-align: center;
        line-height: 0.68rem;
        color #fff
    }

    .header-fix {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        background-color: $bgColor;
        overflow: hidden;
        color: #fff;
        padding: 0.2rem;

        .header-left {
            position: absolute;
            top: 0.2rem;
            left: 0.2rem;
        }

        .header-center {
            text-align: center;
            margin: 0 0.2rem;
        }
    }
}
</style>

当前效果图:

注意,因为当前详情页内容不够长,不能下滑,所以给 Detail.vue 内容设置一个高度,让他能够向下滑动。

头部的布局样式完成了,接下来就需要实现一些逻辑了。默认打开详情页的时候,导航条是不显示的,只显示返回按钮图标,当页面向下滑动的时候,返回图标隐藏,导航条渐渐显示出来,我么来实现一下这个效果。

首先在 data 中定义一个变量 showAbs,默认为 true,然后通过 v-show 来决定哪个元素显示:

<template>
<div class="detail-header">
    <router-link class="header-abs" to="/" v-show="showAbs">
        <span class="iconfont">&#xe624;</span>
    </router-link>
    <div class="header-fix" v-show="!showAbs">
        <div class="header-left">
            <span class="iconfont">&#xe624;</span>
        </div>
        <div class="header-center">北京世界园艺博览会</div>
    </div>
</div>
</template>

<script>
export default {
    name: "DetailHeader",
    data(){
        return {
            showAbs: true
        }
    }
};
</script>

所以这个时候返回图标这个元素是显示的,header 部分不显示,当我们将页面向下滑,高度差不多是 50px 的时候,让返回图标隐藏,header 显示,所以这里要写一个滚动监听的函数,那就用到 activted 这个生命周期函数了。

在 activted 中,我们给 window 添加一个滚动监听事件 handleScroll,然后在 methods 中编写这个事件方法,通过 document.documentElement.scroll 来获取页面向上滚动的高度,然后做判断,当这个高度大于 50 的时候,也就是差不多大于图标按钮的高度时,图标按钮隐藏,header 部分显示,所以让 this.showAbs 为 false 就可以了,否则让他为 true,这样就实现了当页面的滚动高度大于 50 的时候,返回图标隐藏,header 显示。

header.vue

<script>
export default {
    name: "DetailHeader",
    data(){
        return {
            showAbs: true
        }
    },
    methods:{
        handleScroll(){
            const top = document.documentElement.scrollTop;
            if(top > 50){
                this.showAbs = false;
            }else{
                this.showAbs = true;
            }
            console.log(top);
        }
    },
    activated(){
        window.addEventListener("scroll",this.handleScroll);
    }
};
</script>

接下来就该实现渐隐渐显的动画效果了,在 if 判断中定义一个 opacity 变量,他等于 top/100,因为 top 值在页面滚动的时候是变化的,所以 opacity 也就是变化的了,然后做一个三元运算,当这个 opacity 大于 1 的时候,我就让他一直等于 1,也就是让他一直显示,否则的话,还是让他等于这个 opacity 变量,然后给 data 中的 opacityStyle 设置值,让他里边的 opacity 等于 opacity。

header.vue

<script>
export default {
    name: "DetailHeader",
    data(){
        return {
            showAbs: true,
            opacitySty:{
                opacity : 0
            }
        }
    },
    methods:{
        handleScroll(){
            const top = document.documentElement.scrollTop;
            if(top>50){
                this.showAbs = false;
                let opacity = top / 100;
                opacity = opacity > 1 ? 1 : opacity;
                this.opacitySty.opacity = opacity;
            }else{
                this.showAbs = true
            }
        }
    },
    activated() {
        window.addEventListener("scroll",this.handleScroll)
    },
};
</script>

回到页面,向下滚动,可以看到导航出现了一个渐隐渐显的效果。最后记得给导航中的返回按钮也添加一个返回首页的路由。

以上就实现了 Header 渐隐渐显效果。

四、对全局事件的解绑

先来回顾上一章,给 window 对象添加滚动监听事件这部分,我们在 handleScroll 中打印一些内容,例:

detail/header.vue

methods:{
    handleScroll(){
        console.log("scroll");
        // ...
    }
},
activated(){
    window.addEventListener("scroll",this.handleScroll);
}

启动项目服务,打开页面,在详情页滚动页面的时候,可以看到控制台输出了 “scroll” 内容,这没有问题,但是,回到首页,在滚动页面的时候,控制台中也打印出了 “scroll” 内容,也就是说首页也执行了 handleScroll 这个方法,但是这个方法我们是写在 header.vue 中的,为什么首页也会有呢?

因为之前我们都是用 @ 给元素绑定事件,而且是给当前组件中的元素绑定,他只作用于组件的内部,不会影响外部的组件,但是这里我们是给全局的 window 对象绑定的,所以会造成这种问题,这个滚动事件不仅对这个组件有效果,对其他的组件也产生了影响,那如何去处理这个问题呢?

当我们对这个组件用了 keep-alive 的时候,这个组件会多出一个 activted 生命是周期函数,这一块内容可以回顾一下 “Vue.js第7课-项目实战-城市列表开发(part04)” 中的第十一章,activted 在每次页面展示的时候会执行,与之对应,他还提供给我们一个新的生命周期函数,叫做 deactivted,他是页面即将被隐藏,或者说页面即将被替换成新的页面的时候,这个组件的 deactivted 这个生命周期函数会被执行,我们在这个函数中将给 window 对象添加的滚动事件给移除掉。

detail/header.vue

// ...
activated(){
    window.addEventListener("scroll",this.handleScroll);
},
deactivated(){
    window.removeEventListener("scroll",this.handleScroll);
}
// ...

也就你页面展示的时候绑定 scroll 事件,页面隐藏的时候再去对这个 scroll 事件进行解绑。这个时候,回到页面,在详情页滚动的时候,可以看到打印出了 “scroll”,回到首页,滚动页面的时候,并没有打印出 “scroll”,说明并没有执行滚动时间,这样就解决了上一章中留下的 BUG。

最后记得提交代码并合并分支。

五、使用递归组件实现详情页列表

这一章我们来完成详情页的列表部分,可以看一下 去哪网 的详情页列表部分,因为内容比较多,所以我们先简单的实现一部分列表内容,来看一下递归组件如何使用。

首先还是新建一个 detail-list 分支并切换,在这个分支上进行代码的开发。然后新建 list.vue,编写组件的基本结构,然后到 Detail.vue 中引入并使用这个模板。这次我们先不编写模板的样式,先来看一下对数据的处理,在 Detail.vue 中添加一组数据,例:

Detail.vue

data() {
    return {
        list: [{
            title: "非指定日上午场票",
            children: [{
                    title: "[上午场]成人票(早定优惠)"
                },
                {
                    title: "[上午场]老人票"
                }
            ]
        }, {
            title: "指定日上午场票",
            children: [{
                    title: "[上午场]老人票"
                },
                {
                    title: "[上午场]学生票"
                }
            ]
        }, {
            title: "儿童票"
        }, {
            title: "特惠票"
        }]
    }
}

然后使用属性的方式将 list 传递给子组件 list.vue,list.vue 接收到 list 数据后,我们在模板中将它渲染出来,例:

list.vue

<template>
<div>
    <div class="list-con" v-for="(item,index) of list" :key="index">
        <div class="list-tit">{{item.title}}</div>
    </div>
</div>
</template>

此时页面是这样的一个效果:

之前我们在 list 数据的每一项下还添加了一组子项 children,它里面又包含着几组数据,现在我们将 children 子项页渲染到页面,这个时候就用到递归组件了,递归组件的意思就是在组件自身调用组件自身:

list.vue

<template>
<div>
    <div class="list-con" v-for="(item,index) of list" :key="index">
        <div class="list-tit">{{item.title}}</div>
        <div class="list-children">
            <div v-if="item.children" class="list-children-item">
                <detail-list :list="item.children"></detail-list>
            </div>
        </div>
    </div>
</div>
</template>

上面代码中,我们在 list-children 这个元素下,先做了一个判断,当 item.children 下有值的时候,调用一下自身,也就是 detail-list 这个组件,这个组件也是通过属性的形式,传一个 list,因为在 list.vue 中已经通过 props 接收到 list 了,而且外层已经循环过 list 了,现在我们只是要获取 list 下的 children 中的数据了,所以直接让这个 list 属性等于 item.children 就可以了,打开页面,可以看到这样一个效果:

虽然样式上有问题,但是我们可以在控制台中看到这些数据是存在层级关系的,说明数据渲染的没有问题,接下来我们对样式做一个调整。

list.vue

<template>
<div>
    <div class="list-con" v-for="(item,index) of list" :key="index">
        <div class="list-tit border-bottom">
            <span class="ticket"></span>
            {{item.title}}
        </div>
        <div class="list-children">
            <div v-if="item.children" class="list-children-item">
                <detail-list :list="item.children"></detail-list>
            </div>
        </div>
    </div>
</div>
</template>

<script>
export default {
    name: "DetailList",
    props: {
        list: Array
    }
};
</script>

<style lang="stylus" scoped>
.list-con {
    .list-tit {
        position: relative;
        height: 0.88rem;
        line-height: 0.88rem;
        padding: 0 0.2rem 0 0.6rem;

        .ticket {
            display: inline-block;
            position: absolute;
            width: 0.36rem;
            height: 0.36rem;
            top: 0.26rem;
            left: 0.2rem;
            background: url('//s.qunarzz.com/piao/image/touch/sight/detail.png') 0 -0.45rem no-repeat;
            background-size: auto;
            margin-right: 0.1rem;
            background-size: 0.4rem 3rem;
        }
    }

    .list-children {
        padding: 0 0.2rem;

        .list-children-item {
            .ticket {
                background: none;
            }
        }
    }
}
</style>

此时打开页面,可以看到页面是这样一个效果的:

如果我们将 data 中 list 里的 children 中再添加一组 children 呢?可以试一下:

Detail.vue

// ...
list: [{
    title: "非指定日上午场票",
    children: [{
            title: "[上午场]成人票(早定优惠)",
            children:[{
                title:"第三级"
            },{
                title:"第三级"
            }]
        },
        {
            title: "[上午场]老人票"
        }
    ]
}, {
    title: "指定日上午场票",
    children: [{
            title: "[上午场]老人票"
        },
        {
            title: "[上午场]学生票"
        }
    ]
}, {
    title: "儿童票"
}, {
    title: "特惠票"
}]
// ...

list.vue 中不需要做任何修改,因为我们在子组件中调用自身的时候,其实就相当于在调用子组件的位置又加了一边 template 中的内容,所以他会找到子数据再循环渲染一次,打开页面可以看一下:

没有任何问题,记得将这部分代码提交一下。


长得好看的都会关注我的 o(≧v≦)o~~

相关文章

网友评论

    本文标题:Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

    本文链接:https://www.haomeiwen.com/subject/kobqxctx.html