运行环境
"vue": "^2.7.10"
"nuxt": "^2.15.8"
> node -v
v20.18.0
> npm -v
10.8.2
使用swiper模拟滚动加载
安装swiper
npm i -S swiper
npm i -S vue-awesome-swiper
其中swiper安装成功,插件vue-awesome-swiper报错:
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: qiujing_pc@1.3.0
npm error Found: swiper@11.1.14
npm error node_modules/swiper
npm error swiper@"^11.1.14" from the root project
npm error
npm error Could not resolve dependency:
npm error peer swiper@"^7.0.0 || ^8.0.0" from vue-awesome-swiper@5.0.1
npm error node_modules/vue-awesome-swiper
npm error vue-awesome-swiper@"*" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /Users/duanhuiqian/.npm/_logs/2024-11-12T01_43_12_246Z-eresolve-report.txt
npm error A complete log of this run can be found in: /Users/duanhuiqian/.npm/_logs/2024-11-12T01_43_12_246Z-debug-0.log
解决办法
Vue3使用 swiper@7 或者swiper@8
Vue2使用 swiper@5 + vue-awesome-swiper@4
# 如果已经安装了swiper
npm uninstall swiper
npm i -S swiper@5
npm i -S vue-awesome-swiper@4
package.js
"swiper": "^5.4.5",
"vue-awesome-swiper": "^4.1.1",
Demo结构
downLoad.vue
<template>
<div page="App下载页" :class="[$options.name]" :ref="$options.name">
<swiper class="swiper" :options="swiperOption">
<swiper-slide>
<div class="content-body ">
<div class="main-content-area">Slide 1</div>
</div>
</swiper-slide>
<swiper-slide>
<div class="content-body v-bg2">
<div class="main-content-area">Slide 2</div>
</div>
</swiper-slide>
<swiper-slide>
<div class="content-body v-bg3">
<div class="main-content-area">Slide 3</div>
</div>
</swiper-slide>
<swiper-slide>
<div class="content-body v-bg4">
<div class="main-content-area">Slide 4</div>
</div>
</swiper-slide>
<swiper-slide>
<div class="content-body v-bg5">
<div class="main-content-area">Slide 5</div>
</div>
</swiper-slide>
<!-- <div class="swiper-pagination" slot="pagination"></div> -->
</swiper>
</div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
export default {
name: "downLooad",
meta: {
title: "App下载",
keywords: "",
description: "",
},
components: {
Swiper,
SwiperSlide
},
data() {
return {
swiperOption: {
direction: 'vertical',
slidesPerView: 1, // 一屏幕显示多少个slide
// spaceBetween: 10, // 滑动到下一个轮播图之间的距离
speed: 1500, // 控制滑动速度,单位是毫秒
// centeredSlides: true, // 居中显示
mousewheel: true,
// pagination: { // 禁止分页器,不显示分页器圆点
// el: '.swiper-pagination',
// clickable: true
// }
}
}
}
};
</script>
<style lang="less" scoped>
.downLooad {
height: calc(100vh - 64px - 119px);
background: linear-gradient(180deg, #fff 0%, #FDF4F1 32%, #FDEFEF 100%);
display: flex;
width: 100%;
.swiper {
margin-bottom: -10px;
width: 100%;
.content-body {
background: linear-gradient(180deg, rgba(255, 119, 119, 0.08) 0%, rgba(241, 164, 86, 0.08) 32%, #FDEFEF 100%);
height: 100%;
width: 100%;
.main-content-area {
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
}
&.v-bg2 {
background: linear-gradient(180deg, #FFEBEB 0%, #FDF1E6 32%, #F9DDDF 100%);
}
}
}
}
</style>
预览效果
添加平滑过渡动画 - animate.css
animate.css 是一个广泛使用的 CSS 动画库,提供了多种预定义的动画效果。
安装animate
npm install animate.css --save
在download.vue中引入:
import 'animate.css';
使用方法
package.js
"animate.css": "^4.1.1",
使用animate.css只需要在文件中引入样式,然后直接在class中直接编写即可
</template>
... // vue模版代码
<div class="animate__backInRight animate__animated animate__delay-2s animate__duration-1s">
</template>
<script>
...
import 'animate.css'; // 文档 https://animate.style/
...
注意事项
- animate__animated
- 此类为一切样式基础,必不可少
- animate__backInRight
- 动画类, __后缀为具体的动画类名,比如:backInRigh、 backInLeft
- animate__delay-2s
- 功能类,具体功能指动画延迟2秒执行
Basic usage
After installing Animate.css, add the class animate__animated to an element, along with any of the animation names (don't forget the animate__ prefix!):
<h1 class="animate__animated animate__bounce">An animated element</h1>
That's it! You've got a CSS animated element. Super!
Animations can improve the UX of an interface, but keep in mind that they can also get in the way of your users! Please read the best practices and gotchas sections to bring your web-things to life in the best way possible.
解决动画同屏渲染问题
slideChange
- 使用swiper的change事件,获取当前活动的幻灯片索引,对相应的幻灯片进行
v-show操作,保证动画可以在加载到幻灯片时再释放
<template>
<div page="App下载页" :class="[$options.name]" :ref="$options.name">
<swiper class="swiper" :options="swiperOption" ref="swiperbox" @slideChange="onSlideChange">
<swiper-slide>
<div class="content-body " v-show="currentSlide == 0">
<div class="main-content-area animate__backInRight animate__animated animate__duration-1s">Slide 1</div>
</div>
</swiper-slide>
<swiper-slide>
<div class="content-body v-bg2" v-show="currentSlide == 1">
<div class="main-content-area animate__backInRight animate__animated animate__duration-1s">Slide 2</div>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
import 'animate.css';
export default {
name: "downLooad",
meta: {
title: "App下载",
keywords: "",
description: "",
},
components: {
Swiper,
SwiperSlide
},
data() {
return {
currentSlide: 0,
swiperOption: {
direction: 'vertical',
slidesPerView: 1, // 一屏幕显示多少个slide
speed: 1500, // 控制滑动速度,单位是毫秒
mousewheel: true,
}
}
},
methods: {
onSlideChange() {
let el = this.$refs.swiperbox.$swiper;
this.currentSlide = el.activeIndex;
},
}
};
</script>
因为使用了v-show的缘故,所以每次在切换下一张幻灯片和回到上一张幻灯片时,会重复释放动画,如果只需要展示一次动画,当前方案下可以对v-show特殊处理,或者直接使用
v-if,用关键变量来控制只进行初始化的动画释放展示
IntersectionObserver
利用浏览器API IntersectionObserver 来监听元素是否进入可视窗口从而添加动画样式类
实现步骤
改造v-template模版代码
给要监听的元素添加ref
<template>
<div page="App下载页" :class="[$options.name]" :ref="$options.name">
<swiper class="swiper" :options="swiperOption" ref="swiperbox">
<swiper-slide v-for="(slide, index) in slides" :key="index" ref="swiperSlide">
<div class="content-body " :class="index > 0 && 'v-bg2'">
<div class="main-content-area hidden">Slide {{ slide }}</div>
</div>
</swiper-slide>
</swiper>
</div>
</template>
IntersectionObserver 监听元素是否进入可视窗口
...
mounted() {
this.initIntersectionObserver();
},
methods: {
initIntersectionObserver() {
const options = {
root: null, // 观察根元素,默认为视口
rootMargin: '0px', // 根元素的边距
threshold: 0.5 // 元素进入视口的比例阈值
};
this.observer = new IntersectionObserver(this.handleIntersection, options);
// 获取所有 swiper-slide 元素并观察
if (this.$refs.swiperSlide && Array.isArray(this.$refs.swiperSlide)) {
this.$refs.swiperSlide.forEach(slide => {
const _el = slide.$el.querySelector('.main-content-area');
if (_el) this.observer.observe(_el);
});
}
},
当进入视口时播放进入的过渡动画
播放过一次动画后关闭 IntersectionObserver 的监听
...
handleIntersection(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate__backInRight','visible', 'animate__animated', 'animate__duration-1s');
// 添加完动画类样式后即销毁监听
observer.unobserve(entry.target);
}
});
},
- 完整代码:
download.vue
<template>
<div page="App下载页" :class="[$options.name]" :ref="$options.name">
<swiper class="swiper" :options="swiperOption" ref="swiperbox">
<swiper-slide v-for="(slide, index) in slides" :key="index" ref="swiperSlide">
<div class="content-body " :class="index > 0 && 'v-bg2'">
<div class="main-content-area hidden">Slide {{ slide }}</div>
</div>
</swiper-slide>
</swiper>
</div>
</template>
<script>
import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
import 'animate.css';
export default {
name: "downLooad",
meta: {
title: "App下载",
keywords: "",
description: "",
},
components: {
Swiper,
SwiperSlide
},
data() {
return {
slides: [1, 2, 3, 4, 5],
swiperOption: {
direction: 'vertical',
slidesPerView: 1, // 一屏幕显示多少个slide
speed: 1500, // 控制滑动速度,单位是毫秒
mousewheel: true,
}
}
},
mounted() {
this.initIntersectionObserver();
},
methods: {
initIntersectionObserver() {
const options = {
root: null, // 观察根元素,默认为视口
rootMargin: '0px', // 根元素的边距
threshold: 0.5 // 元素进入视口的比例阈值
};
this.observer = new IntersectionObserver(this.handleIntersection, options);
// 获取所有 swiper-slide 元素并观察
if (this.$refs.swiperSlide && Array.isArray(this.$refs.swiperSlide)) {
this.$refs.swiperSlide.forEach(slide => {
const _el = slide.$el.querySelector('.main-content-area');
if (_el) this.observer.observe(_el);
});
}
},
handleIntersection(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate__backInRight','visible', 'animate__animated', 'animate__duration-1s');
// 添加完动画类样式后即销毁监听
observer.unobserve(entry.target);
}
});
},
}
};
</script>
<style lang="less" scoped>
.downLooad {
height: calc(100vh - 64px - 119px);
background: linear-gradient(180deg, #fff 0%, #FDF4F1 32%, #FDEFEF 100%);
display: flex;
width: 100%;
.swiper {
margin-bottom: -10px;
width: 100%;
.content-body {
background: linear-gradient(180deg, rgba(255, 119, 119, 0.08) 0%, rgba(241, 164, 86, 0.08) 32%, #FDEFEF 100%);
height: 100%;
width: 100%;
.main-content-area {
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
opacity: 0;
&.visible{
opacity: 1;
}
}
&.v-bg2 {
background: linear-gradient(180deg, #FFEBEB 0%, #FDF1E6 32%, #F9DDDF 100%);
}
}
}
}
</style>
最后,关于不使用swiper插件
如果不想使用swiper插件,可使用纯css的方式并结合 IntersectionObserver,笔者这里有可提供部分算法思路
使用scroll-snap-type, 并修改滚动条样式,使其透明
- 关键css
.content {
width: 100%;
height: 100vh;
scroll-snap-type: y mandatory;
overflow-y: scroll;
overflow-x: hidden;
.page {
scroll-snap-align: start;
height: 100vh;
}
/* WebKit 浏览器 (Chrome, Safari) */
&::-webkit-scrollbar {
display:none;
width: 0;
/* 滚动条宽度 */
}
&::-webkit-scrollbar-track {
background: transparent;
/* 轨道背景色 */
}
&::-webkit-scrollbar-thumb {
background: transparent;
/* 滚动条颜色 */
}
&::-webkit-scrollbar-thumb:hover {
background: transparent;
/* 滚动条悬停颜色 */
}
}
- html伪代码
<div class="content">
<div class="bg-mb page">
test
</div>
<div v-if="isIos && appDownload" class="bg-mb-2 page">
<img :src="`/images/${theme_resdir}/bg_1ios_1.png`" alt="IOS下载须知" />
</div>
<div v-if="isIos && appDownload" class="bg-mb-2 page">
<img :src="`/images/${theme_resdir}/bg_1ios_1.png`" alt="IOS下载须知" />
</div>
....
</div>
CSS 实现滚动加载
animation-timeline: scroll()
因为 Animate.css 特性,不止可以直接作为className写在template中,同样可以作为动画类名写在style里面
...{
...
animation-name: backInRight;
animation-timeline: scroll();
}
不作为主要方法讲述,是因为他还只是实验性功能,存在兼容性问题。当兼容性问题解决后,这个方式将成为滚动动画的主流方式。