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 | } | ... | ... |