
# Carousel 轮播图

基于 [react-slick](https://github.com/akiran/react-slick)简单使用

旋转木马，一组轮播的区域。

## 使用场景

- 当有一组平级的内容。
- 当内容空间不足时，可以用走马灯的形式进行收纳，进行轮播展现。
- 常用于一组图片或卡片轮播。


## 案例演示

### 基本用法

---demo
```js
import { Carousel } from 'amos-framework';

const afterChange = (currentIndex) => console.log(currentIndex);

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel afterChange={afterChange}>
      <div className="content-item">content <strong>1</strong></div>
      <div className="content-item">content <strong>2</strong></div>
      <div className="content-item">content <strong>3</strong></div>
      <div className="content-item">content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 自动切换

---demo
```js
import { Carousel } from 'amos-framework';

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel autoplay>
      <div className="content-item">content <strong>1</strong></div>
      <div className="content-item">content <strong>2</strong></div>
      <div className="content-item">content <strong>3</strong></div>
      <div className="content-item">content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 禁用无限循环 infinite

`infinite=true` 时，将会 clone 已有的 item。注意：当 item 个数只有 1 个时，则不会进行 clone

---demo
```js
import { Carousel } from 'amos-framework';

const settings = {
  autoplay: true,
  infinite: true
  // infinite: false
};

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel {...settings}>
      <div className="content-item">content <strong>1</strong></div>
      <div className="content-item">content <strong>2</strong></div>
      <div className="content-item">content <strong>3</strong></div>
      <div className="content-item">content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 渐显

---demo
```js
import { Carousel } from 'amos-framework';

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel effect="fade">
      <div className="content-item">content <strong>1</strong></div>
      <div className="content-item">content <strong>2</strong></div>
      <div className="content-item">content <strong>3</strong></div>
      <div className="content-item">content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 垂直

---demo
```js
import { Carousel } from 'amos-framework';

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel vertical className="my-slider">
      <div>content <strong>1</strong></div>
      <div>content <strong>2</strong></div>
      <div>content <strong>3</strong></div>
      <div>content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 自定义页码

---demo
```js
import { Carousel } from 'amos-framework';

const props = {
  customPaging: function(i) {
    return (
      <a>
        <img src={`${__carousel_images[`item${i+1}`]}`} />
      </a>
    );
  },
  dots: true,
  className: 'slick-slide-custom',
  dotsClass: "slick-dots slick-thumb",
  infinite: true,
  speed: 500,
  slidesToShow: 1,
  slidesToScroll: 1
};

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel {...props}>
      <div>
        <img src={__carousel_images.item1} />
      </div>
      <div>
        <img src={__carousel_images.item2} />
      </div>
      <div>
        <img src={__carousel_images.item3} />
      </div>
      <div>
        <img src={__carousel_images.item4} />
      </div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 自定义dot包裹

---demo
```js
import { Carousel } from 'amos-framework';

const props = {
  dots: true,
  infinite: true,
  speed: 500,
  slidesToShow: 1,
  slidesToScroll: 1,
  containerStyle: { marginBottom: 40 },
  appendDots: dots => (
    <div
      style={{
        backgroundColor: "#ddd",
        borderRadius: "10px",
        padding: "10px",
        bottom: '-40px',
        height: 36
      }}
    >
      <ul style={{ margin: "0px" }}> {dots} </ul>
    </div>
  ),
  customPaging: i => (
    <div
      style={{
        width: "30px",
        color: "blue",
        border: "1px blue solid"
      }}
    >
      {i + 1}
    </div>
  )
};

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel {...props}>
      <div className="content-item">content <strong>1</strong></div>
      <div className="content-item">content <strong>2</strong></div>
      <div className="content-item">content <strong>3</strong></div>
      <div className="content-item">content <strong>4</strong></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 双轮播使用之一组作为nav

本案例时双方均作为对方的 nav 使用。

如果只需要一端控制，则只需要设置一处 `asNavFor` 即可。

---demo
```js
import { Carousel } from 'amos-framework';

const content = [
  <div key="1" className="content-item"><div>content1</div></div>,
  <div key="2" className="content-item"><div>content2</div></div>,
  <div key="3" className="content-item"><div>content3</div></div>,
  <div key="4" className="content-item"><div>content4</div></div>,
  <div key="5" className="content-item"><div>content5</div></div>,
  <div key="6" className="content-item"><div>content6</div></div>,
  <div key="7" className="content-item"><div>content7</div></div>,
  <div key="8" className="content-item"><div>content8</div></div>,
  <div key="9" className="content-item"><div>content9</div></div>,
  <div key="10" className="content-item"><div>content10</div></div>,
  <div key="11" className="content-item"><div>content11</div></div>,
  <div key="12" className="content-item"><div>content12</div></div>
];

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      nav1: null,
      nav2: null
    };
  }

  componentDidMount() {
    // didMount 之后，才能拿到 ref 值
    this.updateNavForRef();
  }

  updateNavForRef(){
    this.setState({
      nav1: this.slider1,
      nav2: this.slider2
    });
  }

  // 注意，此处如果直接设置 asNavFor={this.slider2}, 无法触发 didMount 之后 Carousel 的刷新。
  render() {
    return (
      <div style={{ color: 'white', minHeight: 220 }}>
        <Carousel
          asNavFor={this.state.nav2}
          ref={slider => this.slider1 = slider}
          dots={false}
        >
          {content}
        </Carousel>
        <Carousel
          containerClassName="carousel-nav"
          asNavFor={this.state.nav1}
          ref={slider => this.slider2 = slider}
          slidesToShow={6}
          swipeToSlide
          focusOnSelect
          draggable
          dots={false}
          arrows
        >
          {content}
        </Carousel>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### change 控制案例

`beforeChange` 和 `afterChange` 变化。

---demo
```js
import { Carousel } from 'amos-framework';

const content = [
  <div key="1" className="content-item"><div>content1</div></div>,
  <div key="2" className="content-item"><div>content2</div></div>,
  <div key="3" className="content-item"><div>content3</div></div>,
  <div key="4" className="content-item"><div>content4</div></div>,
  <div key="5" className="content-item"><div>content5</div></div>
];

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeIndex: 0,
      activeIndex2: 0
    };
  }

  beforeChange = (current, next) => this.setState({ activeIndex: next });

  afterChange = (current) => this.setState({ activeIndex2: current });

  render() {
    const props = {
      dots: true,
      infinite: true,
      speed: 1000,
      arrows: true,
      slidesToShow: 1,
      slidesToScroll: 1,
      beforeChange: this.beforeChange,
      afterChange: this.afterChange
    };

    return (
      <div style={{ color: 'white', minHeight: 220 }}>
        <div style={{ color: '#333' }}>
          <p>
            beforeChange => activeIndex: <strong>{this.state.activeIndex}</strong>
          </p>
          <p>
            afterChange => activeIndex: <strong>{this.state.activeIndex2}</strong>
          </p>
        </div>
        <Carousel {...props}>
          {content}
        </Carousel>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend


### 外部控制切换

---demo
```js
import { Carousel, Button } from 'amos-framework';

const content = [
  <div key="1" className="content-item"><div>content1</div></div>,
  <div key="2" className="content-item"><div>content2</div></div>,
  <div key="3" className="content-item"><div>content3</div></div>,
  <div key="4" className="content-item"><div>content4</div></div>,
  <div key="5" className="content-item"><div>content5</div></div>
];

class Demo extends Component {
  render() {
    const props = {
      dots: true,
      infinite: true,
      speed: 1000,
      arrows: true,
      slidesToShow: 1,
      slidesToScroll: 1
    };

    return (
      <div style={{ color: 'white', minHeight: 220 }}>
        <Button onClick={() => this.carouselRef.prev()} style={{ margin: 5 }}>上一页</Button>
        <Button onClick={() => this.carouselRef.next()} style={{ margin: 5 }}>下一页</Button>
        <Carousel {...props} ref={node => this.carouselRef = node}>
          {content}
        </Carousel>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 循环强调选中

---demo
```js
import { Carousel } from 'amos-framework';

const afterChange = (currentIndex) => console.log(currentIndex);

const cofnig = {
  afterChange,
  className: "center-demo",
  centerMode: true,
  infinite: true,
  centerPadding: "60px",
  slidesToShow: 3,
  dots: false,
  arrows: true
};

ReactDOM.render((
  <div style={{ color: 'white', minHeight: '10em' }}>
    <Carousel {...cofnig}>
      <div className="center-item"><div>content1</div></div>
      <div className="center-item"><div>content2</div></div>
      <div className="center-item"><div>content3</div></div>
      <div className="center-item"><div>content4</div></div>
    </Carousel>
  </div>
), _react_runner_);
```
---demoend

### 文字内容翻牌

---demo
```js
import { Carousel, Row, Col } from 'amos-framework';

const content = [
  <span key={1}>一段话内容 <strong>1</strong></span>,
  <span key={2}>一段话内容 <strong>2</strong></span>,
  <span key={3}>一段话内容 <strong>3</strong></span>,
  <span key={4}>一段话内容 <strong>4</strong></span>
];

const h1 = (
  <Carousel containerClassName="text-carousel" dots={false} autoplay>
    {content}
  </Carousel>
);
const v1 = (
  <Carousel containerClassName="text-carousel" vertical dots={false} autoplay>
    {content}
  </Carousel>
);

const mconfig = {
  slidesToShow: 2,
  slidesToScroll: 2,
  dots: false,
  autoplay: true
};

const mh1 = (
  <Carousel containerClassName="text-carousel-m" {...mconfig}>
    {content}
  </Carousel>
);
const mv1 = (
  <Carousel containerClassName="text-carousel-mv" vertical {...mconfig}>
    {content}
  </Carousel>
);

const fast = (speed) => (
  <Carousel containerClassName="text-carousel" dots={false} autoplay autoplaySpeed={speed}>
    {content}
  </Carousel>
);

ReactDOM.render((
  <div>
    <Row>
      <Col span={4}><div>单行文本滚动：</div></Col>
      <Col span={4}>{h1}</Col>
      <Col span={4}>{v1}</Col>
    </Row>
    <Row>
      <Col span={4}><div>多行文本滚动：</div></Col>
      <Col span={4}>{mh1}</Col>
      <Col span={4}>{mv1}</Col>
    </Row>
    <Row>
      <Col span={4}><div>文本快速滚动(300ms, 200ms)：</div></Col>
      <Col span={4}>{fast(300)}</Col>
      <Col span={4}>{fast(100)}</Col>
    </Row>
  </div>
), _react_runner_);
```
---demoend

## API

以下 props 均通过 `Carousel` 组件进行传递。

### Carousel 容器 props

| params | type | default | description |
| --- | --- | --- | --- |
| effect | string | scrollx | 动画效果函数，可取 `scrollx, fade` |
| prefixCls | string | `amos-carousel` | 自定义样式前缀 |
| containerStyle | object | - | 容器自定义内联样式 |
| containerClassName | string | - | 容器自定义样式名 |

### Carousel slick props

| params | type | default | description |
| --- | --- | --- | --- |
| dots | boolean | true | 是否显示面板指示点 |
| vertical | boolean | false | 垂直显示 |
| autoplay | boolean | false | 是否自动切换 |
| easing | string | linear | 动画效果，缓动函数，默认为 linear |
| beforeChange | `function(from, to)` | 无 | 切换面板的回调，索引更改前回调。`(oldIndex, newIndex) => ...` |
| afterChange | `function(current)` | 无 | 切换面板的回调，索引更改后回调。`(currentIndex: number) => ...` |
| style | object | - | slider 自定义样式 |
| accessibility | bool | - | 启用tabbing和箭头键导航 |
| nextArrow | node | - | 下一页， node |
| prevArrow | node | - | 上一页 node |
| pauseOnHover | bool | - | 鼠标悬浮在slider上时，停止自动播放 |
| className | string | - | CSS class for inner slider div |
| adaptiveHeight | bool | - | 自动调整滑块高度 |
| arrows | bool | false | 是否显示箭头 |
| autoplaySpeed | number | - | 每次自动滚动之间的延迟（毫秒）, 默认 3000 |
| centerMode | bool | - | 中心模块着重显示 |
| centerPadding | `string or any` | - | 中心模块的边距 '50px' |
| cssEase | `string or any` | - | css 缓动函数，默认为 linear |
| dotsClass | string | - | dots 的自定义样式名，默认为 slick-dots |
| draggable | bool | false- | 是否可拖动 |
| fade | bool | - | 是否启动 fade，如果设置 effect='fade',则默认 `fade=true` |
| focusOnSelect | bool | - | 选中时自动聚焦 |
| infinite | bool | - | 无限环绕内容 |
| initialSlide | number | - | 初始化的 slide 索引 |
| lazyLoad | bool | - | 按需或逐步加载图像或渲染组件 |
| rtl | bool | - | 反转 slide 顺序 |
| slide | string | - | 滑动容器类型, 默认为 div |
| slidesToShow | number | - | 一帧要显示多少张 slide。注意：如果 `slidesToShow` 值小于 `itmes` 总数时，此时 `infinite` 不能设置为 true |
| slidesToScroll | number | - | 单次滚动的 slide 数量 |
| speed | number | 500 | 动画执行速度，默认为 500，毫秒 |
| swipe | bool | - | 启用/禁用滑动以更改 slide |
| swipeToSlide | bool | - | 启用拖动/滑动，不考虑 `slidesToScroll` |
| touchMove | bool | - | 是否启用触摸移动 |
| touchThreshold | number | 5 | 触摸移动的阈值，默认为 5 |
| variableWidth | bool | - | 启用自定义宽度 |
| useCSS | bool | - | 启用/禁用 CSS 过渡 |
| slickGoTo | number | - | 跳转到指定的索引，如果 `dontAnimate=true`, 则下跳转的过程中无动画 |
| pauseOnDotsHover | bool | - | 在 dot 上悬停时阻止自动播放 |
| pauseOnFocus | bool | - | slide 聚焦时，暂停自动播放 |
| rows | number | - | slick 中每个 slide 的行数（启用网格模式） |
| slidesPerRow | number | - | 在网格模式下显示的 slide 数，这对行选项很有用 |

> hack code： slidesToShow=3 则 `infinite={items.length >= 3}`

## 方法

| 名称 | 描述 |
| --- | --- |
| goTo(slideNumber) | 切换到指定面板 |
| next() | 切换到下一面板 |
| prev() | 切换到上一面板 |
| play() | 启动 |
| pause() | 暂停 |

更多参数可参考：[react-slick](https://react-slick.neostack.com/)
