Carousel: add direction attribute and support vertical direction (#15122)

This commit is contained in:
hetech 2019-04-19 15:51:11 +08:00 committed by GitHub
parent 5425dc69fe
commit 2a65c9df7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 306 additions and 53 deletions

View File

@ -3,7 +3,7 @@
text-align: center;
border-right: solid 1px #eff2f6;
display: inline-block;
width: 50%;
width: 49%;
box-sizing: border-box;
&:last-child {
border-right: none;
@ -27,6 +27,14 @@
font-size: 18px;
line-height: 300px;
margin: 0;
&.small {
font-size: 14px;
line-height: 150px;
}
&.medium {
font-size: 14px;
line-height: 200px;
}
}
&:nth-child(2n) {
background-color: #99a9bf;
@ -36,12 +44,3 @@
}
}
.demo-carousel .small h3 {
font-size: 14px;
line-height: 150px;
}
.demo-carousel .medium h3 {
font-size: 14px;
line-height: 200px;
}

View File

@ -11,7 +11,7 @@ Loop a series of images or texts in a limited space
<span class="demonstration">Switch when indicator is hovered (default)</span>
<el-carousel height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -19,7 +19,7 @@ Loop a series of images or texts in a limited space
<span class="demonstration">Switch when indicator is clicked</span>
<el-carousel trigger="click" height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -122,7 +122,7 @@ When a page is wide enough but has limited height, you can activate card mode fo
<template>
<el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="item in 6" :key="item">
<h3>{{ item }}</h3>
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
@ -147,6 +147,38 @@ When a page is wide enough but has limited height, you can activate card mode fo
```
:::
By default, `direction` is `horizontal`. Let carousel be displayed in the vertical direction by setting `direction` to `vertical`.
:::demo
```html
<template>
<el-carousel height="200px" direction="vertical" :autoplay="false">
<el-carousel-item v-for="item in 4" :key="item">
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
<style>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
```
:::
### Carousel Attributes
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
@ -159,6 +191,7 @@ When a page is wide enough but has limited height, you can activate card mode fo
| arrow | when arrows are shown | string | always/hover/never | hover |
| type | type of the Carousel | string | card | — |
| loop | display the items in loop | boolean | - | true |
| direction | display direction | string | horizontal/vertical | horizontal |
### Carousel Events
| Event Name | Description | Parameters |

View File

@ -12,7 +12,7 @@ Presenta una serie de imágenes o textos en un espacio limitado
<span class="demonstration">Switch when indicator is hovered (default)</span>
<el-carousel height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -20,7 +20,7 @@ Presenta una serie de imágenes o textos en un espacio limitado
<span class="demonstration">Switch when indicator is clicked</span>
<el-carousel trigger="click" height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -126,7 +126,7 @@ Cuando una página es suficientemente ancha pero tiene una altura limitada, pued
<template>
<el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="item in 6" :key="item">
<h3>{{ item }}</h3>
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
@ -151,6 +151,38 @@ Cuando una página es suficientemente ancha pero tiene una altura limitada, pued
```
:::
By default, `direction` is `horizontal`. Let carousel be displayed in the vertical direction by setting `direction` to `vertical`.
:::demo
```html
<template>
<el-carousel height="200px" direction="vertical" :autoplay="false">
<el-carousel-item v-for="item in 4" :key="item">
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
<style>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
```
:::
### Atributos de Carousel
| Atributo | Descripcion | Tipo | Valores aceptados | Por defecto |
| ------------------ | ---------------------------------------- | ------- | ------------------ | ----------- |
@ -162,7 +194,8 @@ Cuando una página es suficientemente ancha pero tiene una altura limitada, pued
| indicator-position | Posición del indicador de paginación | string | outside/none | — |
| arrow | Cuando se muestran las flechas | string | always/hover/never | hover |
| type | Tipo de carrusel | string | card | — |
| loop | Si se muestra cíclicamente | boolean | — | true |
| loop | Si se muestra cíclicamente | boolean | — | true |
| direction | display direction | string | horizontal/vertical| horizontal |
### Eventos de Carousel
| Nombre evento | Descripción | Parametros |

View File

@ -11,7 +11,7 @@ Affiche en boucle une série d'images ou de textes dans un espace limité.
<span class="demonstration">Défile quand la souris passe sur l'indicateur (défaut)</span>
<el-carousel height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -19,7 +19,7 @@ Affiche en boucle une série d'images ou de textes dans un espace limité.
<span class="demonstration">Défile quand on clique sur l'indicateur</span>
<el-carousel trigger="click" height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -122,7 +122,7 @@ Quand la page est suffisement large mais avec une hauteur limitée, vous pouvez
<template>
<el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="item in 6" :key="item">
<h3>{{ item }}</h3>
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
@ -147,6 +147,38 @@ Quand la page est suffisement large mais avec une hauteur limitée, vous pouvez
```
:::
By default, `direction` is `horizontal`. Let carousel be displayed in the vertical direction by setting `direction` to `vertical`.
:::demo
```html
<template>
<el-carousel height="200px" direction="vertical" :autoplay="false">
<el-carousel-item v-for="item in 4" :key="item">
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
<style>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
```
:::
### Attributs du Carousel
| Attribut | Description | Type | Valeurs acceptées | Défaut |
|---------- |-------------- |---------- |-------------------------------- |-------- |
@ -159,6 +191,7 @@ Quand la page est suffisement large mais avec une hauteur limitée, vous pouvez
| arrow | Détermine quand les flèches sont affichés. | string | always/hover/never | hover |
| type | Type du carousel. | string | card | — |
| loop | Affiche les éléments en boucle. | boolean | - | true |
| direction | display direction | string | horizontal/vertical | horizontal |
### Évènements du Carousel
| Nom | Description | Paramètres |

View File

@ -13,7 +13,7 @@
<span class="demonstration">默认 Hover 指示器触发</span>
<el-carousel height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -21,7 +21,7 @@
<span class="demonstration">Click 指示器触发</span>
<el-carousel trigger="click" height="150px">
<el-carousel-item v-for="item in 4" :key="item">
<h3>{{ item }}</h3>
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
@ -122,7 +122,39 @@
<template>
<el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="item in 6" :key="item">
<h3>{{ item }}</h3>
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
<style>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
```
:::
### 方向
默认情况下,`direction` 为 `horizontal`。通过设置 `direction``vertical` 来让走马灯在垂直方向上显示。
:::demo
```html
<template>
<el-carousel height="200px" direction="vertical" :autoplay="false">
<el-carousel-item v-for="item in 3" :key="item">
<h3 class="medium">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</template>
@ -159,6 +191,7 @@
| arrow | 切换箭头的显示时机 | string | always/hover/never | hover |
| type | 走马灯的类型 | string | card | — |
| loop | 是否循环显示 | boolean | - | true |
| direction | 走马灯展示的方向 | string | horizontal/vertical | horizontal |
### Carousel Events
| 事件名称 | 说明 | 回调参数 |

View File

@ -10,11 +10,7 @@
'is-animating': animating
}"
@click="handleItemClick"
:style="{
msTransform: `translateX(${ translate }px) scale(${ scale })`,
webkitTransform: `translateX(${ translate }px) scale(${ scale })`,
transform: `translateX(${ translate }px) scale(${ scale })`
}">
:style="itemStyle">
<div
v-if="$parent.type === 'card'"
v-show="!active"
@ -25,6 +21,7 @@
</template>
<script>
import { autoprefixer } from 'element-ui/src/utils/util';
const CARD_SCALE = 0.83;
export default {
name: 'ElCarouselItem',
@ -63,7 +60,8 @@
return index;
},
calculateTranslate(index, activeIndex, parentWidth) {
calcCardTranslate(index, activeIndex) {
const parentWidth = this.$parent.$el.offsetWidth;
if (this.inStage) {
return parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1) / 4;
} else if (index < activeIndex) {
@ -73,23 +71,33 @@
}
},
calcTranslate(index, activeIndex, isVertical) {
const distance = this.$parent.$el[isVertical ? 'offsetHeight' : 'offsetWidth'];
return distance * (index - activeIndex);
},
translateItem(index, activeIndex, oldIndex) {
const parentWidth = this.$parent.$el.offsetWidth;
const parentType = this.$parent.type;
const parentDirection = this.parentDirection;
const length = this.$parent.items.length;
if (this.$parent.type !== 'card' && oldIndex !== undefined) {
if (parentType !== 'card' && oldIndex !== undefined) {
this.animating = index === activeIndex || index === oldIndex;
}
if (index !== activeIndex && length > 2 && this.$parent.loop) {
index = this.processIndex(index, activeIndex, length);
}
if (this.$parent.type === 'card') {
if (parentType === 'card') {
if (parentDirection === 'vertical') {
console.warn('[Element Warn][Carousel]vertical directionis not supported in card mode');
}
this.inStage = Math.round(Math.abs(index - activeIndex)) <= 1;
this.active = index === activeIndex;
this.translate = this.calculateTranslate(index, activeIndex, parentWidth);
this.translate = this.calcCardTranslate(index, activeIndex);
this.scale = this.active ? 1 : CARD_SCALE;
} else {
this.active = index === activeIndex;
this.translate = parentWidth * (index - activeIndex);
const isVertical = parentDirection === 'vertical';
this.translate = this.calcTranslate(index, activeIndex, isVertical);
}
this.ready = true;
},
@ -103,6 +111,21 @@
}
},
computed: {
parentDirection() {
return this.$parent.direction;
},
itemStyle() {
const translateType = this.parentDirection === 'vertical' ? 'translateY' : 'translateX';
const value = `${translateType}(${ this.translate }px) scale(${ this.scale })`;
const style = {
transform: value
};
return autoprefixer(style);
}
},
created() {
this.$parent && this.$parent.updateItems();
},

View File

@ -1,16 +1,16 @@
<template>
<div
class="el-carousel"
:class="{ 'el-carousel--card': type === 'card' }"
:class="carouselClasses"
@mouseenter.stop="handleMouseEnter"
@mouseleave.stop="handleMouseLeave">
<div
class="el-carousel__container"
:style="{ height: height }">
<transition name="carousel-arrow-left">
<transition
v-if="arrowDisplay"
name="carousel-arrow-left">
<button
type="button"
v-if="arrow !== 'never'"
v-show="(arrow === 'always' || hover) && (loop || activeIndex > 0)"
@mouseenter="handleButtonEnter('left')"
@mouseleave="handleButtonLeave"
@ -19,10 +19,11 @@
<i class="el-icon-arrow-left"></i>
</button>
</transition>
<transition name="carousel-arrow-right">
<transition
v-if="arrowDisplay"
name="carousel-arrow-right">
<button
type="button"
v-if="arrow !== 'never'"
v-show="(arrow === 'always' || hover) && (loop || activeIndex < items.length - 1)"
@mouseenter="handleButtonEnter('right')"
@mouseleave="handleButtonLeave"
@ -34,16 +35,20 @@
<slot></slot>
</div>
<ul
class="el-carousel__indicators"
v-if="indicatorPosition !== 'none'"
:class="{ 'el-carousel__indicators--labels': hasLabel, 'el-carousel__indicators--outside': indicatorPosition === 'outside' || type === 'card' }">
:class="indicatorsClasses">
<li
v-for="(item, index) in items"
class="el-carousel__indicator"
:class="{ 'is-active': index === activeIndex }"
:key="index"
:class="[
'el-carousel__indicator',
'el-carousel__indicator--' + direction,
{ 'is-active': index === activeIndex }]"
@mouseenter="throttledIndicatorHover(index)"
@click.stop="handleIndicatorClick(index)">
<button class="el-carousel__button"><span v-if="hasLabel">{{ item.label }}</span></button>
<button class="el-carousel__button">
<span v-if="hasLabel">{{ item.label }}</span>
</button>
</li>
</ul>
</div>
@ -87,6 +92,13 @@ export default {
loop: {
type: Boolean,
default: true
},
direction: {
type: String,
default: 'horizontal',
validator(val) {
return ['horizontal', 'vertical'].indexOf(val) !== -1;
}
}
},
@ -101,8 +113,31 @@ export default {
},
computed: {
arrowDisplay() {
return this.arrow !== 'never' && this.direction !== 'vertical';
},
hasLabel() {
return this.items.some(item => item.label.toString().length > 0);
},
carouselClasses() {
const classes = ['el-carousel', 'el-carousel--' + this.direction];
if (this.type === 'card') {
classes.push('el-carousel--card');
}
return classes;
},
indicatorsClasses() {
const classes = ['el-carousel__indicators', 'el-carousel__indicators--' + this.direction];
if (this.hasLabel) {
classes.push('el-carousel__indicators--labels');
}
if (this.indicatorPosition === 'outside' || this.type === 'card') {
classes.push('el-carousel__indicators--outside');
}
return classes;
}
},
@ -149,6 +184,7 @@ export default {
},
handleButtonEnter(arrow) {
if (this.direction === 'vertical') return;
this.items.forEach((item, index) => {
if (arrow === this.itemInStage(item, index)) {
item.hover = true;
@ -157,6 +193,7 @@ export default {
},
handleButtonLeave() {
if (this.direction === 'vertical') return;
this.items.forEach(item => {
item.hover = false;
});
@ -201,7 +238,6 @@ export default {
}
index = Number(index);
if (isNaN(index) || index !== Math.floor(index)) {
process.env.NODE_ENV !== 'production' &&
console.warn('[Element Warn][Carousel]index must be an integer.');
return;
}

View File

@ -2,9 +2,16 @@
@import "common/var";
@include b(carousel) {
overflow-x: hidden;
position: relative;
@include m(horizontal) {
overflow-x: hidden;
}
@include m(vertical) {
overflow-y: hidden;
}
@include e(container) {
position: relative;
height: 300px;
@ -49,13 +56,22 @@
@include e(indicators) {
position: absolute;
list-style: none;
bottom: 0;
left: 50%;
transform: translateX(-50%);
margin: 0;
padding: 0;
z-index: #{$--index-normal + 1};
@include m(horizontal) {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
@include m(vertical) {
right: 0;
top: 50%;
transform: translateY(-50%);
}
@include m(outside) {
bottom: #{$--carousel-indicator-height + $--carousel-indicator-padding-vertical * 2};
text-align: center;
@ -90,15 +106,26 @@
}
@include e(indicator) {
display: inline-block;
background-color: transparent;
padding: $--carousel-indicator-padding-vertical $--carousel-indicator-padding-horizontal;
cursor: pointer;
&:hover button {
opacity: 0.72;
}
@include m(horizontal) {
display: inline-block;
padding: $--carousel-indicator-padding-vertical $--carousel-indicator-padding-horizontal;
}
@include m(vertical) {
padding: $--carousel-indicator-padding-horizontal $--carousel-indicator-padding-vertical;
.el-carousel__button {
width: $--carousel-indicator-height;
height: #{$--carousel-indicator-width / 2};
}
}
@include when(active) {
button {
opacity: 1;

View File

@ -120,3 +120,18 @@ export const isIE = function() {
export const isEdge = function() {
return !Vue.prototype.$isServer && navigator.userAgent.indexOf('Edge') > -1;
};
export const autoprefixer = function(style) {
if (typeof style !== 'object') return style;
const rules = ['transform', 'transition', 'animation'];
const prefixes = ['ms-', 'webkit-'];
rules.forEach(rule => {
const value = style[rule];
if (rule && value) {
prefixes.forEach(prefix => {
style[prefix + rule] = value;
});
}
});
return style;
};

View File

@ -10,12 +10,13 @@ describe('Carousel', () => {
vm = createVue({
template: `
<div>
<el-carousel>
<el-carousel ref="carousel">
<el-carousel-item v-for="item in 3" :key="item"></el-carousel-item>
</el-carousel>
</div>
`
});
expect(vm.$refs.carousel.direction).to.be.equal('horizontal');
expect(vm.$el.querySelectorAll('.el-carousel__item').length).to.equal(3);
});
@ -256,5 +257,21 @@ describe('Carousel', () => {
}, 10);
}, 10);
});
it('vertical direction', () => {
vm = createVue({
template: `
<div>
<el-carousel ref="carousel" :autoplay="false" direction="vertical" height="100px">
<el-carousel-item v-for="item in 3" :key="item"></el-carousel-item>
</el-carousel>
</div>
`
});
const items = vm.$el.querySelectorAll('.el-carousel__item');
expect(vm.$refs.carousel.direction).to.be.equal('vertical');
expect(items[0].style.transform.indexOf('translateY') !== -1).to.be.true;
});
});

4
types/carousel.d.ts vendored
View File

@ -4,6 +4,7 @@ export type CarouselIndicatorTrigger = 'hover' | 'click'
export type CarouselIndicatorPosition = 'outside' | 'none'
export type CarouselArrowVisibility = 'always' | 'hover' | 'never'
export type CarouselType = 'card'
export type CarouselDirection = 'horizontal' | 'vertical'
/** Loop a series of images or texts in a limited space */
export declare class ElCarousel extends ElementUIComponent {
@ -31,6 +32,9 @@ export declare class ElCarousel extends ElementUIComponent {
/** Type of the Carousel */
type: CarouselType
/** Display direction */
direction: CarouselDirection
/**
* Manually switch slide by index
*