移入时暂停,移出后自动轮播
可自定义设置以下属性:
- 轮播图片数组(imageData),默认[]
- 滑动轮播间隔(interval),默认3000ms
- 图片宽度(imageWidth),默认400px
- 图片高度(imageHeight),默认300px
共使用两种滑动效果实现方式:
- 延时调用setInterval(setTimeout类似)
- requestAnimationFrame()(效果更好)
效果如下图:
①创建图片轮播组件Carousel.vue:
- 滑动效果使用setInterval延时调用**(使用setTimeout超时调用类似)**
<template>
<!-- 方法1:使用 will-change: transform; translateX(-${left}px); -->
<!-- <div class="m-slider" :style="`width: ${imageWidth}px;`" @mouseenter="onStop" @mouseleave="onStart">
<div :class="{ 'transition': transition }" :style="`width: ${width}px; will-change: transform; transform: translateX(${-left}px);`"> -->
<!-- 方法2:使用position: relative;和position: absolute; left: ${-left}px; -->
<div class="m-slider" :style="`position: relative; width: ${imageWidth}px; height: ${imageHeight + 36}px;`" @mouseenter="onStop" @mouseleave="onStart">
<div :class="{ 'transition': transition }" :style="`width: ${width}px; position: absolute; left: ${-left}px;`">
<div
v-for="(item, index) in imageData"
:key="index"
:style="`width: ${imageWidth}px;`"
class="m-image">
<img v-lazy="getDefault(item.imgUrl)" :alt="item.title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
<p class="u-img-title" :title="item.title">{
{ item.title }}</p>
</div>
<div class="m-image" :style="`width: ${imageWidth}px;`">
<img v-lazy="getDefault(imageData[0].imgUrl)" :alt="imageData[0].title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
<p class="u-img-title" :title="imageData[0].title">{
{ imageData[0].title }}</p>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload' // 图片懒加载插件使用版本v1.3.3
Vue.use(VueLazyLoad)
export default {
name: 'Carousel',
props: {
imageData: { // 轮播图片数组
type: Array,
default: () => {
return []
}
},
interval: { // 滑动轮播间隔
type: Number,
default: 3000
},
imageWidth: { // 图片宽度
type: Number,
default: 400
},
imageHeight: { // 图片高度
type: Number,
default: 300
}
},
data () {
return {
left: 0, // 滑动偏移值
transition: false, // 暂停时未完成滑动的过渡标志
slideTimer: null, // 自动切换定时器
moveTimer: null // 向左滑动定时器
}
},
computed: {
width () { // 容器宽度:(图片数组长度+1) * 图片宽度
return (this.imageData.length + 1) * this.imageWidth
},
len () {
return this.imageData.length || 0
}
},
mounted () {
window.onfocus = () => { // 页面激活状态
this.onStart()
}
window.onblur = () => { // 页面未激活状态
this.onStop()
}
this.onStart()
},
methods: {
getDefault (src) { // 获取懒加载默认图
return {
src: src,
error: require('../assets/images/default.png'),
loading: require('../assets/images/default.png')
}
},
onStart () {
if (this.len > 1) { // 超过一条时滑动
this.transition = false
this.onAutoSlide() // 自动滑动轮播
console.log('imageSlider start')
}
},
onStop () {
clearTimeout(this.slideTimer)
clearInterval(this.moveTimer)
this.sliderTimer = null
this.moveTimer = null
this.transition = true
this.left = Math.ceil(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整
console.log('imageSlider stop')
},
onAutoSlide () {
this.slideTimer = setTimeout(() => {
const target = this.left % (this.len * this.imageWidth) + this.imageWidth
this.autoMoveLeft(target)
}, this.interval)
},
// 滑动使用setInterval延时调用
autoMoveLeft (target) { // 自动切换,向左滑动效果
if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left
this.left = 0
}
this.moveTimer = setInterval(() => {
if (this.left >= target) {
clearInterval(this.moveTimer)
this.moveTimer = null
this.onAutoSlide() // 自动间隔切换下一张
} else {
var step = Math.ceil((target - this.left) / 10) // 越来越慢的滑动过程
this.left += step
}
}, 25)
},
beforeDestroy () {
clearTimeout(this.slideTimer)
clearInterval(this.moveTimer)
this.slideTimer = null
this.moveTimer = null
}
}
}
</script>
<style lang="less" scoped>
@themeColor: #1890FF;
.m-slider {
margin: 0 auto;
overflow: hidden;
.transition {
transition: transform 0.3s ease-out;
}
.m-image {
display: inline-block;
.u-img {
vertical-align: bottom; // 消除img标签底部的5px
cursor: pointer;
}
.u-img-title {
font-size: 18px;
color: #333;
line-height: 36px;
text-align: left;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:hover {
color: @themeColor;
}
}
}
}
</style>
- 滑动效果使用requestAnimationFrame**()**
<template> <div class="m-slider" :style="`width: ${imageWidth}px;`" @mouseenter="onStop" @mouseleave="onStart"> <div :class="{ 'transition': transition }" :style="`width: ${width}px; will-change: transform; transform: translateX(${-left}px);`"> <div v-for="(item, index) in imageData" :key="index" :style="`width: ${imageWidth}px;`" class="m-image"> <img v-lazy="getDefault(item.imgUrl)" :alt="item.title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/> <p class="u-img-title" :title="item.title">{ { item.title }}</p> </div> <div class="m-image" :style="`width: ${imageWidth}px;`"> <img v-lazy="getDefault(imageData[0].imgUrl)" :alt="imageData[0].title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/> <p class="u-img-title" :title="imageData[0].title">{ { imageData[0].title }}</p> </div> </div> </div> </template> <script> import Vue from 'vue' import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyLoad) // 图片懒加载插件 export default { name: 'Carousel', props: { imageData: { // 轮播图片数组 type: Array, default: () => { return [] } }, interval: { // 滑动轮播间隔 type: Number, default: 3000 }, imageWidth: { // 图片宽度 type: Number, default: 400 }, imageHeight: { // 图片高度 type: Number, default: 300 } }, data () { return { left: 0, // 滑动偏移值 transition: false, // 暂停时未完成滑动的过渡标志 slideTimer: null, // 自动切换定时器 moveRaf: null, // 动画回调标识 target: null, // 要移动到的目标位置 start: 0, end: 0, fpsRaf: null, // fps回调标识 step: 15 // 默认移动参数,对应60fps } }, computed: { width () { // 容器宽度:(图片数组长度+1) * 图片宽度 return (this.imageData.length + 1) * this.imageWidth }, len () { return this.imageData.length || 0 } }, mounted () { window.onfocus = () => { // 页面激活状态 this.onStart() } window.onblur = () => { // 页面未激活状态 this.onStop() } this.fpsRaf = requestAnimationFrame(this.getFPS) // 获取浏览器的刷新率 }, methods: { getDefault (src) { // 获取懒加载默认图 return { src: src, error: require('../assets/images/default.png'), loading: require('../assets/images/default.png') } }, getFPS (timestamp) { // 单位ms,用1000ms/两个时间的间隔≈刷新频率fps // console.log('timestamp:', timestamp) if (this.fpsRaf === 2) { this.start = timestamp } if (this.fpsRaf === 3) { this.end = timestamp const fps = Math.floor(1000 / (this.end - this.start)) if (fps === 120) { this.step = 30 } } this.fpsRaf = requestAnimationFrame(this.getFPS) if (this.fpsRaf > 3) { cancelAnimationFrame(this.fpsRaf) this.onStart() } }, onStart () { if (this.len > 1) { // 超过一条时滑动 this.transition = false this.onAutoSlide() // 自动滑动轮播 console.log('imageSlider start') } }, onStop () { clearTimeout(this.slideTimer) this.slideTimer = null cancelAnimationFrame(this.moveRaf) this.transition = true this.left = Math.ceil(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整 console.log('imageSlider stop') }, onAutoSlide () { this.slideTimer = setTimeout(() => { const target = this.left % (this.len * this.imageWidth) + this.imageWidth this.autoMoveLeft(target) }, this.interval) }, // 滑动效果使用requestAnimationFrame autoMoveLeft (target) { // 自动切换,向左滑动效果 if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left this.left = 0 } this.target = target this.moveRaf = requestAnimationFrame(this.autoLeftSlideEffect) }, autoLeftSlideEffect () { // 自动向左滑动效果 if (this.left >= this.target) { cancelAnimationFrame(this.moveRaf) this.onAutoSlide() // 自动间隔切换下一张 } else { const move = Math.ceil((this.target - this.left) / this.step) this.left += move this.moveRaf = requestAnimationFrame(this.autoLeftSlideEffect) } }, beforeDestroy () { clearTimeout(this.slideTimer) this.slideTimer = null } } } </script> <style lang="less" scoped> @themeColor: #1890FF; .m-slider { margin: 100px auto; overflow: hidden; .transition { transition: transform 0.3s ease-out; } .m-image { display: inline-block; .u-img { vertical-align: bottom; // 消除img标签底部的5px cursor: pointer; } .u-img-title { font-size: 18px; color: #333; line-height: 36px; text-align: left; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; &:hover { color: @themeColor; } } } } </style>
②在要使用滑动轮播图片的页面引入使用:
<Carousel :imageData="imageData" :imageWidth="460" :imageHeight="320" :interval="3000" /> import Carousel from '@/components/Carousel' components: { Carousel } data () { return { imageData: [ { title: 'image-1,image-1,image-1,image-1,image-1,image-1,image-1,image-1,image-1', imgUrl: 'image src...' }, { title: 'image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2', imgUrl: 'image src...' }, { title: 'image-3,image-3,image-3,image-3,image-3,image-3,image-3,image-3,image-3', imgUrl: 'image src...' } ] } }