接上篇 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