<template>
<div
class="g-slides"
name="slide"
@mouseenter="pause"
@mouseleave="doAutoPlay"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
<div class="g-slides-window" ref="window">
<slot></slot>
</div>
<div class="g-slides-dots">
<span
@click="select(n-1);"
class="span"
:class="{active:n-1 === selectedIndex}"
v-for="n in childrenLength"
:key="n"
:data-index="n"
></span>
</div>
<div class="g-slides-arrow" v-if="showArrow">
<div @click="select(selectedIndex-1)"><g-icon class="g-slides-arrow-left" icon="left"></g-icon></div>
<div @click="select(selectedIndex+1)"><g-icon class="g-slides-arrow-right" icon="right"></g-icon></div>
</div>
</div>
</template>
<script>
import GIcon from '../icon'
export default {
name: 'slides',
components: { GIcon },
props: {
value: {
type: String
},
autoPlay: {
type: Boolean,
default: true
},
showArrow: {
type: Boolean,
default: true
},
autoPlayTime: {
type: Number,
default:3000
}
},
data() {
return {
childrenLength: 0,
lastSelect: null,
timer: null,
startTouch: null
}
},
mounted() {
this.updateChildren()
this.childrenLength = this.$children.filter(item => item.$options.name === 'slides-item').length
this.doAutoPlay()
},
methods: {
onTouchStart(e) {
if (e.touches.length > 1) {
return
}
this.startTouch = e.touches[0]
},
onTouchMove() {
this.pause()
},
onTouchEnd(e) {
let endTouch = e.changedTouches[0]
const { clientX: x1, clientY: y1 } = this.startTouch
const { clientX: x2, clientY: y2 } = endTouch
let z = Math.sqrt(Math.pow(x2 - x1, 2), Math.pow(y2 - y1, 2))
let y = Math.abs(y2 - y1)
let rate = z / y
// 如果角度大于2则意味着滑动角度小于30°
if (rate > 2) {
if (x2 > x1) {
this.select(this.selectedIndex + 1)
} else {
this.select(this.selectedIndex - 1)
}
}
this.doAutoPlay()
},
doAutoPlay() {
if (this.autoPlay && !this.timer) {
const list = this.names
let n = list.indexOf(this.value || this.names[0])
const run = () => {
n++
Iif (n == list.length) {
n = 0
}
Iif (n === -1) {
n = list.length - 1
}
this.select(n)
this.timer = setTimeout(run, this.autoPlayTime)
}
this.timer = setTimeout(run, this.autoPlayTime)
}
},
updateChildren() {
this.$children.forEach(vm => {
let reverse = this.selectedIndex > this.lastSelect
Iif (
this.selectedIndex === this.names.length - 1 &&
this.lastSelect === 0
) {
reverse = false
}
Iif (
this.selectedIndex === 0 &&
this.lastSelect === this.names.length - 1
) {
reverse = true
}
vm.reverse = reverse
this.$nextTick(() => {
vm.selected = this.value || this.names[0]
})
})
},
select(n) {
this.lastSelect = this.selectedIndex
n === -1 ? (n = this.names.length - 1) : n
this.$emit('input', this.names[n])
},
pause() {
window.clearTimeout(this.timer)
this.timer = null
}
},
watch: {
value() {
this.updateChildren()
}
},
computed: {
selectedIndex() {
return this.names.indexOf(this.value) < 0
? 0
: this.names.indexOf(this.value)
},
names() {
return this.$children.filter(item => item.$options.name === 'slides-item').map(item => item.name)
}
}
}
</script>
<style lang="scss" scoped>
.g-slides {
display: inline-block;
position: relative;
&-window {
width: 100%;
overflow: hidden;
position: relative;
}
&-dots {
padding: 8px 0;
display: flex;
justify-content: center;
align-items: center;
position:absolute;
bottom:0;
left:50%;
transform: translateX(-50%);
.span {
width: 20px;
height: 20px;
display: inline-block;
border-radius: 50%;
background: #ddd;
display: inline-flex;
justify-content: center;
align-items: center;
margin: 0 8px;
cursor: pointer;
outline: none;
}
.active {
background: red;
cursor: default;
}
}
&-arrow{
position:absolute;
top:50%;
transform: translateY(-50%);
display:flex;
justify-content:space-between;
align-items:center;
width:100%;
svg{
display:flex;
justify-content:center;
align-items:center;
width:50px;
height:50px;
cursor: pointer;
}
}
}
</style> |