nuxt4 + nuxt-swiper实现官网全屏播放

项目背景

楼主最近在做一个官网项目,风格偏酷炫, 页面需要实现全屏和流畅切换。 经过调研,确定使用技术栈 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()
}
相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax