Commit ccf544dcc2499ddde06aa7bb0076d4885efc9055
1 parent
9b24f1ab
Merge branch 'master' of https://github.com/iview/iview into 2.0
Update carousel component: add loop and radiusDot property # Conflicts: # package.json # src/components/date-picker/picker.vue # src/components/select/select.vue
Showing
4 changed files
with
115 additions
and
19 deletions
Show diff stats
examples/routers/carousel.vue
| 1 | 1 | <template> |
| 2 | 2 | <div> |
| 3 | - <Carousel v-model="v1" dots="inside" trigger="hover"> | |
| 3 | + <Carousel v-model="v1" dots="inside" trigger="hover" autoplay loop radius-dot> | |
| 4 | 4 | <Carousel-item> |
| 5 | - <Card> | |
| 6 | - <div class="demo-carousel">1</div> | |
| 7 | - </Card> | |
| 5 | + <div class="demo-carousel">1</div> | |
| 8 | 6 | </Carousel-item> |
| 9 | 7 | <Carousel-item> |
| 10 | 8 | <div class="demo-carousel">2</div> |
| ... | ... | @@ -24,8 +22,8 @@ |
| 24 | 22 | export default { |
| 25 | 23 | data () { |
| 26 | 24 | return { |
| 27 | - v1: 1 | |
| 28 | - } | |
| 25 | + v1: 0 | |
| 26 | + }; | |
| 29 | 27 | }, |
| 30 | 28 | methods: { |
| 31 | 29 | push () { | ... | ... |
src/components/carousel/carousel-item.vue
| ... | ... | @@ -27,6 +27,15 @@ |
| 27 | 27 | mounted () { |
| 28 | 28 | this.$parent.slotChange(); |
| 29 | 29 | }, |
| 30 | + watch: { | |
| 31 | + width (val) { | |
| 32 | + if (val && this.$parent.loop) { | |
| 33 | + this.$nextTick(() => { | |
| 34 | + this.$parent.initCopyTrackDom(); | |
| 35 | + }); | |
| 36 | + } | |
| 37 | + } | |
| 38 | + }, | |
| 30 | 39 | beforeDestroy () { |
| 31 | 40 | this.$parent.slotChange(); |
| 32 | 41 | } | ... | ... |
src/components/carousel/carousel.vue
| ... | ... | @@ -4,9 +4,11 @@ |
| 4 | 4 | <Icon type="chevron-left"></Icon> |
| 5 | 5 | </button> |
| 6 | 6 | <div :class="[prefixCls + '-list']"> |
| 7 | - <div :class="[prefixCls + '-track']" :style="trackStyles"> | |
| 7 | + <div :class="[prefixCls + '-track', showCopyTrack ? '' : 'higher']" :style="trackStyles" ref="originTrack"> | |
| 8 | 8 | <slot></slot> |
| 9 | 9 | </div> |
| 10 | + <div :class="[prefixCls + '-track', showCopyTrack ? 'higher' : '']" :style="copyTrackStyles" ref="copyTrack" v-if="loop"> | |
| 11 | + </div> | |
| 10 | 12 | </div> |
| 11 | 13 | <button :class="arrowClasses" class="right" @click="arrowEvent(1)"> |
| 12 | 14 | <Icon type="chevron-right"></Icon> |
| ... | ... | @@ -16,7 +18,7 @@ |
| 16 | 18 | <li :class="[n - 1 === currentIndex ? prefixCls + '-active' : '']" |
| 17 | 19 | @click="dotsEvent('click', n - 1)" |
| 18 | 20 | @mouseover="dotsEvent('hover', n - 1)"> |
| 19 | - <button></button> | |
| 21 | + <button :class="[radiusDot ? 'radius' : '']"></button> | |
| 20 | 22 | </li> |
| 21 | 23 | </template> |
| 22 | 24 | </ul> |
| ... | ... | @@ -48,6 +50,10 @@ |
| 48 | 50 | type: Number, |
| 49 | 51 | default: 2000 |
| 50 | 52 | }, |
| 53 | + loop: { | |
| 54 | + type: Boolean, | |
| 55 | + default: false | |
| 56 | + }, | |
| 51 | 57 | easing: { |
| 52 | 58 | type: String, |
| 53 | 59 | default: 'ease' |
| ... | ... | @@ -59,6 +65,10 @@ |
| 59 | 65 | return oneOf(value, ['inside', 'outside', 'none']); |
| 60 | 66 | } |
| 61 | 67 | }, |
| 68 | + radiusDot: { | |
| 69 | + type: Boolean, | |
| 70 | + default: false | |
| 71 | + }, | |
| 62 | 72 | trigger: { |
| 63 | 73 | type: String, |
| 64 | 74 | default: 'click', |
| ... | ... | @@ -84,11 +94,16 @@ |
| 84 | 94 | listWidth: 0, |
| 85 | 95 | trackWidth: 0, |
| 86 | 96 | trackOffset: 0, |
| 97 | + trackCopyOffset: 0, | |
| 98 | + showCopyTrack: false, | |
| 87 | 99 | slides: [], |
| 88 | 100 | slideInstances: [], |
| 89 | 101 | timer: null, |
| 90 | 102 | ready: false, |
| 91 | - currentIndex: this.value | |
| 103 | + currentIndex: this.value, | |
| 104 | + trackIndex: this.value, | |
| 105 | + copyTrackIndex: this.value, | |
| 106 | + hideTrackPos: -1, // 默认左滑 | |
| 92 | 107 | }; |
| 93 | 108 | }, |
| 94 | 109 | computed: { |
| ... | ... | @@ -97,13 +112,27 @@ |
| 97 | 112 | `${prefixCls}` |
| 98 | 113 | ]; |
| 99 | 114 | }, |
| 115 | + listStyle () { | |
| 116 | + return { | |
| 117 | + width: `${this.trackWidth * 2}px`, | |
| 118 | + }; | |
| 119 | + }, | |
| 100 | 120 | trackStyles () { |
| 101 | 121 | return { |
| 102 | 122 | width: `${this.trackWidth}px`, |
| 103 | - transform: `translate3d(-${this.trackOffset}px, 0px, 0px)`, | |
| 123 | + transform: `translate3d(${-this.trackOffset}px, 0px, 0px)`, | |
| 104 | 124 | transition: `transform 500ms ${this.easing}` |
| 105 | 125 | }; |
| 106 | 126 | }, |
| 127 | + copyTrackStyles () { | |
| 128 | + return { | |
| 129 | + width: `${this.trackWidth}px`, | |
| 130 | + transform: `translate3d(${-this.trackCopyOffset}px, 0px, 0px)`, | |
| 131 | + transition: `transform 500ms ${this.easing}`, | |
| 132 | + position: 'absolute', | |
| 133 | + top: 0 | |
| 134 | + }; | |
| 135 | + }, | |
| 107 | 136 | arrowClasses () { |
| 108 | 137 | return [ |
| 109 | 138 | `${prefixCls}-arrow`, |
| ... | ... | @@ -142,6 +171,12 @@ |
| 142 | 171 | }); |
| 143 | 172 | } |
| 144 | 173 | }, |
| 174 | + // copy trackDom | |
| 175 | + initCopyTrackDom () { | |
| 176 | + this.$nextTick(() => { | |
| 177 | + this.$refs.copyTrack.innerHTML = this.$refs.originTrack.innerHTML; | |
| 178 | + }); | |
| 179 | + }, | |
| 145 | 180 | updateSlides (init) { |
| 146 | 181 | let slides = []; |
| 147 | 182 | let index = 1; |
| ... | ... | @@ -158,7 +193,6 @@ |
| 158 | 193 | }); |
| 159 | 194 | |
| 160 | 195 | this.slides = slides; |
| 161 | - | |
| 162 | 196 | this.updatePos(); |
| 163 | 197 | }, |
| 164 | 198 | updatePos () { |
| ... | ... | @@ -185,21 +219,57 @@ |
| 185 | 219 | this.updatePos(); |
| 186 | 220 | this.updateOffset(); |
| 187 | 221 | }, |
| 222 | + updateTrackPos (index) { | |
| 223 | + if (this.showCopyTrack) { | |
| 224 | + this.trackIndex = index; | |
| 225 | + } else { | |
| 226 | + this.copyTrackIndex = index; | |
| 227 | + } | |
| 228 | + }, | |
| 229 | + updateTrackIndex (index) { | |
| 230 | + if (this.showCopyTrack) { | |
| 231 | + this.copyTrackIndex = index; | |
| 232 | + } else { | |
| 233 | + this.trackIndex = index; | |
| 234 | + } | |
| 235 | + }, | |
| 188 | 236 | add (offset) { |
| 189 | - let index = this.currentIndex; | |
| 237 | + // 获取单个轨道的图片数 | |
| 238 | + let slidesLen = this.slides.length; | |
| 239 | + // 如果是无缝滚动,需要初始化双轨道位置 | |
| 240 | + if (this.loop) { | |
| 241 | + if (offset > 0) { | |
| 242 | + // 初始化左滑轨道位置 | |
| 243 | + this.hideTrackPos = -1; | |
| 244 | + } else { | |
| 245 | + // 初始化右滑轨道位置 | |
| 246 | + this.hideTrackPos = slidesLen; | |
| 247 | + } | |
| 248 | + this.updateTrackPos(this.hideTrackPos); | |
| 249 | + } | |
| 250 | + // 获取当前展示图片的索引值 | |
| 251 | + let index = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex; | |
| 190 | 252 | index += offset; |
| 191 | - while (index < 0) index += this.slides.length; | |
| 192 | - index = index % this.slides.length; | |
| 193 | - this.currentIndex = index; | |
| 194 | - this.$emit('input', index); | |
| 253 | + while (index < 0) index += slidesLen; | |
| 254 | + if (((offset > 0 && index === slidesLen) || offset < 0 && index === slidesLen - 1) && this.loop) { | |
| 255 | + // 极限值(左滑:当前索引为总图片张数, 右滑:当前索引为总图片张数 - 1)切换轨道 | |
| 256 | + this.showCopyTrack = !this.showCopyTrack; | |
| 257 | + this.trackIndex += offset; | |
| 258 | + this.copyTrackIndex += offset; | |
| 259 | + } else { | |
| 260 | + if (!this.loop) index = index % this.slides.length; | |
| 261 | + this.updateTrackIndex(index); | |
| 262 | + } | |
| 263 | + this.$emit('input', index === this.slides.length ? 0 : index); | |
| 195 | 264 | }, |
| 196 | 265 | arrowEvent (offset) { |
| 197 | 266 | this.setAutoplay(); |
| 198 | 267 | this.add(offset); |
| 199 | 268 | }, |
| 200 | 269 | dotsEvent (event, n) { |
| 201 | - if (event === this.trigger && this.currentIndex !== n) { | |
| 202 | - this.currentIndex = n; | |
| 270 | + let curIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex; | |
| 271 | + if (event === this.trigger && curIndex !== n) { | |
| 272 | + this.updateTrackIndex(n); | |
| 203 | 273 | this.$emit('input', n); |
| 204 | 274 | // Reset autoplay timer when trigger be activated |
| 205 | 275 | this.setAutoplay(); |
| ... | ... | @@ -215,7 +285,10 @@ |
| 215 | 285 | }, |
| 216 | 286 | updateOffset () { |
| 217 | 287 | this.$nextTick(() => { |
| 218 | - this.trackOffset = this.currentIndex * this.listWidth; | |
| 288 | + /* hack: revise copyTrack offset (1px) */ | |
| 289 | + let ofs = this.copyTrackIndex > 0 ? -1 : 1; | |
| 290 | + this.trackOffset = this.trackIndex * this.listWidth; | |
| 291 | + this.trackCopyOffset = this.copyTrackIndex * this.listWidth + ofs; | |
| 219 | 292 | }); |
| 220 | 293 | } |
| 221 | 294 | }, |
| ... | ... | @@ -228,6 +301,11 @@ |
| 228 | 301 | }, |
| 229 | 302 | currentIndex (val, oldVal) { |
| 230 | 303 | this.$emit('on-change', oldVal, val); |
| 304 | + }, | |
| 305 | + trackIndex () { | |
| 306 | + this.updateOffset(); | |
| 307 | + }, | |
| 308 | + copyTrackIndex () { | |
| 231 | 309 | this.updateOffset(); |
| 232 | 310 | }, |
| 233 | 311 | height () { | ... | ... |
src/styles/components/carousel.less
| ... | ... | @@ -31,6 +31,9 @@ |
| 31 | 31 | overflow: hidden; |
| 32 | 32 | |
| 33 | 33 | z-index: 1; |
| 34 | + &.higher { | |
| 35 | + z-index: 2; | |
| 36 | + } | |
| 34 | 37 | } |
| 35 | 38 | |
| 36 | 39 | &-item { |
| ... | ... | @@ -159,6 +162,11 @@ |
| 159 | 162 | color: transparent; |
| 160 | 163 | |
| 161 | 164 | transition: all .5s; |
| 165 | + &.radius { | |
| 166 | + width: 6px; | |
| 167 | + height: 6px; | |
| 168 | + border-radius: 50%; | |
| 169 | + } | |
| 162 | 170 | } |
| 163 | 171 | |
| 164 | 172 | &:hover > button { |
| ... | ... | @@ -168,6 +176,9 @@ |
| 168 | 176 | &.@{carousel-prefix-cls}-active > button { |
| 169 | 177 | opacity: 1; |
| 170 | 178 | width: 24px; |
| 179 | + &.radius{ | |
| 180 | + width: 6px; | |
| 181 | + } | |
| 171 | 182 | } |
| 172 | 183 | } |
| 173 | 184 | } | ... | ... |