mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:58:07 +08:00
feat:carousel支持卡片动画模式 (#6354)
* feat:carousel支持多图配置 * Update carousel.md --------- Co-authored-by: zhaowenli <zhaowenli@baidu.com> Co-authored-by: RUNZE LU <36724300+lurunze1226@users.noreply.github.com>
This commit is contained in:
parent
5dd69e55c1
commit
b69f9c5b08
@ -101,10 +101,41 @@ itemSchema: {
|
||||
}
|
||||
```
|
||||
|
||||
## 多图模式
|
||||
|
||||
> `2.8.1` 及以上版本
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "carousel",
|
||||
"auto": true,
|
||||
"thumbMode": "cover",
|
||||
"animation": "slide",
|
||||
"multiple": {count: 3},
|
||||
"interval": 0,
|
||||
"duration": 5000,
|
||||
"height": 300,
|
||||
"options": [
|
||||
{
|
||||
"image": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692722/4f3cb4202335.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
},
|
||||
{
|
||||
"image": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395692942/d8e4992057f9.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
},
|
||||
{
|
||||
"image": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395693148/1314a2a3d3f6.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
},
|
||||
{
|
||||
"image": "https://internal-amis-res.cdn.bcebos.com/images/2020-1/1578395693379/8f2e79f82be0.jpeg@s_0,w_216,l_1,f_jpg,q_80"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ---------------------------- | --------- | ---------------------- | ------------------------------------------------------- |
|
||||
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| ---------------------------- | --------- | ---------------------- | ------------------------------------------------------- | --- |
|
||||
| type | `string` | `"carousel"` | 指定为 Carousel 渲染器 |
|
||||
| className | `string` | `"panel-default"` | 外层 Dom 的类名 |
|
||||
| options | `array` | `[]` | 轮播面板数据 |
|
||||
@ -119,13 +150,16 @@ itemSchema: {
|
||||
| itemSchema | `object` | | 自定义`schema`来展示数据 |
|
||||
| auto | `boolean` | `true` | 是否自动轮播 |
|
||||
| interval | `string` | `5s` | 切换动画间隔 |
|
||||
| duration | `string` | `0.5s` | 切换动画时长 |
|
||||
| duration | `number` | `500` | 切换动画时长(ms) |
|
||||
| width | `string` | `auto` | 宽度 |
|
||||
| height | `string` | `200px` | 高度 |
|
||||
| controls | `array` | `['dots', 'arrows']` | 显示左右箭头、底部圆点索引 |
|
||||
| controlsTheme | `string` | `light` | 左右箭头、底部圆点索引颜色,默认`light`,另有`dark`模式 |
|
||||
| animation | `string` | fade | 切换动画效果,默认`fade`,另有`slide`模式 |
|
||||
| thumbMode | `string` | `"cover" \| "contain"` | 图片默认缩放模式 |
|
||||
| multiple | `object` | `{count: 1}` | 多图模式,count表示展示的数量 | `2.8.1` |
|
||||
| alwaysShowArrow | `boolean` | `false` | 是否一直显示箭头,为false时鼠标hover才会显示 | `2.8.1` |
|
||||
| icons | {prev: `SchemaCollection`; next: `SchemaCollection`;} | | 自定义箭头图标 | `2.8.1` |
|
||||
|
||||
## 事件表
|
||||
|
||||
|
@ -176,6 +176,15 @@
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.#{$ns}Carousel-arrow--always {
|
||||
.#{$ns}Carousel-leftArrow {
|
||||
display: block;
|
||||
}
|
||||
.#{$ns}Carousel-rightArrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.#{$ns}Carousel-leftArrow {
|
||||
display: block;
|
||||
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||
import Transition, {
|
||||
ENTERED,
|
||||
ENTERING,
|
||||
EXITING
|
||||
EXITING,
|
||||
EXITED
|
||||
} from 'react-transition-group/Transition';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {resolveVariableAndFilter} from 'amis-core';
|
||||
@ -88,6 +89,26 @@ export interface CarouselSchema extends BaseSchema {
|
||||
* 配置固定值
|
||||
*/
|
||||
options?: Array<any>;
|
||||
|
||||
/**
|
||||
* 是否一直显示箭头
|
||||
*/
|
||||
alwaysShowArrow?: boolean;
|
||||
|
||||
/**
|
||||
* 多图模式配置项
|
||||
*/
|
||||
multiple?: {
|
||||
count: number
|
||||
};
|
||||
|
||||
/**
|
||||
* 自定义箭头图标
|
||||
*/
|
||||
icons?: {
|
||||
prev?: SchemaCollection;
|
||||
next?: SchemaCollection;
|
||||
};
|
||||
}
|
||||
|
||||
const animationStyles: {
|
||||
@ -108,6 +129,7 @@ export interface CarouselState {
|
||||
current: number;
|
||||
options: any[];
|
||||
nextAnimation: string;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const defaultSchema = {
|
||||
@ -156,6 +178,8 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
| 'animation'
|
||||
| 'controls'
|
||||
| 'placeholder'
|
||||
| 'multiple'
|
||||
| 'alwaysShowArrow'
|
||||
> = {
|
||||
auto: true,
|
||||
interval: 5000,
|
||||
@ -163,13 +187,16 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
controlsTheme: 'light',
|
||||
animation: 'fade',
|
||||
controls: ['dots', 'arrows'],
|
||||
placeholder: '-'
|
||||
placeholder: '-',
|
||||
multiple: {count: 1},
|
||||
alwaysShowArrow: false
|
||||
};
|
||||
|
||||
state = {
|
||||
current: 0,
|
||||
options: this.props.options || getPropValue(this.props) || [],
|
||||
nextAnimation: ''
|
||||
nextAnimation: '',
|
||||
loading: false
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -294,11 +321,19 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
|
||||
@autobind
|
||||
next() {
|
||||
const multiple = this.props.multiple;
|
||||
if (this.state.loading && multiple && multiple.count > 1) {
|
||||
return;
|
||||
}
|
||||
this.autoSlide('next');
|
||||
}
|
||||
|
||||
@autobind
|
||||
prev() {
|
||||
const multiple = this.props.multiple;
|
||||
if (this.state.loading && multiple && multiple.count > 1) {
|
||||
return;
|
||||
}
|
||||
this.autoSlide('prev');
|
||||
}
|
||||
|
||||
@ -310,8 +345,12 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
|
||||
@autobind
|
||||
async changeSlide(index: number) {
|
||||
const {current} = this.state;
|
||||
const {dispatchEvent, data} = this.props;
|
||||
const {current, loading} = this.state;
|
||||
const {dispatchEvent, data, multiple} = this.props;
|
||||
|
||||
if (loading && multiple && multiple.count > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rendererEvent = await dispatchEvent(
|
||||
'change',
|
||||
@ -369,14 +408,37 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
|
||||
@autobind
|
||||
handleMouseEnter() {
|
||||
const multiple = this.props.multiple;
|
||||
if (multiple && multiple.count > 1) {
|
||||
return;
|
||||
}
|
||||
this.clearAutoTimeout();
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleMouseLeave() {
|
||||
const multiple = this.props.multiple;
|
||||
if (multiple && multiple.count > 1) {
|
||||
return;
|
||||
}
|
||||
this.prepareAutoSlide();
|
||||
}
|
||||
|
||||
// 处理options
|
||||
getNewOptions(options: any, count: number = 1) {
|
||||
let newOptions: Array<any> = options;
|
||||
if (Array.isArray(options) && options.length) {
|
||||
newOptions = new Array(options.length);
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
newOptions[i] = new Array(count);
|
||||
for(let j = 0; j < count; j++) {
|
||||
newOptions[i][j] = options[(i + j) % options.length];
|
||||
}
|
||||
}
|
||||
}
|
||||
return newOptions;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
render,
|
||||
@ -391,9 +453,13 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
controlsTheme,
|
||||
placeholder,
|
||||
data,
|
||||
name
|
||||
name,
|
||||
duration,
|
||||
multiple,
|
||||
alwaysShowArrow,
|
||||
icons
|
||||
} = this.props;
|
||||
const {options, current, nextAnimation} = this.state;
|
||||
const {options, current, nextAnimation, loading} = this.state;
|
||||
|
||||
let body: JSX.Element | null = null;
|
||||
let carouselStyles: {
|
||||
@ -408,6 +474,15 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
const animationName = nextAnimation || animation;
|
||||
|
||||
if (Array.isArray(options) && options.length) {
|
||||
let multipleCount = 1;
|
||||
if (multiple && typeof multiple.count === 'number' && multiple.count >= 2) {
|
||||
multipleCount = Math.floor(multiple.count) < options.length ? Math.floor(multiple.count) : options.length;
|
||||
}
|
||||
const newOptions = this.getNewOptions(options, multipleCount);
|
||||
const transitionDuration = multipleCount > 1 && typeof duration === 'number'
|
||||
? `${duration}ms`: (duration || '500ms');
|
||||
const timeout = multipleCount > 1 && typeof duration === 'number' ? duration : 500;
|
||||
|
||||
body = (
|
||||
<div
|
||||
ref={this.wrapperRef}
|
||||
@ -420,7 +495,7 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
in={key === current}
|
||||
timeout={500}
|
||||
timeout={timeout}
|
||||
key={key}
|
||||
>
|
||||
{(status: string) => {
|
||||
@ -430,6 +505,40 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
(item: HTMLElement) => item.offsetHeight
|
||||
);
|
||||
}
|
||||
if (multipleCount > 1) {
|
||||
if ((status === ENTERING || status === EXITING) && !loading) {
|
||||
this.setState({loading: true});
|
||||
} else if ((status === ENTERED || status === EXITED) && loading) {
|
||||
this.setState({loading: false});
|
||||
}
|
||||
}
|
||||
|
||||
const transformStyles: {
|
||||
[propName: string]: number;
|
||||
} = {
|
||||
[ENTERING]: 0,
|
||||
[ENTERED]: 0,
|
||||
[EXITING]: animationName === 'slideRight' ? 100 / multipleCount : -100 / multipleCount,
|
||||
[EXITED]: animationName === 'slideRight' ? -100 / multipleCount : 100 / multipleCount
|
||||
};
|
||||
const itemStyle = multipleCount > 1 ? {
|
||||
transitionTimingFunction: 'linear',
|
||||
transitionDuration: transitionDuration,
|
||||
...(animation === 'slide' ? {transform: `translateX(${transformStyles[status]}%)`} : {})
|
||||
} : {};
|
||||
const itemRender = (option: any) => render(
|
||||
`${current}/body`,
|
||||
itemSchema ? itemSchema : (defaultSchema as any),
|
||||
{
|
||||
thumbMode: this.props.thumbMode,
|
||||
data: createObject(
|
||||
data,
|
||||
isObject(option)
|
||||
? option
|
||||
: {item: option, [name!]: option}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -438,20 +547,19 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
animationName,
|
||||
animationStyles[status]
|
||||
)}
|
||||
style={itemStyle}
|
||||
>
|
||||
{render(
|
||||
`${current}/body`,
|
||||
itemSchema ? itemSchema : (defaultSchema as any),
|
||||
{
|
||||
thumbMode: this.props.thumbMode,
|
||||
data: createObject(
|
||||
data,
|
||||
isObject(option)
|
||||
? option
|
||||
: {item: option, [name!]: option}
|
||||
)
|
||||
}
|
||||
)}
|
||||
{multipleCount === 1 ? itemRender(option) : null}
|
||||
{multipleCount > 1 ?
|
||||
newOptions[key].map((option: any, index: number) => (
|
||||
<div key={index} style={{
|
||||
width: 100 / multipleCount + '%',
|
||||
height: '100%',
|
||||
float: 'left'
|
||||
}}>
|
||||
{itemRender(option)}
|
||||
</div>
|
||||
)) : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
@ -463,7 +571,7 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`Carousel Carousel--${controlsTheme}`, className)}
|
||||
className={cx(`Carousel Carousel--${controlsTheme}`, {['Carousel-arrow--always']: !!alwaysShowArrow}, className)}
|
||||
style={carouselStyles}
|
||||
>
|
||||
{body ? body : placeholder}
|
||||
@ -471,12 +579,16 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
{dots ? this.renderDots() : null}
|
||||
{arrows ? (
|
||||
<div className={cx('Carousel-leftArrow')} onClick={this.prev}>
|
||||
<Icon icon="left-arrow" className="icon" />
|
||||
{icons && icons.prev
|
||||
? React.isValidElement(icons.prev) ? icons.prev : render('arrow-prev', icons.prev)
|
||||
: (<Icon icon="left-arrow" className="icon" />)}
|
||||
</div>
|
||||
) : null}
|
||||
{arrows ? (
|
||||
<div className={cx('Carousel-rightArrow')} onClick={this.next}>
|
||||
<Icon icon="right-arrow" className="icon" />
|
||||
{icons && icons.next
|
||||
? React.isValidElement(icons.next) ? icons.next : render('arrow-next', icons.next)
|
||||
: (<Icon icon="right-arrow" className="icon" />)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user