React 实现简易轮播图

  

 使用 ReactJS 实现一个简易的轮播图 (carousel) 组件。

Task 1:在相框中展示图片,左右按钮切换当前图片

实现思路;把图片横向排列成组(image row),放置在相框(frame)中,隐藏超出相框的部分。利用图片组左侧和相框左侧的距离(margin-left)改变当前展示在相框中的内容,点击左右按钮可以改变这个距离。

// How to make use of this component

<Carousel width={400} height={400}>
  {images.map(image => <img src={image} alt="" key={image}/>)}
</Carousel>

  



// Carousel component

import React, { Component } from 'react';

export default class Carousel extends Component {
  constructor(props) {
    super(props);
    this.state = { currentIndex: 0 };
    this.renderChildren = this.renderChildren.bind(this);
    this.setIndex = this.setIndex.bind(this);
  }

  renderChildren() {
    const { children, width, height } = this.props;
    const childStyle = {
       width,
      height: height
    };

    return React.Children.map(children, child => {
      const childClone = React.cloneElement(child, { style: childStyle });
      return (
        <div
          style={{
            display: 'inline-block'
          }}
        >
          {childClone}
        </div>
      );
    });
  }

  setIndex(index) {
    const len = this.props.children.length;
    const nextIndex = (index + len) % len;

    this.setState({ currentIndex: nextIndex });
  }

  render() {
    const { width, height } = this.props;
    const { currentIndex } = this.state;

    const offset = -currentIndex * width;

    const frameStyle = {
       width,
      height: height,
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      position: 'relative'
    };

    const imageRowStyle = {
      marginLeft: offset,
      transition: '.2s'
    };

    const buttonStyle = {
      position: 'absolute',
      top: '40%',
      bottom: '40%',
       '10%',
      background: 'rgba(0,0,0,0.2)',
      outline: 'none',
      border: 'none'
    };

    const leftButtonStyle = {
      ...buttonStyle,
      left: 0
    };

    const rightButtonStyle = {
      ...buttonStyle,
      right: 0
    };

    return (
      <div className="carousel">
        <div className="frame" style={frameStyle}>
          <button
            onClick={() => this.setIndex(currentIndex - 1)}
            style={leftButtonStyle}
          >
            <
          </button>
          <div style={imageRowStyle}>{this.renderChildren()}</div>
          <button
            onClick={() => this.setIndex(currentIndex + 1)}
            style={rightButtonStyle}
          >
            >
          </button>
        </div>
      </div>
    );
  }
}

  

实现思路

  1. 如何显示block-level的div在同一行

    Solution 1:

    For parent element

white space: nowrap

  For children elements

display: inline block

  

Solution 2:

对同一行的div设置float属性

float:left

  隐藏超出相框的图片部分

overflow: hidden

  通过图片组和相框左侧的距离(margin-left)控制显示在相框中的内容,设置动画时间为0.2秒

const offset = -(currentIndex * width)

const imageRowStyle = {
  marginLeft: offset,
  transition: '.2s'
};

  通过按钮来控制margin-left, 改变当前相框中内容

  setIndex(index) {
    const len = this.props.children.length;
    const nextIndex = (index + len) % len;

    this.setState({ currentIndex: nextIndex });
  }
  
  ...
  // in render function
  const {width, height} = this.props;
  const {currrentIndex} = this.state;
  const offset = - currentIndex * wid
  th

  放置按钮在相框中,设置合适大小,背景半透明,取消边框显示。

    const buttonStyle = {
      position: 'absolute',
      top: '40%',
      bottom: '40%',
       '10%',
      background: 'rgba(0,0,0,0.2)',
      outline: 'none',
      border: 'none'
    };

    const leftButtonStyle = {
      ...buttonStyle,
      left: 0
    };

    const rightButtonStyle = {
      ...buttonStyle,
      right: 0
    };

  

 

 
实现思路市通过改变
 
marginLeft,改变左侧间距,
marginLeft的使用肯定市需要浮动定位和固定定位的结合,还有就是溢出隐藏,

如果想这个过程中有动画效果,一个动火过度,就需要增加
transition: '.2s'

  


 

Task #2 平滑无限循环

在之前的步骤中,我们使用了transiion来实现滑动动画。但当图片从最后一幅去到第一幅或者反之的时候,相框中会经过所有的图片,造成不连贯的体验。为了解决这个问题,我们可以在一头一尾多加上一副图片,当carousel处于最后一幅图片并点击next按钮的时候,首先把下一幅图片移动到相框,在动画结束的时候,再把相框中的图片切换为第一幅图片。

这里需要使用requestAnimationFrame,来保证当动画结束的时候,立即改变component state。



作者:一拾五
链接:https://www.jianshu.com/p/07f36235eb2e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 
 
 
import React, { Component } from 'react';

export default class Carouse extends Component {
  constructor(props) {
    super(props);
    this.state = { currentIndex: 1, offset: -this.props.width };
    this.renderChildren = this.renderChildren.bind(this);
    this.setIndex = this.setIndex.bind(this);
  }

  renderChildren() {
    const { children, width, height } = this.props;
    const childStyle = {
       width,
      height: height
    };

    if (!children) {
      return;
    }

    const appendedChildren = [
      children[children.length - 1],
      ...children,
      children[0]
    ];

    return React.Children.map(appendedChildren, (child, index) => {
      const childClone = React.cloneElement(child, { style: childStyle });

      return (
        <div
          style={{
            display: 'inline-block'
          }}
          key={index}
        >
          {childClone}
        </div>
      );
    });
  }

  setIndex(index) {
    let nextIndex = index;
    const len = this.props.children.length;
    const { width } = this.props;

    this.setState({ currentIndex: nextIndex });

    const currentOffset = this.state.offset;
    const nextOffset = -nextIndex * width;

    let start = null;

    const move = timestamp => {
      if (!start) {
        start = timestamp;
      }

      const progress = timestamp - start;

      this.setState({
        offset: currentOffset + (nextOffset - currentOffset) * progress / 100
      });

      if (progress < 100) {
        requestAnimationFrame(move);
      } else {
        if (nextIndex === 0) {
          nextIndex = len;
        } else if (nextIndex === len + 1) {
          nextIndex = 1;
        }

        this.setState({ currentIndex: nextIndex, offset: -nextIndex * width });
      }
    };

    requestAnimationFrame(move);
  }

  render() {
    const { width, height } = this.props;
    const { currentIndex, offset } = this.state;

    const frameStyle = {
       width,
      height: height,
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      position: 'relative'
    };

    const imageRowStyle = {
      marginLeft: offset
    };

    const buttonStyle = {
      position: 'absolute',
      top: '40%',
      bottom: '40%',
       '10%',
      background: 'rgba(0,0,0,0.2)',
      outline: 'none',
      border: 'none'
    };

    const leftButtonStyle = {
      ...buttonStyle,
      left: 0
    };

    const rightButtonStyle = {
      ...buttonStyle,
      right: 0
    };

    return (
      <div className="carousel">
        <div className="frame" style={frameStyle}>
          <button
            onClick={() => this.setIndex(currentIndex - 1)}
            style={leftButtonStyle}
          >
            <
          </button>
          <div style={imageRowStyle}>{this.renderChildren()}</div>
          <button
            onClick={() => this.setIndex(currentIndex + 1)}
            style={rightButtonStyle}
          >
            >
          </button>
        </div>
      </div>
    );
  }
}

  

 
 
 
 
1、路在何方? 路在脚下 2、何去何从? 每个人都在探索,未来的方向在何处。如果说某些方向是世人已经公认的,那么就先按照公认的去走吧(ps:站在巨人的肩膀上看世界会清晰)。 如果说方向,当今世人还不清晰准确。那么就大胆往前走吧,对与错并不重要。心中的方向更加重要。
原文地址:https://www.cnblogs.com/yuanjili666/p/13565308.html