项目背景
楼主最近在做一个官网项目,风格偏酷炫, 页面需要实现全屏和流畅切换。 经过调研,确定使用技术栈 nuxt4 + nuxt-swiper 。
为什么选择nuxt ?
因为这是官网,对SEO有一定的要求。
为什么不选用fullpage ?
因为fullpage4需要商业许可证,控制台报错提示需要licenseKey。 且官网页面有多个滚动设计, ,从风险规避和灵活性两个角度分析, 放弃fullpage 。
经过思考, 决定使用nuxt的配套插件nuxt-swiper
这篇文章主要是记录一下nuxt-swiper
的使用。
因为使用的nuxt-swiper版本和nuxt版本的问题,插件在使用的过程中会报错。
报错1 : SwiperSlide 没有注册
修复 : 要在import { Swiper, SwiperSlide } from 'swiper/vue'
中引入
报错2 :Autoplay 不存在
修复: 没有全局配置,相关模块需要显示引入import { Navigation, Pagination, Autoplay } from 'swiper/modules'
nuxt-swiper使用
安装
csharp
npm install nuxt-swiper
# 或
yarn add nuxt-swiper
Nuxt 配置
arduino
// nuxt.config.js
export default {
modules: [
'nuxt-swiper'
],
// 可选:全局配置,
// 不在这里做全局配置, 我的vscode会报错
swiper: {
}
}
组件使用
xml
// 完整代码
<template>
<div class="image-slider-container">
<Swiper ref="swiperRef" :modules="modules" :slides-per-view="1" :space-between="30" :loop="true" :speed="600"
:autoplay="{
delay: 3000,
disableOnInteraction: false,
pauseOnMouseEnter: true
}" :pagination="{
el: '.custom-pagination',
clickable: true,
type: 'bullets',
bulletClass: 'custom-bullet',
bulletActiveClass: 'custom-bullet-active',
renderBullet: renderBullet
}" :navigation="{
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
disabledClass: 'swiper-button-disabled'
}" @swiper="onSwiper" @slide-change="onSlideChange" class="image-swiper">
<SwiperSlide v-for="(image, index) in list" :key="image.id">
<div class="image-wrapper">
<img :src="image.image_url" alt='' class="slider-image" loading="lazy" />
</div>
</SwiperSlide>
</Swiper>
<!-- 自定义分页器容器 -->
<div class="custom-pagination-container">
<div class="custom-pagination"></div>
</div>
<!-- 自定义导航按钮 -->
<div class="custom-navigation">
<button class="swiper-button-prev custom-button">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
<button class="swiper-button-next custom-button">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
</div>
</div>
</template>
<script setup>
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Navigation, Pagination, Autoplay } from 'swiper/modules'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import 'swiper/css/autoplay'
const modules = [Navigation, Pagination, Autoplay]
// Swiper 实例
const swiperRef = ref(null)
let swiperInstance = null
// 当前活动幻灯片索引
const currentSlide = ref(0)
// Swiper 事件处理
const onSwiper = (swiper) => {
swiperInstance = swiper
}
const onSlideChange = (swiper) => {
currentSlide.value = swiper.activeIndex
}
// 自定义分页器渲染
const renderBullet = (index, className) => {
return `<span class="${className}">${index + 1}</span>`
}
let list = ref([])
onMounted(async () => {
const response = {
"code": 0,
"data": {
"total": 4,
"page": 1,
"limit": 10,
"list": [
{
"id": 1,
"name": "bg1",
"title": "bg1",
"image_url": "https:\/\/pt-cdn.jingyougz.com\/game_center\/cms\/2029\/image\/dev\/image_3953430423951760092237.png",
"link_url": "",
"created_at": "2025-10-10 18:31:08"
},
{
"id": 2,
"name": "bg2",
"title": "bg2",
"image_url": "https:\/\/pt-cdn.jingyougz.com\/game_center\/cms\/2029\/image\/dev\/image_1263315629251760092296.png",
"link_url": "",
"created_at": "2025-10-10 18:31:47"
},
{
"id": 3,
"name": "bg3",
"title": "bg3",
"image_url": "https:\/\/pt-cdn.jingyougz.com\/game_center\/cms\/2029\/image\/dev\/image_6751789659581760432067.png",
"link_url": "",
"created_at": "2025-10-14 16:54:32"
},
{
"id": 4,
"name": "bg4",
"title": "bg4",
"image_url": "https:\/\/pt-cdn.jingyougz.com\/game_center\/cms\/2029\/image\/dev\/image_5171852654321760432089.png",
"link_url": "",
"created_at": "2025-10-14 16:54:54"
}
]
}
}
// const { response } = await getImage({ column_id: 5 })
list.value = response.data.list
})
</script>
<style scoped>
.image-slider-container {
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 2rem 0;
}
.image-swiper {
width: 87%;
height: 500px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.image-wrapper {
position: relative;
width: 100%;
height: 100%;
border-radius: 12px;
overflow: hidden;
}
.slider-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.image-wrapper:hover .slider-image {
transform: scale(1.05);
}
/* 自定义分页器容器 */
.custom-pagination-container {
display: flex;
justify-content: center;
margin-top: 2rem;
padding: 0 1rem;
}
/* 自定义导航按钮容器 */
.custom-navigation {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1rem;
pointer-events: none;
z-index: 10;
}
.custom-button {
width: 50px;
height: 50px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
border: 2px solid rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
pointer-events: all;
color: #333;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.custom-button:hover {
background: white;
border-color: rgba(0, 0, 0, 0.2);
transform: scale(1.1);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
}
.custom-button:active {
transform: scale(0.95);
}
</style>
<style>
/* 全局 Swiper 样式修改 */
.custom-pagination {
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
}
.image-slider-container .custom-bullet {
width: 12px;
height: 12px;
background: rgba(0, 0, 0, 0.2);
border-radius: 50%;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
color: transparent;
font-size: 0;
}
.image-slider-container .custom-bullet-active {
background: #007bff;
transform: scale(1.2);
width: 32px;
border-radius: 16px;
}
.custom-bullet-active::before {
content: attr(data-index);
color: white;
font-size: 10px;
font-weight: 600;
}
/* 隐藏默认的导航按钮(如果显示的话) */
.swiper-button-next:after,
.swiper-button-prev:after {
display: none;
}
</style>
配置项分类
基础配置
arduino
const swiperOptions = {
// 幻灯片显示数量
slidesPerView: 1,
// 幻灯片间距
spaceBetween: 30,
// 循环模式
loop: true,
// 居中幻灯片
centeredSlides: false,
// 方向
direction: 'horizontal', // 'horizontal' | 'vertical'
// 速度
speed: 300,
// 启用CSS模式(使用CSS变换)
cssMode: false
}
分页配置
javascript
const paginationOptions = {
pagination: {
// 分页器类型
type: 'bullets', // 'bullets' | 'fraction' | 'progressbar' | 'custom'
// 可点击
clickable: true,
// 动态分页
dynamicBullets: false,
// 自定义分页HTML
renderBullet: function (index, className) {
return '<span class="' + className + '">' + (index + 1) + '</span>'
},
// 分页器位置
el: '.swiper-pagination'
}
}
导航配置
arduino
const navigationOptions = {
navigation: {
// 下一页按钮
nextEl: '.swiper-button-next',
// 上一页按钮
prevEl: '.swiper-button-prev',
// 隐藏不可用按钮
hideOnClick: true,
// 禁用类
disabledClass: 'swiper-button-disabled'
}
}
自动播放配置
arduino
const autoplayOptions = {
autoplay: {
// 延迟时间
delay: 5000,
// 用户交互后是否停止
disableOnInteraction: false,
// 暂停自动播放
pauseOnMouseEnter: true,
// 等待过渡完成
waitForTransition: true,
// 反向播放
reverseDirection: false,
// 停止在最后一张
stopOnLastSlide: false
}
}
响应式配置
yaml
const responsiveOptions = {
breakpoints: {
// 当屏幕宽度 >= 320px
320: {
slidesPerView: 1,
spaceBetween: 10
},
// 当屏幕宽度 >= 768px
768: {
slidesPerView: 2,
spaceBetween: 20
},
// 当屏幕宽度 >= 1024px
1024: {
slidesPerView: 3,
spaceBetween: 30
},
// 当屏幕宽度 >= 1440px
1440: {
slidesPerView: 4,
spaceBetween: 40
}
}
}
特效配置
yaml
// 淡入淡出效果
const fadeEffectOptions = {
effect: 'fade',
fadeEffect: {
crossFade: true
}
}
// 立方体效果
const cubeEffectOptions = {
effect: 'cube',
cubeEffect: {
slideShadows: true,
shadow: true,
shadowOffset: 20,
shadowScale: 0.94
}
}
// 封面流效果
const coverflowEffectOptions = {
effect: 'coverflow',
coverflowEffect: {
rotate: 50,
stretch: 0,
depth: 100,
modifier: 1,
slideShadows: true
}
}
常用事件和方法
javascript
// 事件处理
const eventHandlers = {
onSwiper: (swiper) => {
console.log('Swiper初始化完成', swiper)
},
onSlideChange: (swiper) => {
console.log('幻灯片切换', swiper.activeIndex)
},
onSlideChangeTransitionStart: (swiper) => {
console.log('切换过渡开始')
},
onSlideChangeTransitionEnd: (swiper) => {
console.log('切换过渡结束')
},
onAutoplay: (swiper) => {
console.log('自动播放')
}
}
// 常用方法
const swiperMethods = {
slideNext: () => swiperInstance.value.slideNext(),
slidePrev: () => swiperInstance.value.slidePrev(),
slideTo: (index) => swiperInstance.value.slideTo(index),
update: () => swiperInstance.value.update(),
destroy: () => swiperInstance.value.destroy()
}