视频加载失败

React轮播图(下)

2246 字
11 分钟
React轮播图(下)

关于react轮播图的源码,无缝版已上传至github,https://github.com/gaodaxiu0406/React-Slider

用react+webpack写一个的轮播图项目(项目配置请看上篇[React轮播图(上)])#

先将整个文件写在一个文件中

1.画结构#

  • 新建一个components文件夹
  • components文件夹下新建Slider.js文件和Slider.less文件(Slider.less文件是用来给Slider.js写样式的)

2.在indix.js中引入组件 渲染到页面中#

  • 打开src文件夹中的index.html文件
<div id="root"></div>
  • 2-1) 在这个id为root的标签中渲染元素
  • 2-2) 回到index.js中,写一个轮播图组件,渲染到index.html中
import React from 'react';
import ReactDOM from 'react-dom';
import Slider from './components/Slider';
let images=[
{src:require('./images/1.jpg')},
{src:require('./images/2.jpg')},
{src:require('./images/3.jpg')},
{src:require('./images/4.jpg')}
];
ReactDOM.render(<Slider images={images}/>,document.querySelector('#root'));
  • 解释1) 引入React,引入ReactDOM,引入Slider组件 然后通过ReactDOM.render将Slider组件渲染到index.html的id为root的div标签中
  • 解释2) Slider组件需要图片参数
    • src文件夹下新建一个images文件夹,存入轮播的图片
    • 将图片路径通过require存入images数组中
    • 通过组件Slider标签将所需的images参数传入组件Slider(让images参数变量等于images数组)

3.开始写Slider.js中的代码#

  • 需要默认导出一个组件Slider供外面文件(index.js)调用;还需要接收一个images属性进行轮播
import React from 'react';
import ReactDOM from 'react-dom';
require('./Slider.less');
export default class Slider extends React.Component{
render(){
let images=this.props.images;
return(
<div className="slider-wrapper">
<ul className="sliders">
{images.map((image,index)=>(
<li className="slider">
<img src={image.src}/>
</li>))
}
</ul>
</div>
)
}
}
    1. require('./Slider.less'); —> 这里引入Slider.less用import和require都是一样的效果,都是加载一个模块的意思,模块可能是图片,可能是less/css/js/json文件,都可以 —> 在webpack中一切皆模块,不管是什么资源,都可以作为模块来加载。
    1. li的数量取决于images数据,images中有几张图片,就有几个li。
    • 在render中let一个变量images来接收index.js传入的images,然后用map方法遍历整个数组。

4.在Slider.less中写好轮播图的样式#

*{
padding: 0;
margin: 0;
}
ul,li{
list-style: none;
}
.wrapper{
width: 400px;
height: 400px;
position: relative;
margin: 30px auto;
.sliders{
height: 400px;
position: absolute;
left:0;
.slider{
float: left;
width: 400px;
height: 400px;
img{
width: 100%;
height: 100%;
}
}
}
}

宽高为400px的轮播图。

5.写配置文件#

  • 在webpack.config.js中加loaders
module:{
loaders:[
{test:/\.js$/,loader:'babel-loader',exclude:/node_modules/},
{test:/\.less$/,loader:'style-loader!css-loader!less-loader'},
{test:/\.(jpg|png|gif)$/,loader:'url-loader'}
]
}
  • babel默认情况下什么都不做,需要一个配置文件.babelrc文件
    • 新建一个.babelrc配置文件

      {
      "presets": ["es2015","stage-0","react"]
      }
      • presets预设,“es2015”将es6编译成es5,“stage-0”将es7编译成es5,“react”将react编译成es5。
        • 原理1:{test:/\.js$/,loader:'babel-loader',exclude:/node_modules/}—>处理(编译)js文件<如果发现文件是js>,用babel-loader加载,加载的时候需要读配置文件.babelrc,代码es6/es7/react都要通过babel转成es5;同时通过exclude将node_modules文件夹下的所有js文件排除掉。
        • 原理2:{test:/\.less$/,loader:'style-loader!css-loader!less-loader'}—>如果发现文件以.less结尾的,第一步通过less-loader将less编译成css,然后通过css-loader进行加载,然后通过style-loader将他通过style标签的形式插入到页面中去,变成一个行内样式。
        • 原理3:{test:/\.(jpg|png|gif)$/,loader:'url-loader'}—>凡是资源文件都可以用url-loader来加载,不论是图片、图标、字体、视频、音频;后面可以通过问号传参,有个参数limit(例如:{test:/\.(jpg|png|gif)$/,loader:'url-loader?limit=8192'}—>小于8K的资源文件将直接以base64的形式内联在代码中,可以减少一次http请求)。

此时执行npm run build,将代码打包到出口文件中,打开build文件夹下的index.html文件就可以直接预览效果了#

  • 此时会发现控制台有个报错
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Slider`. See https://fb.me/react-warning-keys for more information.
in li (created by Slider)
in Slider

需要唯一的key属性,在Slider.js文件中的li需要唯一的key属性,给li标签加上key属性即可

<li className="slider" key={index}> <img src={image.src}/> </li>

关掉浏览器,重新执行npm run build,再打开build文件夹下的index.html文件预览,控制台就没有报错了

6.写功能#

  • 让图片动起来,需要给Slider.js加个状态,需要有个定时器让他动起来 写在周期函数componentDidMount中
constructor(){
super();
this.state={pos:0};//默认索引
}
componentDidMount(){
this.$timer=setInterval(()=>{
let pos=this.state.pos;
pos++;//每隔2s让pos加1,pos值影响ul的左偏移量left的值 所以ul应该有个style属性 left值应该变化
this.setState({pos:pos})
},this.props.interval*1000)
  • pos++; —> 每隔2s让pos加1,pos值影响ul的左偏移量left的值,所以ul应该有个style属性,left值应该变化

    • ul应该有个style属性

      render(){
      let images=this.props.images;
      let style={
      width:400*images.length,
      left:this.state.pos*-400
      };
      return(
      <div className="slider-wrapper">
      <ul style={style} className="sliders">
      {
      images.map((image,index)=>(
      <li className="slider" key={index}>
      <img src={image.src}/>
      </li>
      ))
      }
      </ul>
      </div>
      )
      }
  • this.props.interval*1000—>每隔2s轮播一次,需要有变量传进来—>在index.js中新增一个interval变量(间隔时间让外面可以控制)

      1. 这里通过this.props获取index.js中传递进来的的interval变量this.props.interval*1000
      1. index.js中新增一个interval变量

        ReactDOM.render(
        <Slider
        images={images}
        interval={2}
        />,document.querySelector('#root'));

写功能步骤总结#

  • 第一步<定义一个默认索引>pos,默认值是0
  • 第二步<在组件加载完成之后创建定时器>setInterval赋给this.$timer
  • 第三步<每隔2s让图片向左偏移一个宽度的距离>(interval是外界传进来的,是图片轮播的间隔时间),在index中要给组件Slider传进来一个2,2*1000意味着2s变一次
  • this.$timer中先取出老的pos值,第一次轮播pos就是0,然后pos++,pos变成1,然后setState重新设置pos值,让pos值往上
  • pos会影响ul的left值,一张图片的宽度是400px,向左偏移400,就是*-400
  • ul的宽度应该是宽度400乘以图片的数量images.length,4张图就是1600px

执行npm run dev#

  • 注意,执行npm run dev可自动将文件编译更新打包到出口文件index.html中,并且只要更改文件,页面就会自动刷新,但是如果更改配置文件,需要重新启动npm run dev服务
  • 此时执行npm run dev命令,轮播图就动起来了,但是越界了,因为此时还没有做边界处理

7.完善功能#

将Slider需要的属性(可外界控制的),在index.js的Slider组件标签中传入

ReactDOM.render(
<Slider
images={images}//图片
interval={2}//多长时间轮播一次
speed={1}//每次轮播的速度
pause={true}//当鼠标移动上去之后自动暂停
autoplay={true}//是否启用自动轮播,false不自动轮播
- 在Slider.js中
dots={true}//是否有点状导航
arrows={true}//是否有箭头导航
/>,document.querySelector('#root'));
  • 添加transitionDuration—>规定完成过渡效果需要花费的时间 speed默认值是1 这里就是1s
let style={
width:400*images.length,
left:this.state.pos*-400,
transitionDuration:this.props.speed+'s'
};
  • 在Slider.js的周期函数中添加一个判断
componentDidMount(){
if(this.props.autoPlay){
this.$timer=setInterval(()=>{
let pos=this.state.pos;
pos++;
this.setState({pos:pos})
},this.props.interval*1000)
}
}
  • this.props.autoPlay 是否自动轮播,如果外界传入true就是自动轮播,传入false就是不自动轮播

8.将轮播切换单独拎出来写成一个方法turn#

turn(n){
let pos=this.state.pos;
pos+=n;
this.setState({pos:pos})
}
  • 或者使用es6的箭头函数
turn=(n)=>{
let pos=this.state.pos;
pos+=n;
this.setState({pos:pos})
}
  • let pos=this.state.pos;获取旧索引
  • turn表示切换,n表示切换的步长,方便以后操作<例如往左走传1进来即可>,往右走传-1进来即可
  • 那么此时在周期函数中,直接调用这个turn方法即可,默认往右轮播,传入-1
componentDidMount(){
if(this.props.autoplay){
this.timer=setInterval(()=>{
this.turn(1);
},this.props.interval*1000)
}
}

9.实现鼠标移上去停止轮播#

  • 给div加onMouseOver和onMouseOut事件
<ul onMouseOver={()=>clearInterval(this.timer)} onMouseOut={this.play} style={style} className="sliders">
...
</ul>

10.自动轮播部分也封装成一个函数play#

play=()=>{
this.timer=setInterval(()=>{
this.turn(1);
},this.props.interval*1000)
};
  • play表示开启定时器进行自动轮播
  • 那么此时在周期函数中直接调用this.play()即可
componentDidMount(){
if(this.props.autoplay){
this.play();
}
}

11.边界判断#

turn=(n)=>{
let pos=this.state.pos;
pos+=n;
if(pos>=this.props.images.length){
pos=0;
}
this.setState({pos:pos})
};
  • 当索引为图片总张数的时候 让索引变为0

12.写左右箭头#

在ul下加一个div

<div className="arrows">
<span className="arrow-left">&lt;</span>
<span className="arrow-right">&gt;</span>
</div>
  • 在Slider.less中写样式
.arrows{
position: absolute;
width: 100%;
height: 20px;
top:50%;
margin-top:-10px;
.arrow{
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
cursor: pointer;
font-size: 30px;
background-color: #eee;
&:hover{
background-color: #999;
}
}
.arrow-left{
margin-left: 5px;
float: left;
}
.arrow-right{
margin-right: 5px;
float: right;
}
}

给左右arrow绑定事件

<div className="arrows">
<span onClick={()=>this.turn(-1)} className="arrow arrow-left">&lt;</span>
<span onClick={()=>this.turn(1)} className="arrow arrow-right">&gt;</span>
</div>

13.处理左边界#

  • 在turn中做个判断
turn=(n)=>{
let pos=this.state.pos;
pos+=n;
if(pos>=this.props.images.length){
pos=0;
}
if(pos<0){
pos=this.props.images.length-1;
}
this.setState({pos:pos})
};

当索引小于0的时候,让索引等于images的长度-1

14.根据传进来的arrows值判断是否有左右箭头切换效果#

  • 第一种方法(看着比较乱)
{
this.props.arrows?<div className="arrows">
<span onClick={()=>this.turn(-1)} className="arrow arrow-left">&lt;</span>
<span onClick={()=>this.turn(1)} className="arrow arrow-right">&gt;</span>
</div>:null
}
  • 第二种,在render中
let arrows=null;
if(this.props.arrows){
arrows=(
<div className="arrows">
<span onClick={()=>this.turn(-1)} className="arrow arrow-left">&lt;</span>
<span onClick={()=>this.turn(1)} className="arrow arrow-right">&gt;</span>
</div>
)
}

那么ul下面就可以直接用{arrows}代替了(看着很清晰明了)

<div className="slider-wrapper">
<ul onMouseOver={()=>clearInterval(this.timer)} onMouseOut={this.play} style={style} className="sliders">
{
images.map((image,index)=>(
<li className="slider" key={index}>
<img src={image.src}/>
</li>
))
}
</ul>
{arrows}
</div>

15.根据传进来的dots值判断是否有点状导航#

let dots=null;
if(this.props.dots){
dots=(
<div className="dots">
{
images.map((image,index)=>(
<span className="dot" key={index}></span>
))
}
</div>
)
}
  • dots直接放到最下面即可
...
{arrows}
{dots}

15-1.在Slider.less中写dots的样式#

.dots{
width: 100%;
height: 20px;
position: absolute;
bottom: 10px;
text-align: center;
.dot{
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
margin-left: 5px;
background-color: #abcdef;
cursor: pointer;
&:hover{
background-color: #999999;
}
}
}

16.点状导航的点击跟随事件#

给span加onClick事件

···
<span className="dot" key={index} onClick={()=>this.turn(index-this.props.pos)}></span>
···

16-1.轮播点状导航自动跟随事件#

在Slider.less中加一个active样式

.active{
background-color: #999999;
}

给span标签添加active属性

<span className={"dot "+(index==this.state.pos?'active':'')} key={index} onClick={()=>this.turn(index-this.state.pos)}></span>

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
React轮播图(下)
https://gaodaxiu-blog.netlify.app/posts/react轮播图项目开写/
作者
GaoDaXiu
发布于
2017-07-31
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
GaoDaXiu
Hello, I'm GaoDaXiu.
公告
欢迎来到我的博客!
分类
标签
最新动态
站点统计
文章
76
分类
20
标签
128
总字数
72,109
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Netlify CI
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录