Commit 69460a549c3bbbde4b44905cdf6d6fcecdec5024
Committed by
GitHub
Merge pull request #2181 from lisafra/update-carousel
Update carousel component: add loop and radiusDot property
Showing
4 changed files
with
110 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: { |
... | ... | @@ -100,10 +115,19 @@ |
100 | 115 | trackStyles () { |
101 | 116 | return { |
102 | 117 | width: `${this.trackWidth}px`, |
103 | - transform: `translate3d(-${this.trackOffset}px, 0px, 0px)`, | |
118 | + transform: `translate3d(${-this.trackOffset}px, 0px, 0px)`, | |
104 | 119 | transition: `transform 500ms ${this.easing}` |
105 | 120 | }; |
106 | 121 | }, |
122 | + copyTrackStyles () { | |
123 | + return { | |
124 | + width: `${this.trackWidth}px`, | |
125 | + transform: `translate3d(${-this.trackCopyOffset}px, 0px, 0px)`, | |
126 | + transition: `transform 500ms ${this.easing}`, | |
127 | + position: 'absolute', | |
128 | + top: 0 | |
129 | + }; | |
130 | + }, | |
107 | 131 | arrowClasses () { |
108 | 132 | return [ |
109 | 133 | `${prefixCls}-arrow`, |
... | ... | @@ -142,6 +166,12 @@ |
142 | 166 | }); |
143 | 167 | } |
144 | 168 | }, |
169 | + // copy trackDom | |
170 | + initCopyTrackDom () { | |
171 | + this.$nextTick(() => { | |
172 | + this.$refs.copyTrack.innerHTML = this.$refs.originTrack.innerHTML; | |
173 | + }); | |
174 | + }, | |
145 | 175 | updateSlides (init) { |
146 | 176 | let slides = []; |
147 | 177 | let index = 1; |
... | ... | @@ -158,7 +188,6 @@ |
158 | 188 | }); |
159 | 189 | |
160 | 190 | this.slides = slides; |
161 | - | |
162 | 191 | this.updatePos(); |
163 | 192 | }, |
164 | 193 | updatePos () { |
... | ... | @@ -185,21 +214,57 @@ |
185 | 214 | this.updatePos(); |
186 | 215 | this.updateOffset(); |
187 | 216 | }, |
217 | + updateTrackPos (index) { | |
218 | + if (this.showCopyTrack) { | |
219 | + this.trackIndex = index; | |
220 | + } else { | |
221 | + this.copyTrackIndex = index; | |
222 | + } | |
223 | + }, | |
224 | + updateTrackIndex (index) { | |
225 | + if (this.showCopyTrack) { | |
226 | + this.copyTrackIndex = index; | |
227 | + } else { | |
228 | + this.trackIndex = index; | |
229 | + } | |
230 | + }, | |
188 | 231 | add (offset) { |
189 | - let index = this.currentIndex; | |
232 | + // 获取单个轨道的图片数 | |
233 | + let slidesLen = this.slides.length; | |
234 | + // 如果是无缝滚动,需要初始化双轨道位置 | |
235 | + if (this.loop) { | |
236 | + if (offset > 0) { | |
237 | + // 初始化左滑轨道位置 | |
238 | + this.hideTrackPos = -1; | |
239 | + } else { | |
240 | + // 初始化右滑轨道位置 | |
241 | + this.hideTrackPos = slidesLen; | |
242 | + } | |
243 | + this.updateTrackPos(this.hideTrackPos); | |
244 | + } | |
245 | + // 获取当前展示图片的索引值 | |
246 | + let index = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex; | |
190 | 247 | 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); | |
248 | + while (index < 0) index += slidesLen; | |
249 | + if (((offset > 0 && index === slidesLen) || (offset < 0 && index === slidesLen - 1)) && this.loop) { | |
250 | + // 极限值(左滑:当前索引为总图片张数, 右滑:当前索引为总图片张数 - 1)切换轨道 | |
251 | + this.showCopyTrack = !this.showCopyTrack; | |
252 | + this.trackIndex += offset; | |
253 | + this.copyTrackIndex += offset; | |
254 | + } else { | |
255 | + if (!this.loop) index = index % this.slides.length; | |
256 | + this.updateTrackIndex(index); | |
257 | + } | |
258 | + this.$emit('input', index === this.slides.length ? 0 : index); | |
195 | 259 | }, |
196 | 260 | arrowEvent (offset) { |
197 | 261 | this.setAutoplay(); |
198 | 262 | this.add(offset); |
199 | 263 | }, |
200 | 264 | dotsEvent (event, n) { |
201 | - if (event === this.trigger && this.currentIndex !== n) { | |
202 | - this.currentIndex = n; | |
265 | + let curIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex; | |
266 | + if (event === this.trigger && curIndex !== n) { | |
267 | + this.updateTrackIndex(n); | |
203 | 268 | this.$emit('input', n); |
204 | 269 | // Reset autoplay timer when trigger be activated |
205 | 270 | this.setAutoplay(); |
... | ... | @@ -215,7 +280,10 @@ |
215 | 280 | }, |
216 | 281 | updateOffset () { |
217 | 282 | this.$nextTick(() => { |
218 | - this.trackOffset = this.currentIndex * this.listWidth; | |
283 | + /* hack: revise copyTrack offset (1px) */ | |
284 | + let ofs = this.copyTrackIndex > 0 ? -1 : 1; | |
285 | + this.trackOffset = this.trackIndex * this.listWidth; | |
286 | + this.trackCopyOffset = this.copyTrackIndex * this.listWidth + ofs; | |
219 | 287 | }); |
220 | 288 | } |
221 | 289 | }, |
... | ... | @@ -228,6 +296,11 @@ |
228 | 296 | }, |
229 | 297 | currentIndex (val, oldVal) { |
230 | 298 | this.$emit('on-change', oldVal, val); |
299 | + }, | |
300 | + trackIndex () { | |
301 | + this.updateOffset(); | |
302 | + }, | |
303 | + copyTrackIndex () { | |
231 | 304 | this.updateOffset(); |
232 | 305 | }, |
233 | 306 | 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 | } | ... | ... |