| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| --> |
| <!-- Created by Tw93 on 17/07/28. --> |
| |
| <template> |
| <div :style="containerS"> |
| <div :ref="`sliderCtn_${sliderId}`" |
| :style="{width:cardWidth+'px',height:cardS.height+'px',transform: `translateX(-${currentIndex * (cardS.width + cardS.spacing)}px)`}" |
| @panstart="onPanStart" |
| @panmove="onPanMove" |
| @panend="onPanEnd" |
| @horizontalpan="onEpPanStart"> |
| <div class="slider" |
| v-for="(v,index) in cardList" |
| :ref="`card${index}_${sliderId}`" |
| :style="{transform: `scale(${index===currentIndex ? 1 : cardS.scale})`,left: `${index * (cardS.width+cardS.spacing)}px`,marginLeft:`${(containerS.width - cardS.width) / 2}px`,width: cardS.width+'px', height: cardS.height+'px'}"> |
| <slot :name="`card${index}_${sliderId}`"></slot> |
| </div> |
| <slot name="pull-more"></slot> |
| </div> |
| </div> |
| </template> |
| |
| <style scoped> |
| .slider { |
| position: absolute; |
| top: 0; |
| } |
| </style> |
| |
| <script> |
| const swipeBack = weex.requireModule('swipeBack'); |
| const animation = weex.requireModule('animation'); |
| import Utils from '../utils'; |
| import BindEnv from '../utils/bind-env'; |
| import Binding from 'weex-bindingx/lib/index.weex.js'; |
| |
| export default { |
| props: { |
| sliderId: { |
| type: [String, Number], |
| default: 1 |
| }, |
| panOffset: { |
| type: Number, |
| default: 80 |
| }, |
| cardLength: { |
| type: Number, |
| default: 1 |
| }, |
| selectIndex: { |
| type: Number, |
| default: 0 |
| }, |
| enableSwipe: { |
| type: Boolean, |
| default: true |
| }, |
| containerS: { |
| type: Object, |
| default: () => ({ |
| position: 'relative', |
| width: 750, |
| height: 352 |
| }) |
| }, |
| cardS: { |
| type: Object, |
| default: () => ({ |
| width: 360, |
| height: 300, |
| spacing: 0, |
| scale: 0.75 |
| }) |
| }, |
| autoPlay: { |
| type: Boolean, |
| default: false |
| }, |
| interval: { |
| type: [Number, String], |
| default: 1200 |
| } |
| }, |
| data: () => ({ |
| isMoving: false, |
| gesToken: 0, |
| startX: 0, |
| startTime: 0, |
| currentIndex: 0, |
| autoPlayTimer: null |
| }), |
| computed: { |
| cardList () { |
| return new Array(this.cardLength + 1).join().split(''); |
| }, |
| cardWidth () { |
| return (this.cardLength - 1) * this.cardS.width + this.containerS.width + 235; |
| } |
| }, |
| created () { |
| this.currentIndex = this.selectIndex; |
| }, |
| mounted () { |
| // ios和页面返回冲突,组件里面将ios系统横滑返回禁止 |
| if (swipeBack && swipeBack.forbidSwipeBack) { |
| swipeBack.forbidSwipeBack(true); |
| } |
| |
| setTimeout(() => { |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| if (BindEnv.supportsEB() && sliderCtn && sliderCtn.ref) { |
| Binding.prepare && Binding.prepare({ |
| anchor: sliderCtn.ref, |
| eventType: 'pan' |
| }); |
| } |
| }, 20); |
| this.checkNeedAutoPlay(); |
| }, |
| methods: { |
| onPanStart (e) { |
| if (!this.enableSwipe || BindEnv.supportsEB()) { |
| return; |
| } |
| this.clearAutoPlay(); |
| this.startX = e.changedTouches[0].clientX; |
| this.startTime = Date.now(); |
| }, |
| onPanMove (e) { |
| if (!this.enableSwipe || BindEnv.supportsEB()) { |
| return; |
| } |
| const moveX = e.changedTouches[0].clientX - this.startX; |
| const index = this.loopedIndex(this.currentIndex, this.cardLength); |
| const cardLength = this.cardLength; |
| const currentCardLeft = this.currentIndex * (this.cardS.width + this.cardS.spacing); |
| |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| sliderCtn && animation.transition(sliderCtn, { |
| styles: { |
| transform: `translateX(${moveX - currentCardLeft}px)` |
| }, |
| timingFunction: 'ease', |
| delay: 0, |
| duration: 0 |
| }, () => { |
| }); |
| |
| if (this.cardS.scale !== 1) { |
| const currentCard = this.$refs[`card${this.loopedIndex(index, cardLength)}_${this.sliderId}`][0]; |
| currentCard && animation.transition(currentCard, { |
| styles: { |
| transform: `scale(${1 - Math.abs(moveX) / (this.cardS.width) * (1 - this.cardS.scale)})` |
| }, |
| timingFunction: 'ease', |
| delay: 0, |
| duration: 0 |
| }, () => { |
| }); |
| // 左边的卡片 |
| const leftCard = this.$refs[`card${this.loopedIndex(index - 1, cardLength)}_${this.sliderId}`][0]; |
| // loop 函数负数返回 0,这里有点冲突 |
| if (leftCard && index !== 0) { |
| animation.transition(leftCard, { |
| styles: { |
| transform: `scale(${1 - Math.abs(moveX - this.cardS.width) / (this.cardS.width) * (1 - this.cardS.scale)})` |
| }, |
| timingFunction: 'ease', |
| delay: 0, |
| duration: 0 |
| }, () => { |
| }); |
| } |
| // 右边卡片 |
| const rightCard = this.$refs[`card${this.loopedIndex(index + 1, cardLength)}_${this.sliderId}`][0]; |
| rightCard && animation.transition(rightCard, { |
| styles: { |
| transform: `scale(${1 - Math.abs(this.cardS.width + moveX) / (this.cardS.width) * (1 - this.cardS.scale)})` |
| }, |
| timingFunction: 'ease', |
| delay: 0, |
| duration: 0 |
| }, () => { |
| }); |
| } |
| }, |
| onEpPanStart (e) { |
| if (this.enableSwipe && BindEnv.supportsEB() && e.state === 'start') { |
| this.clearAutoPlay(); |
| setTimeout(() => { |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| this.bindExp(sliderCtn); |
| }, 0) |
| } |
| }, |
| onPanEnd (e) { |
| if (!this.enableSwipe || BindEnv.supportsEB()) { |
| return; |
| } |
| this.panEnd(e); |
| }, |
| panEnd (e) { |
| this.isMoving = true; |
| let moveX = e.deltaX; |
| |
| if (Utils.env.isWeb()) { |
| moveX = e.changedTouches[0].clientX - this.startX; |
| } |
| |
| const originIndex = this.currentIndex; |
| let selectIndex = originIndex; |
| const duration = Date.now() - this.startTime; |
| const panOffset = this.panOffset || (this.cardS.width / 2); |
| const isPullMore = (selectIndex === this.cardLength - 1 && (moveX < -68 || (moveX < -10 && duration < 200))); |
| |
| if (isPullMore) { |
| this.$emit('wxcEpSliderPullMore', { currentIndex: selectIndex }); |
| } |
| |
| if (moveX < -panOffset || (moveX < -10 && duration < 200)) { |
| if (selectIndex !== this.cardLength - 1) { |
| selectIndex++; |
| } |
| } else if (moveX > panOffset || (moveX > 10 && duration < 500)) { |
| if (selectIndex !== 0) { |
| selectIndex--; |
| } |
| } |
| |
| this.slideTo(originIndex, selectIndex); |
| setTimeout(() => { this.checkNeedAutoPlay() }, 3000); |
| }, |
| slideTo (originIndex, selectIndex) { |
| let currentCardScale = 1; |
| let rightCardScale = this.cardS.scale; |
| let leftCardScale = this.cardS.scale; |
| const duration = (selectIndex === 0 && originIndex === this.cardLength - 1 && this.cardLength !== 2) ? 0.00001 : 300; |
| this.$emit('wxcEpSliderCurrentIndexSelected', { currentIndex: selectIndex }); |
| if (originIndex < selectIndex) { |
| currentCardScale = this.cardS.scale; |
| rightCardScale = 1; |
| } else if (originIndex > selectIndex) { |
| currentCardScale = this.cardS.scale; |
| leftCardScale = 1; |
| } |
| const currentCard = this.$refs[`card${this.loopedIndex(originIndex, this.cardLength)}_${this.sliderId}`][0]; |
| currentCard && animation.transition(currentCard, { |
| styles: { |
| transform: `scale(${currentCardScale})` |
| }, |
| timingFunction: 'ease', |
| duration |
| }, () => { |
| }); |
| |
| const leftCard = this.$refs[`card${this.loopedIndex(originIndex - 1, this.cardLength)}_${this.sliderId}`][0]; |
| if (this.isMoving && leftCard && originIndex !== 0) { |
| animation.transition(leftCard, { |
| styles: { |
| transform: `scale(${leftCardScale})` |
| }, |
| timingFunction: 'ease', |
| duration |
| }, () => { |
| }); |
| } |
| const rightCard = this.$refs[`card${this.loopedIndex(originIndex + 1, this.cardLength)}_${this.sliderId}`][0]; |
| if (rightCard && originIndex !== this.cardLength - 1) { |
| animation.transition(rightCard, { |
| styles: { |
| transform: `scale(${rightCardScale})` |
| }, |
| timingFunction: 'ease', |
| duration |
| }, () => { |
| }); |
| } |
| |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| sliderCtn && animation.transition(sliderCtn, { |
| styles: { |
| transform: `translateX(-${selectIndex * (this.cardS.width + this.cardS.spacing)}px)` |
| }, |
| timingFunction: 'ease', |
| duration |
| }, () => { |
| this.isMoving = false; |
| if (originIndex !== selectIndex) { |
| this.currentIndex = selectIndex; |
| } |
| }); |
| }, |
| // 使index维持在0-length之间循环 |
| loopedIndex (index, total) { |
| if (index < 0) { |
| index = index + (1 - index / total) * total; |
| } |
| return parseInt(index % total); |
| }, |
| bindExp (element) { |
| if (element && element.ref) { |
| if (this.isMoving && this.gesToken !== 0) { |
| Binding.unbind({ |
| eventType: 'pan', |
| token: this.gesToken |
| }) |
| this.gesToken = 0; |
| return; |
| } |
| |
| this.startTime = Date.now(); |
| const index = this.loopedIndex(this.currentIndex, this.cardLength); |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| const currentCard = this.$refs[`card${index}_${this.sliderId}`][0]; |
| let rightCard = null; |
| let leftCard = null; |
| const currentCardLeft = this.currentIndex * (this.cardS.width + this.cardS.spacing); |
| |
| // 卡片容器 |
| const sliderCtnExp = `x - ${currentCardLeft}`; |
| const args = [ |
| { |
| element: sliderCtn.ref, |
| property: 'transform.translateX', |
| expression: sliderCtnExp |
| } |
| ]; |
| |
| if (this.cardS.scale !== 1) { |
| const currentCardExp = `1-abs(x)/${this.cardS.width}*${1 - this.cardS.scale}`; |
| const leftCardExp = `1-abs(x-${this.cardS.width})/${this.cardS.width}*${1 - this.cardS.scale}`; |
| const rightCardExp = `1-abs(${this.cardS.width}+x)/${this.cardS.width}*${1 - this.cardS.scale}`; |
| |
| args.push({ |
| element: currentCard.ref, |
| property: 'transform.scale', |
| expression: currentCardExp |
| }); |
| |
| if (index === 0 && this.$refs[`card${index + 1}_${this.sliderId}`]) { |
| rightCard = this.$refs[`card${index + 1}_${this.sliderId}`][0]; |
| args.push({ |
| element: rightCard.ref, |
| property: 'transform.scale', |
| expression: rightCardExp |
| }); |
| } else if (index === this.cardLength - 1 && this.$refs[`card${index - 1}_${this.sliderId}`]) { |
| leftCard = this.$refs[`card${index - 1}_${this.sliderId}`][0]; |
| args.push({ |
| element: leftCard.ref, |
| property: 'transform.scale', |
| expression: leftCardExp |
| }); |
| } else if (this.$refs[`card${index - 1}_${this.sliderId}`]) { |
| // 左边卡片 |
| leftCard = this.$refs[`card${index - 1}_${this.sliderId}`][0]; |
| args.push({ |
| element: leftCard.ref, |
| property: 'transform.scale', |
| expression: leftCardExp |
| }); |
| // 右边卡片 |
| rightCard = this.$refs[`card${index + 1}_${this.sliderId}`][0]; |
| args.push({ |
| element: rightCard.ref, |
| property: 'transform.scale', |
| expression: rightCardExp |
| }); |
| } |
| } |
| |
| const gesTokenObj = Binding.bind({ |
| anchor: element.ref, |
| eventType: 'pan', |
| props: args |
| }, (e) => { |
| if (!this.isMoving && (e.state === 'end' || e.state === 'cancel' || e.state === 'exit')) { |
| this.panEnd(e); |
| } |
| }); |
| |
| this.gesToken = gesTokenObj.token; |
| } |
| }, |
| checkNeedAutoPlay () { |
| if (this.autoPlay) { |
| this.clearAutoPlay(); |
| this.autoPlayTimer = setInterval(() => { |
| this.slideTo(this.currentIndex, this.loopedIndex(this.currentIndex + 1, this.cardLength)); |
| }, parseInt(this.interval)); |
| } |
| }, |
| clearAutoPlay () { |
| this.autoPlayTimer && clearInterval(this.autoPlayTimer); |
| }, |
| rebind () { |
| const sliderCtn = this.$refs[`sliderCtn_${this.sliderId}`]; |
| if (sliderCtn && sliderCtn.ref) { |
| Binding.unbind({ |
| eventType: 'pan', |
| token: this.gesToken |
| }); |
| this.gesToken = 0; |
| this.bindExp(sliderCtn); |
| } |
| }, |
| manualSetPage (selectIndex) { |
| this.clearAutoPlay(); |
| const step = this.currentIndex < selectIndex ? 1 : -1; |
| this.slideTo(this.loopedIndex(selectIndex - step, this.cardLength), selectIndex); |
| setTimeout(() => { |
| this.checkNeedAutoPlay() |
| }, 3000); |
| } |
| } |
| } |
| </script> |
| |