1、框架搭建
在该轮播图中,将使用到三个组件:
- ImgSlides // 轮播图的顶层组件,用于状态控制
- ImgFigure // 图片组件,用于接收顶层传来的状态,并显示相应的图片
- ControllerUnit // 控制组件,用于控制图片的显示
首先,将这三个组件简单实现,并编写样式:
ImgSlides 组件
import React, { Component } from 'react';
import '../style/component/imgSlides.css';
class ImgSlides extends Component {
render() {
return (
<div className="slides">
<ControllerUnit />
<ImgFigure />
</div>
);
}
}
export default ImgSlides;
ImgSlides 样式
.slides{
width: 100%;
height: 40vw;
position: relative;
overflow: hidden;
}
ImgFigure 组件
import React, { Component } from 'react';
import '../style/component/imgFigure.css';
class ImgFigure extends Component {
componentDidMount(){
}
render() {
let arr = [];
for(let i = 0; i < 3; i++){
let imgContent =
<img key={i.toString()} src=`${i.jpg}` alt=`${图片i}`/>
arr.push(imgContent);
}
return (
<div className="imgs">{arr}</div>
);
}
}
export default ImgFigure;
ImgFigure 样式
img{
border: none;
width: 100%;
height: 40vw;
transition: opacity 1s;
}
.imgs img{
position: absolute;
top: 0;
}
ControllerUnit 组件
import React, { Component } from 'react';
import '../style/component/controllerUnit.css';
class ControllerUnit extends Component {
render() {
let arr = [];
for(let i = 0; i < 3; i++){
let btnContent =
<span key={i.toString()} ></span>
arr.push(btnContent);
}
return(
<nav className="btns">{arr}</nav>
);
}
}
export default ControllerUnit;
ControllerUnit 样式
.btns{
display: flex;
position: absolute;
bottom: 1rem;
z-index: 9;
left: 50%;
margin-left: -3rem;
}
.btns span{
display: block;
width: 1rem;
height: 1rem;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
border: 2px solid rgba(0, 0, 0, 0.5);
transition: background .5s;
margin: 0.5rem;
}
2、图片处理
考虑到组件的灵活性,我们在组件中不将图片信息写死,而是从外部传入,组件内部通过 props 获得图片信息。
首先编写图片数据集:src/data/imageDatas.json
[
{
"filename":"home1.jpg",
"title":"1",
"desc":"it is 1.jpg"
},
{
"filename":"home2.jpg",
"title":"2",
"desc":"it is 2.jpg"
},
{
"filename":"home3.jpg",
"title":"3",
"desc":"it is 3.jpg"
}
]
接着,编写图片URL处理工具函数:src/util.js(其中图片存放在src/images/x.jpg下)
export function getImageUrl(imageDatasArr){
for(var i = 0; i < imageDatasArr.length; i++){
var singleImageData = imageDatasArr[i];
singleImageData.imageURL = require('./images/' + singleImageData.filename);
imageDatasArr[i] = singleImageData;
}
return imageDatasArr;
}
其中,通过 require 来请求图片。
最后在 App.js 中将图片数据传给 ImgSlides 组件。
import ImgSlides from './component/ImgSlides'
import {getImageUrl} from './util';
// 获得图片数据
var imageDatas = require('./data/imageDatas.json');
// 拼装图片URL
imageDatas = getImageUrl(imageDatas);
class App extends Component {
render() {
return <ImgSlides imageDatas={imageDatas}/>
}
}
3、图片轮播
(1)图片轮播中需要当前照片显示状态,即该那张图片显示,将该状态定义在 ImgSlides 组件的 state 中,通过改变 state 来渲染组件。(ImgSlides 组件代码片段)
class ImgSlides extends Component {
constructor(props){
super(props);
this.state = {
current: 0, // 当前图片
imgsArrangeArr: [ // 图片样式
// { // 由于要是用动画,所以不能使用 display: none
// 注意样式的值为字符串
// visibility:'hidden', // 是否显示,
// opacity: '0', // 透明度
// }
]
}
}
(2)图片状态的改变,也放在 ImgSlides 组件中,这样可以使 ImgFigure 组件只用来显示,不进行图片处理。(ImgSlides 组件代码片段)
setImgArrange(current){
let imgsArrangeArr = [];
for (var i = 0; i < this.props.imageDatas.length; i++) {
if(i === current){ // 如果是当前要显示的图片,
imgsArrangeArr[i] = {
visibility: 'visible',
opacity: '100'
}
} else{
imgsArrangeArr[i] = {visibility:'hidden', opacity: '0'}
}
}
return imgsArrangeArr;
}
(3)设定定时器,来改变 this.state.current 的值。(ImgSlides 组件代码片段)
setCurrent(){
let _this = this;
setInterval(function(){
_this.setState(function(prev){
// 使用循环队列的思想来设置 current 的值
let current = (prev.current + 1) % this.props.imageDatas.length;
let imgsArrangeArr = this.setImgArrange(current);
return {current: current,
imgsArrangeArr: imgsArrangeArr};
});
},5000);
}
(5)图片数据、图片状态传给 ImgFigure 组件(ImgSlides 组件代码片段)
render() {
return (
<div className="slides" >
<ControllerUnit />
<ImgFigure data = {this.props.imageDatas} current={this.state.current} arrange={this.state.imgsArrangeArr}/>
</div>
);
}
(6)ImgFigure 组件中进行图片显示:(ImgFigure 组件代码片段)
import React, { Component } from 'react';
import '../style/component/imgFigure.css';
class ImgFigure extends Component {
render() {
let arr = [];
let img = this.props.data;
for(let i = 0; i < img.length; i++){
let imgContent =
<img key={i.toString()} style={this.props.arrange[i]} src={img[i].imageURL} alt={img[i].desc}/>
arr.push(imgContent);
}
return (
<div className="imgs">{arr}</div>
);
}
}
export default ImgFigure;
4、控制按钮实现
控制按钮实现的功能是:鼠标悬停在哪个按钮上就显示该按钮对应的图片。
(1)由于图片状态是在 ImgSlides 组件中改变的,所以应该在 ImgSlides 组件中定义方法,在 ControllerUnit 组件中调用,来改变 ImgSlides 中图片的状态。(ImgSlides 组件代码片段)
changeCurrent(index){ // index 表示,鼠标悬停在第 index 个按钮上
this.setState(function(prev){
let imgsArrangeArr = this.setImgArrange(index); // 改变图片显示状态
return {current: index, // 改变状态
imgsArrangeArr: imgsArrangeArr}
});
}
(2)将图片信息,悬停改变图片状态方法传给 ControllerUnit 组件。(ControllerUnit 组件代码片段)
render() {
return (
<div className="slides" onMouseOver={this.hoverHandle} onMouseOut={this.hoverHandle}>
<ControllerUnit current={this.state.current} changeCurrent={this.changeCurrent} count={this.props.imageDatas.length} />
<ImgFigure data = {this.props.imageDatas} current={this.state.current} arrange={this.state.imgsArrangeArr}/>
</div>
);
}
(3)在 ControllerUnit 组件中,实现鼠标悬停显示图片功能。由于在鼠标悬停事件发生时,要确定是鼠标悬停在哪个按钮上,因此要将按钮的 index 以参数的形式传递给方法。(ControllerUnit 组件代码片段)
<span key={i.toString()} onMouseOver={this.changeCurrent(i)}></span>
由于事件绑定需要传入的是函数,故 changeCurrent 函数应该返回一个闭包函数。(ControllerUnit 组件代码片段)
changeCurrent(index){
return function(){
// 在闭包函数中调用父组件的方法进行图片状态改变
this.props.changeCurrent(index);
}.bind(this);
}
(4) ControllerUnit 组件完整代码
import React, { Component } from 'react';
import '../style/component/controllerUnit.css';
class ControllerUnit extends Component {
constructor(props) {
super(props);
this.changeCurrent = this.changeCurrent.bind(this);
}
changeCurrent(index){
return function(){
this.props.changeCurrent(index);
}.bind(this);
}
render() {
let arr = [];
for(let i = 0; i < this.props.count; i++){
let btnContent = null;
if(i === this.props.current){
btnContent =
<span key={i.toString()} style={{background: 'rgba(255,255,255,0.5)'}} onMouseOver={this.changeCurrent(i)}></span>
} else{
btnContent =
<span key={i.toString()} onMouseOver={this.changeCurrent(i)}></span>
}
arr.push(btnContent);
}
return(
<nav className="btns">{arr}</nav>
);
}
}
export default ControllerUnit;
5、鼠标悬停在图片上停止轮播
该功能可以看做是一个状态的改变,当鼠标悬停在图片上,关闭定时器,当鼠标移除,启动定时器。但是,将定时器开启关闭比较麻烦。我们需要的是将鼠标悬停在图片上,轮播停止,只要我们不去更改 state.current 就可以将轮播停止。为此,在 ImgSlides 定义状态 (ImgSlides 组件代码片段)
constructor(props){
super(props);
this.state = {
current: 0, // 当前图片
isHover: false, // 鼠标是否悬停到组件上
imgsArrangeArr: []
}
}
鼠标悬停事件处理(ImgSlides 组件代码片段)
hoverHandle(){
this.setState(function(prev){
return {
isHover: !prev.isHover,
}
})
}
事件绑定(ImgSlides 组件代码片段)
render() {
return (
<div className="slides" onMouseOver={this.hoverHandle} onMouseOut={this.hoverHandle}>
<ControllerUnit current={this.state.current} changeCurrent={this.changeCurrent} count={this.props.imageDatas.length} />
<ImgFigure data = {this.props.imageDatas} current={this.state.current} arrange={this.state.imgsArrangeArr}/>
</div>
);
}
停止更新 state.current 值(ImgSlides 组件代码片段)
setCurrent(){
let _this = this;
setInterval(function(){
if(!_this.state.isHover){
// 当鼠标没有悬停在组件上时,更新状态
_this.setState(function(prev){
let current = (prev.current + 1) % this.props.imageDatas.length;
let imgsArrangeArr = this.setImgArrange(current);
return {current: current,
imgsArrangeArr: imgsArrangeArr};
});
} else{
return null;
}
},5000);
}