接上篇 https://www.cnblogs.com/chenyingying0/p/12635369.html
返回顶部组件
base/backtop/index.vue
<template> <transition name="mine-backtop"> <a href="javascript:;" class="mine-backtop" v-show="visible" @click="backToTop"> <i class="iconfont icon-backtop"></i> </a> </transition> </template> <script> export default { name:"MeBacktop", props:{ visible:{ type:Boolean, default:false } }, methods:{ backToTop(){ this.$emit("backtop");//基础组件,与业务无关,具体实现去页面里 } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .mine-backtop{ overflow:hidden; @include flex-center(); width:45px; height:45px; background:rgba(0,0,0,.6); border:none; border-radius:50%; } .iconfont{ color:#fff; font-size:38px; } .mine-backtop{ &-enter-active, &-leave-active{ transition:opacity 0.4s; } &-enter, &-leave-to{ opacity:0; } } </style>
pages/home/index.vue
<template> <div class="home"> <header class="g-header-container"> <!-- 没有内容自闭合即可--> <home-header/> </header> <!-- 滚动条接收到数据后开始更新 --> <!-- pullDown是布尔值,可以使用简写直接传入,不加冒号 --> <!-- 接收到pull-down消息后,触发pullToRefresh方法 --> <me-scroll :data="recommends" pullDown pullUp @pull-down="pullToRefresh" @pull-up="pullToLoadMore" @scroll-end="scrollEnd" ref="scroll"> <home-slider ref="slider" /> <home-nav /> <!-- 接收热门推荐加载完毕的消息 --> <home-recommend @loaded="getRecommends" ref="recommend" /> </me-scroll> <div class="g-backup-container"> <me-backtop :visible="isBacktopVisible" @backtop="backToTop" /> </div> <!-- 当前页面存在二级页面时需要使用router-view --> <router-view></router-view> </div> </template> <script> import MeScroll from 'base/scroll'; import HomeHeader from './header'; import HomeSlider from './slider'; import HomeNav from './nav'; import HomeRecommend from './recommend'; import MeBacktop from 'base/backtop'; export default { name:"Home", components:{ HomeHeader, HomeSlider, MeScroll, HomeNav, HomeRecommend, MeBacktop }, data(){ return{ recommends:[], isBacktopVisible:false } }, methods:{ getRecommends(recommends){ this.recommends=recommends; }, updateScroll(){ }, pullToRefresh(end){ this.$refs.slider.update().then(end); }, pullToLoadMore(end){ this.$refs.recommend.update().then(end).catch(err=>{ //没有更多内容时 if(err){ console.log(err); } end(); //禁止继续加载更多数据 //替换上拉时的loading,改为“没有更多数据了” }); }, scrollEnd(translate,scroll){ // translate<0向下拉 // -translate>scroll.height拉过的距离大于一屏的高度 this.isBacktopVisible=translate<0 && -translate>scroll.height; }, backToTop(){ this.$refs.scroll && this.$refs.scroll.scrollToTop(); } } } </script> <style lang="scss" scoped> // 引入前面需要加波浪线,否则会报错 @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
修改assets/scss/_containers.scss(之前写错了)
效果图
Header动画效果和显示隐藏
pages/home/header.vue
<template> <div> <me-navbar class="header" v-show="visible"> <i class="iconfont icon-scan" slot="left"></i> <div slot="center">搜索框</div> <i class="iconfont icon-msg" slot="right"></i> </me-navbar> </div> </template> <script> import MeNavbar from 'base/navbar'; export default { name:"HomeHeader", components:{ MeNavbar }, data(){ return{ visible:true } }, methods:{ show(){ this.visible=true; }, hide(){ this.visible=false; } } } </script> <style lang="scss" scoped> // 引入前面需要加波浪线,否则会报错 @import "~assets/scss/mixins"; .header{ &.mine-navbar{ background:transparent; transition:background-color 0.5s; } .iconfont{ font-size:$icon-font-size; color:$icon-color-default; } } .header-transition .header.mine-navbar{ background-color:$header-bgc-translucent; } </style>
base/scroll/index.vue
<template> <!-- wiper会实例化构造函数,生成swiper实例 --> <!-- ref="swiper"能够获取到这个swiper实例 --> <swiper :options="swiperOption" ref='swiper'> <div class="mine-scroll-pull-down" v-if="pullDown"> <!-- ref="pullDownLoading" -- 获取下拉的loading --> <me-loading :text="pullDownText" inline ref="pullDownLoading" /> </div> <swiper-slide> <slot></slot> </swiper-slide> <div class="mine-scroll-pull-up" v-if="pullUp"> <!-- ref="pullUpLoading" -- 获取上拉的loading --> <me-loading :text="pullUpText" inline ref="pullUpLoading" /> </div> <div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div> </swiper> </template> <script> // 组件首字母大写,否则会报错 import {Swiper,SwiperSlide} from 'vue-awesome-swiper'; import MeLoading from 'base/loading'; import { PULL_DOWN_HEIGHT, PULL_DOWN_TEXT_INIT, PULL_DOWN_TEXT_START, PULL_DOWN_TEXT_ING, PULL_DOWN_TEXT_END, PULL_UP_HEIGHT, PULL_UP_TEXT_INIT, PULL_UP_TEXT_START, PULL_UP_TEXT_ING, PULL_UP_TEXT_END } from './config'; export default { name:"MeScroll", components:{ Swiper, SwiperSlide, MeLoading }, props:{//过滤器 scrollbar:{ type:Boolean, default:true }, data:{//热门推荐加载完成后传递过来的recommends数据 type:[Array,Object] }, pullDown:{//是真就开启下拉刷新 type:Boolean, default:false }, pullUp:{//是真就开启下拉加载 type:Boolean, default:false } }, methods:{ update(){//不知道怎么写就去swiper官网查api //console.log(this.$refs.swiper);//打印swiper实例 this.$refs.swiper && this.$refs.swiper.$swiper.update();//调用swiper.update()更新滚动条 }, scrollToTop(speed,runCallback){ // slideTo回到第x张幻灯片,swiper的API提供的 this.$refs.swiper && this.$refs.swiper.$swiper.slideTo(0,speed,runCallback); }, init(){ //将不需要设置getter和setter的数据,放在init中初始化即可 this.pulling=false;//是否正在下拉 this.pullDownText=PULL_DOWN_TEXT_INIT;//设置下拉初始化文字 this.pullUpText=PULL_UP_TEXT_INIT;//设置上拉初始化文字 this.swiperOption={ direction:'vertical',//垂直方向 slidesPerView:'auto',//一次显示几张 freeMode:true,//任意滑动多少距离 setWrapperSize:true,//根据内容设置容器尺寸 scrollbar:{ el:this.scrollbar?'.swiper-scrollbar':null, hide:true //滚动条自动隐藏 }, on:{ //swiper配置时会触发sliderMove方法,这里调用时执行自定义的scroll方法 sliderMove:this.scroll, touchEnd:this.touchEnd,//touchEnd是swiper提供的滚动结束的函数,this.touchEnd是我们自己写的函数, transitionEnd:this.scrollEnd } }; }, scroll(){ const swiper=this.$refs.swiper.$swiper; //滚动时触发scroll事件 this.$emit("scroll",swiper.translate,this.$refs.swiper.$swiper); //如果正在下拉中,不会再次执行 if(this.pulling) return; //console.log(swiper.translate);//打印出滚动条滚过的距离 if(swiper.translate>0){//下拉 if(!this.pullDown){//如果不需要下拉刷新 return; } if(swiper.translate>PULL_DOWN_HEIGHT){ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_START); }else{ this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_INIT); } }else if(swiper.isEnd){//上拉 if(!this.pullUp){ return; } //是否达到上拉的触发条件 //swiper的位移加上swiper的高度(617px)-50px的值如果大于当前内容高度 //swiper.translate这个属性可以获取到wrapper的位移,其实可以理解为滚动条滚动的距离 //swiper.height这个属性获取swiper容器的高度, 也就是显示区域的高度 //PULL_UP_HEIGHT是我们设置的一个值。为了让页面不是到达最低部的时候,可以提前加载内容 //parseInt(swiper.$wrapperEl.css('height'))是wrapper的HTML元素的height属性, 也就是所有内容的高度 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//开始上拉 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_START); }else{//保持初始化 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_INIT); } } }, scrollEnd(){ this.$emit("scroll-end",this.$refs.swiper.$swiper.translate,this.$refs.swiper.$swiper,this.pulling); }, touchEnd(){ const swiper=this.$refs.swiper.$swiper; //如果正在下拉中,不会再次执行 if(this.pulling) return; if(swiper.translate>PULL_DOWN_HEIGHT){//如果距离大于设定的距离 if(!this.pullDown){//如果不需要下拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止触摸 swiper.setTransition(swiper.params.speed);//设置初始速度 swiper.setTranslate(PULL_DOWN_HEIGHT);//移动到设定的位置(拖动过度时回到设置的位置) swiper.params.virtualTranslate=true;//定住不给回弹 this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_ING);//设置正在刷新中的文字 this.$emit("pull-down",this.pullDownEnd);//触发消息,传递结束下拉的函数 }else if(swiper.isEnd){//上拉 //是否达到上拉的触发条件 const isPullUp=Math.abs(swiper.translate)+swiper.height-PULL_UP_HEIGHT>parseInt(swiper.$wrapperEl.css('height')); if(isPullUp){//开始上拉 if(!this.pullUp){//如果不需要上拉刷新 return; } this.pulling=true; swiper.allowTouchMove=false;//禁止触摸 swiper.setTransition(swiper.params.speed);//设置初始速度 swiper.setTranslate(-(parseInt(swiper.$wrapperEl.css('height'))+PULL_UP_HEIGHT-swiper.height));//超过拉动距离时回弹 swiper.params.virtualTranslate=true;//定住不给回弹 this.$refs.pullUpLoading.setText(PULL_UP_TEXT_ING);//设置正在刷新中的文字 this.$emit("pull-up",this.pullUpEnd);//触发消息,传递结束下拉的函数 } } }, pullDownEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullDownLoading.setText(PULL_DOWN_TEXT_END);//设置加载结束后的文字 swiper.allowTouchMove=true;//可以触摸 swiper.setTransition(swiper.params.speed);//设置初始速度 swiper.params.virtualTranslate=false;//可以回弹 swiper.setTranslate(0);//移动到最初的位置 //下拉完成后,显示head组件(下拉过程中会被隐藏) setTimeout(()=>{ this.$emit("pull-down-transition-end"); },swiper.params.speed); }, pullUpEnd(){ const swiper=this.$refs.swiper.$swiper; this.pulling=false; this.$refs.pullUpLoading.setText(PULL_UP_TEXT_END);//设置加载结束后的文字 swiper.allowTouchMove=true;//可以触摸 swiper.params.virtualTranslate=false;//可以回弹 } }, watch:{//检测数据变化的事件 data(){ this.update();//data数据变化时执行update函数 } }, created(){//在created中初始化数据 this.init(); } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .swiper-container{ width:100%; height:100%; overflow:hidden; & .swiper-slide{ height:auto; } } //默认是不显示的 .mine-scroll-pull-down{ position:absolute; left:0; bottom:100%; width:100%; height:80px; } .mine-scroll-pull-up{ position:absolute; left:0; top:100%; width:100%; height:30px; } </style>
pages/home/config.js
pages/home/index.vue
<template> <div class="home"> <header class="g-header-container"> <!-- 没有内容自闭合即可--> <!-- ref用来获取到组件 --> <home-header :class="{'header-transition':isHeaderTransition}" ref="header" /> </header> <!-- 滚动条接收到数据后开始更新 --> <!-- pullDown是布尔值,可以使用简写直接传入,不加冒号 --> <!-- 接收到pull-down消息后,触发pullToRefresh方法 --> <me-scroll :data="recommends" pullDown pullUp @pull-down="pullToRefresh" @pull-up="pullToLoadMore" @scroll="scroll" @scroll-end="scrollEnd" @pull-down-transition-end="pullDownTransitionEnd" ref="scroll"> <home-slider ref="slider" /> <home-nav /> <!-- 接收热门推荐加载完毕的消息 --> <home-recommend @loaded="getRecommends" ref="recommend" /> </me-scroll> <div class="g-backup-container"> <me-backtop :visible="isBacktopVisible" @backtop="backToTop" /> </div> <!-- 当前页面存在二级页面时需要使用router-view --> <router-view></router-view> </div> </template> <script> import MeScroll from 'base/scroll'; import HomeHeader from './header'; import HomeSlider from './slider'; import HomeNav from './nav'; import HomeRecommend from './recommend'; import MeBacktop from 'base/backtop'; import {HEADER_TRANSITION_HEIGHT} from './config'; export default { name:"Home", components:{ HomeHeader, HomeSlider, MeScroll, HomeNav, HomeRecommend, MeBacktop }, data(){ return{ recommends:[], isBacktopVisible:false, isHeaderTransition:false } }, methods:{ getRecommends(recommends){ this.recommends=recommends; }, updateScroll(){ }, pullToRefresh(end){ this.$refs.slider.update().then(end); }, pullToLoadMore(end){ this.$refs.recommend.update().then(end).catch(err=>{ //没有更多内容时 if(err){ console.log(err); } end(); //禁止继续加载更多数据 //替换上拉时的loading,改为“没有更多数据了” }); }, scroll(translate){ this.changeHeaderStatus(translate); }, scrollEnd(translate,scroll,pulling){ if(!pulling){ this.changeHeaderStatus(translate); } // translate<0向下拉 // -translate>scroll.height拉过的距离大于一屏的高度 this.isBacktopVisible=translate<0 && -translate>scroll.height; }, backToTop(){ this.$refs.scroll && this.$refs.scroll.scrollToTop(); }, changeHeaderStatus(translate){ if(translate>0){//上拉 this.$refs.header.hide(); return; } this.$refs.header.show(); this.isHeaderTransition=-translate>HEADER_TRANSITION_HEIGHT; }, pullDownTransitionEnd(){//下拉完成之后显示导航条 this.$refs.header.show(); } } } </script> <style lang="scss" scoped> // 引入前面需要加波浪线,否则会报错 @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
api/home.js
import axios from 'axios'; import {SUCC_CODE,TIMEOUT,HOME_RECOMMEND_PAGE_SIZE,JSONP_OPTIONS} from './config'; import jsonp from 'assets/js/jsonp'; // shuffle打乱数组顺序的方法 const shuffle=(arr)=>{ const arrLength=arr.length; let i=arrLength; let rndNum; while(i--){ //如果当前索引不等于随机数索引,则交换这两个索引的位置 if(i!==(rndNum=Math.floor(Math.random()*arrLength))){ //这是一种新的交换写法 ES6解构 [arr[i],arr[rndNum]]=[arr[rndNum],arr[i]]; } } return arr; } //获取幻灯片数据 ajax export const getHomeSliders=()=>{ //演示超时错误 return axios.get('http://www.imooc.com/api/home/slider',{ timeout:TIMEOUT }).then(res=>{ //console.log(res); if(res.data.code===SUCC_CODE){ //这段代码的作用主要是演示下拉刷新的效果 //每次刷新会加载出不同数量,不同顺序的轮播图 let sliders=res.data.slider; const slider=[sliders[Math.floor(Math.random()*sliders.length)]];//slider是从sliders中随机取出一张图片,并包装成数组 sliders=shuffle(sliders.filter(()=>Math.random()>=0.5));//50%的概率,真就返回,假就剔除 if(sliders.length===0){ sliders=slider;//如果不幸一张都没有,就把之前随机的那张赋值给sliders } return sliders; //return res.data.slider; } throw new Error('没有成功获取到数据'); }).catch(err=>{ console.log(err); //错误处理 return [{ linkUrl:'www.baidu.com', picUrl:require('assets/img/404.png') }] }); } //获取热门推荐数据 export const getHomeRecommend=(page=1,psize=HOME_RECOMMEND_PAGE_SIZE)=>{ const url='https://ju.taobao.com/json/tg/ajaxGetItemsV2.json'; const params={ page, psize, type:0, frontCatId:''//type和frontCatId是根据给定的淘宝接口来添加的 } //调用jsonp获取数据 return jsonp(url,params,JSONP_OPTIONS).then(res=>{ if(res.code==='200'){ return res; } throw new Error('没有成功获取到数据'); }).catch(err=>{ if(err){ console.log(err); } }); }
效果图
来源:https://www.cnblogs.com/chenyingying0/p/12636916.html