|
|
@@ -0,0 +1,205 @@
|
|
|
+<template>
|
|
|
+ <div class="slide-layout" ref="layout">
|
|
|
+ <div class="slide-content"
|
|
|
+ :class="{animation: !pauseAnimation}"
|
|
|
+ :style="{
|
|
|
+ transform: 'translateX(' + (left + activeLeft) + 'px)',
|
|
|
+ width: contentWidth + 'px',
|
|
|
+ }">
|
|
|
+ <div v-for="(item, i) in slideItems" :key="i"
|
|
|
+ class="slide-item"
|
|
|
+ :class="{active: i === showActive}"
|
|
|
+ :style="{width: itemWidth + 'px', transform: 'translateX(' + i + '00%)'}">
|
|
|
+ <slot name="item" :data="item" :active="i === showActive" :index="i - extend" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="slide-control" v-if="ctrl">
|
|
|
+ <slot v-for="(item, i) in items" name="ctrl-item" :index="i" :active="i === mapActive" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+
|
|
|
+function extendItems (items, extend) {
|
|
|
+ let result = [...items]
|
|
|
+ if (items.length <= 1) return result
|
|
|
+
|
|
|
+ let insertBefore = items.length - 1
|
|
|
+ let insertAfter = 0
|
|
|
+
|
|
|
+ for (var i = 0; i < extend; i++) {
|
|
|
+ if (insertAfter >= items.length) {
|
|
|
+ insertAfter = 0
|
|
|
+ }
|
|
|
+ if (insertBefore < 0) {
|
|
|
+ insertBefore = items.length - 1
|
|
|
+ }
|
|
|
+
|
|
|
+ result.push(items[insertAfter])
|
|
|
+ result.unshift(items[insertBefore])
|
|
|
+ insertBefore--
|
|
|
+ insertAfter++
|
|
|
+ }
|
|
|
+
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'slide',
|
|
|
+ props: {
|
|
|
+ items: {
|
|
|
+ default: () => [],
|
|
|
+ type: Array
|
|
|
+ },
|
|
|
+ ctrl: {
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ extend: {
|
|
|
+ default: 1,
|
|
|
+ type: Number
|
|
|
+ },
|
|
|
+ focus: {
|
|
|
+ default: 0,
|
|
|
+ type: Number
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ let slideItems = extendItems(this.items, this.extend)
|
|
|
+
|
|
|
+ return {
|
|
|
+ slideItems,
|
|
|
+ active: 0,
|
|
|
+ mapActive: 0,
|
|
|
+ animations: [],
|
|
|
+ pauseAnimation: true,
|
|
|
+ outWidth: window.outerWidth,
|
|
|
+ activeLeft: 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ itemWidth () {
|
|
|
+ return this.outWidth
|
|
|
+ },
|
|
|
+ contentWidth () {
|
|
|
+ return this.slideItems.length * this.outWidth
|
|
|
+ },
|
|
|
+ showActive () {
|
|
|
+ return this.slideItems.length === 1 ? 0 : this.active + this.extend
|
|
|
+ },
|
|
|
+ left () {
|
|
|
+ return -this.showActive * this.itemWidth
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ focus: {
|
|
|
+ immediate: true,
|
|
|
+ handler (newVal) {
|
|
|
+ this.active = newVal
|
|
|
+ }
|
|
|
+ },
|
|
|
+ active (newVal) {
|
|
|
+ let newActive = null
|
|
|
+ if (newVal === -1) {
|
|
|
+ newActive = this.items.length - 1
|
|
|
+ } else if (newVal >= this.items.length) {
|
|
|
+ newActive = 0
|
|
|
+ }
|
|
|
+ if (newActive !== null) {
|
|
|
+ this.mapActive = newActive
|
|
|
+
|
|
|
+ clearTimeout(this.timeout1)
|
|
|
+ this.timeout1 = setTimeout(() => {
|
|
|
+ this.pauseAnimation = true
|
|
|
+ setTimeout(() => {
|
|
|
+ this.active = newActive
|
|
|
+ setTimeout(() => {
|
|
|
+ this.pauseAnimation = false
|
|
|
+ }, 16)
|
|
|
+ })
|
|
|
+ }, 300)
|
|
|
+ } else {
|
|
|
+ this.mapActive = this.active
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mapActive: {
|
|
|
+ immediate: true,
|
|
|
+ handler (newVal) {
|
|
|
+ this.$emit('slidechange', this.items[newVal], newVal)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ next () {
|
|
|
+ this.active++
|
|
|
+ },
|
|
|
+ prev () {
|
|
|
+ this.active--
|
|
|
+ },
|
|
|
+ startHandle (ev) {
|
|
|
+ this.pauseAnimation = true
|
|
|
+ this.startX = ev.touches[0].clientX
|
|
|
+ },
|
|
|
+ moveHandle (ev) {
|
|
|
+ this.activeLeft = ev.touches[0].clientX - this.startX
|
|
|
+ },
|
|
|
+ endHandle (ev) {
|
|
|
+ if (this.activeLeft > 100) {
|
|
|
+ this.prev()
|
|
|
+ } else if (this.activeLeft < -100) {
|
|
|
+ this.next()
|
|
|
+ }
|
|
|
+ this.pauseAnimation = false
|
|
|
+ this.activeLeft = 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.pauseAnimation = false
|
|
|
+ })
|
|
|
+ this.outWidth = this.$refs.layout.offsetWidth
|
|
|
+ this.startHandle = this.startHandle.bind(this)
|
|
|
+ this.moveHandle = this.moveHandle.bind(this)
|
|
|
+ this.endHandle = this.endHandle.bind(this)
|
|
|
+ this.$refs.layout.addEventListener('touchstart', this.startHandle, false)
|
|
|
+ this.$refs.layout.addEventListener('touchmove', this.moveHandle, false)
|
|
|
+ this.$refs.layout.addEventListener('touchend', this.endHandle, false)
|
|
|
+ },
|
|
|
+ beforeDestroy () {
|
|
|
+ this.$refs.layout.removeEventListener('touchstart', this.startHandle, false)
|
|
|
+ this.$refs.layout.removeEventListener('touchmove', this.moveHandle, false)
|
|
|
+ this.$refs.layout.removeEventListener('touchend', this.endHandle, false)
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.slide-layout {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.slide-content {
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.slide-item {
|
|
|
+ height: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.animation {
|
|
|
+ transition: all .2s linear;
|
|
|
+}
|
|
|
+
|
|
|
+.slide-control {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 7.5%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+}
|
|
|
+</style>
|