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()
}
相关推荐
苏打水com3 小时前
JS基础事件处理与CSS常用属性全解析(附实战示例)
前端
W.Y.B.G3 小时前
JavaScript 计算闰年方法
开发语言·前端·javascript
小六路3 小时前
可以横跨时间轴,分类显示的事件
前端·javascript·vue.js
比老马还六3 小时前
Blockly文件积木开发
前端
Nayana3 小时前
Element-Plus源码分析--form组件
前端
Bellafu6663 小时前
selenium对每种前端控件的操作,python举例
前端·python·selenium
littleboyck4 小时前
VSCode 全自动调试Vue/React项目
前端·visual studio code
洛小豆5 小时前
她问我::is-logged 是啥?我说:前面加冒号,就是 Vue 在发暗号
前端·vue.js·面试
我有一棵树5 小时前
前端开发中 SCSS 变量与 CSS 变量的区别与实践选择,—— 两种变量别混为一谈
前端·css·scss