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: 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: width,
height: height,
whiteSpace: 'nowrap',
overflow: 'hidden',
position: 'relative'
};
const imageRowStyle = {
marginLeft: offset,
transition: '.2s'
};
const buttonStyle = {
position: 'absolute',
top: '40%',
bottom: '40%',
width: '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>
);
}
}
实现思路
-
如何显示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%',
width: '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: 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: width,
height: height,
whiteSpace: 'nowrap',
overflow: 'hidden',
position: 'relative'
};
const imageRowStyle = {
marginLeft: offset
};
const buttonStyle = {
position: 'absolute',
top: '40%',
bottom: '40%',
width: '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:站在巨人的肩膀上看世界会清晰)。
如果说方向,当今世人还不清晰准确。那么就大胆往前走吧,对与错并不重要。心中的方向更加重要。

浙公网安备 33010602011771号