天猫购物网站最显眼的就是轮播图了。我在学习一样新js库,一个新框架或新的编程思想的时候,总是感叹“入门必做选项卡,进阶须撸轮播图。”作为一个React组件,它是状态操控行为的典型,拿来练手是个不错的选择。

为了复习,这次就尝试用原生的javascript+React来完成。


轮播图原生实现

所谓轮播图其实是扩展版的选项卡。

先布局

主干架构

  1. <div id="tabs">
  2. <ul id="btns">
  3. <li><a href="javascript:;"></a></li>
  4. <li><a href="javascript:;"></a></li>
  5. <li><a href="javascript:;"></a></li>
  6. <li><a href="javascript:;"></a></li>
  7. <li><a href="javascript:;"></a></li>
  8. <li><a href="javascript:;"></a></li>
  9. </ul>
  10. <ul id="imgs">
  11. <li><img src="images/banner1.jpg"></li>
  12. <li><img src="images/banner2.jpg"></li>
  13. <li><img src="images/banner3.jpg"></li>
  14. <li><img src="images/banner4.jpg"></li>
  15. <li><img src="images/banner5.jpg"></li>
  16. <li><img src="images/banner6.jpg"></li>
  17. </ul>
  18. </div>

样式如下

  1. /*css-reset*/
  2. *{
  3. margin:0;
  4. padding: 0;
  5. }
  6. ul li{
  7. list-style: none;
  8. }
  9. img{
  10. border: none;
  11. }
  12. a{
  13. text-decoration: none;
  14. }
  15. /******************/
  16. #tabs{
  17. width: 1130px;
  18. height: 500px;
  19. margin: 100px auto;
  20. position: relative;
  21. overflow: hidden;
  22. }
  23. #tabs li{
  24. float: left;
  25. }
  26. #tabs img{
  27. width: 1130px;
  28. height: 500px;
  29. }
  30. #btns{
  31. position: absolute;
  32. top:88%;
  33. left:395px;
  34. z-index: 9;
  35. }
  36. #btns a{
  37. display: block;
  38. width: 17px;
  39. height: 17px;
  40. background: rgba(0,0,0,0.3);
  41. border-radius: 50%;
  42. border: 2px solid rgba(0,0,0,0.3);
  43. }
  44. #btns li{
  45. margin: 10px;
  46. }

大概效果

纯javascript实现

事件

一个简单的轮播图包括多个事件。

  • 鼠标移入移出:当鼠标移出,或者是鼠标不在轮播图上面,执行自动播放
  • 当鼠标移入:不再自动播放,而且点击按钮会执行跳转到相应的页面。

渐变

因为6张图不是很多。所以考虑六张图全部做绝对定位,按照顺序叠加在一起。然再通过一个透明度的运动框架,实现之。

在此我选用这个运动框架:

  1. function getStyle(obj,attr){
  2. if(obj.crrentStyle){
  3. return obj.currentStyle[attr];
  4. //兼容IE8以下
  5. }else{
  6. return getComputedStyle(obj,false)[attr];
  7. //参数false已废。照用就好
  8. }
  9. }
  10. function startMove(obj,json,fn){
  11. //清理定时器
  12. if(obj.timer){
  13. clearInterval(obj.timer);
  14. }
  15. obj.timer=setInterval(function(){
  16. var bStop=false;//如果为false就停了定时器!
  17. var iCur=0;
  18. // 处理属性值
  19. for(var attr in json){
  20. if(attr=='opacity'){
  21. iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
  22. }else{
  23. iCur=parseInt(getStyle(obj,attr));
  24. }
  25. //定义速度值
  26. var iSpeed=(json[attr]-iCur)/8;
  27. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
  28. //检测停止:如果我发现某个值不等于目标点bStop就不能为true。
  29. if(iCur!==json[attr]){
  30. bStop=false;
  31. }
  32. if(attr=='opacity'){
  33. obj.style[attr]=(iCur+iSpeed)/100;
  34. obj.style.filter='alpha(opacity:'+(iCur+iSpeed)+')';
  35. }else{
  36. obj.style[attr]=iCur+iSpeed+'px';
  37. }
  38. }
  39. //检测是否停止,是的话关掉定时器
  40. if(bStop===true){
  41. if(iCur==json[attr]){
  42. clearInterval(obj.timer);
  43. if(fn){
  44. fn();
  45. }
  46. }
  47. }
  48. },30);
  49. }

这个框架可以指定样式值进行渐变。

不得不说,这确实是一个很棒的运动框架。可以把它单独放在为一个名为move.js的文件中再引入。

根据这个思路写出原生的代码:

  1. window.onload=function(){
  2. var oTab=document.getElementById('tabs');
  3. var oBtns=document.getElementById('btns');
  4. var aBtns=document.getElementsByTagName('a');
  5. var oImgs=document.getElementById('imgs');
  6. var aImgsLi=oImgs.getElementsByTagName('li');
  7. var bCheck=true;
  8. var iNow=0;
  9. // 以下是初始化设置:
  10. aBtns[0].style.background='rgba(255,255,255,0.5)';
  11. aImgsLi[0].style.zIndex=6;
  12. function iNowlistener(){//改变的核心函数
  13. // 初始化
  14. for(var i=0;i<aBtns.length;i++){
  15. aBtns[i].style.background='rgba(0,0,0,0.3)';
  16. }
  17. aBtns[iNow].style.background='rgba(255,255,255,0.5)';
  18. for(var j=0;j<aBtns.length;j++){
  19. aImgsLi[j].style.opacity=0;
  20. if(j!==iNow){
  21. aImgsLi[j].style.display='none';
  22. }else{
  23. aImgsLi[j].style.display='block';
  24. startMove(aImgsLi[j],{'opacity':100});
  25. }
  26. }
  27. }
  28. var timer=null;
  29. timer=setInterval(function(){
  30. if(bCheck){
  31. if(iNow==5){//将最后一个变为0
  32. iNow=0;
  33. }else{
  34. iNow++;
  35. }
  36. iNowlistener();
  37. }else{
  38. return false;
  39. }
  40. },2000);
  41. oTab.onmouseover=function(){
  42. bCheck=false;
  43. for(var i=0;i<aBtns.length;i++){
  44. aBtns[i].index=i;
  45. aBtns[i].onmouseover=function(){
  46. if(this.index==iNow){
  47. return false;
  48. }else{
  49. iNow=this.index;
  50. iNowlistener();
  51. }
  52. };
  53. }
  54. };
  55. oTab.onmouseout=function(){
  56. bCheck=true;
  57. };
  58. };

效果如下:



不得不说,原生的代码写起来好长好长。

很长吗?后面的更长。


React思路

以上原生代码已经经过了初步的封装——比如INowListener。但是在React的价值观来说,显然还需要进一步的封装。甚至重新拆分。最理想的情况是:顶层组件作为主干架构和状态机。下层组件接收状态并运行方法。

多少个组件?

在这个轮播图中,就三个组件。

  1. - Tabs
  2. -imgs
  3. -btns
  1. var Tabs=React.createClass({
  2. render:function(){
  3. return (
  4. <div id="tabs">
  5. <Btns/>
  6. <Imgs/>
  7. </div>
  8. );
  9. }
  10. });
  11. var Btns=React.createClass({
  12. render:function(){
  13. var arr=[];
  14. for(var i=0;i<6;i++){
  15. var btnContent=
  16. <li key={i.toString()}><a href="javascript:;"></a></li>
  17. arr.push(btnContent);
  18. }
  19. return (
  20. <ul id="btns">{arr}</ul>
  21. )
  22. }
  23. });
  24. var Imgs=React.createClass({
  25. render:function(){
  26. var arr=[];
  27. for(var i=0;i<6;i++){
  28. var imgContent=
  29. <li key={i.toString()}><img src={"images/banner"+(i+1)+".jpg"}/></li>
  30. arr.push(imgContent);
  31. console.log(arr)
  32. }
  33. return (
  34. <ul id="imgs">{arr}</ul>
  35. );
  36. }
  37. });
  38. ReactDOM.render(
  39. <Tabs/>,
  40. document.getElementById('example')
  41. )

这样写就把样式写出来了。

哪个是状态?

iNOW是状态。而且是最重要的状态!既然这样,就考虑把状态iNow放顶层。

鼠标悬停看起来也是状态,但悬停按钮上,触发iNow改变——因此还是iNow。

鼠标移入移出事件,应该是状态。但是这个移入移出的状态依赖于iNow。所以不能单独用。

需要哪些props?

构造组件时,为了灵活性,一般都不考虑把组件框架写死。比如图片张数,id名,等等都应该是props。但是这些暂时来说,都是次要的。

状态肯定是一个核心props,此外,底层设置状态的回调也是核心的props之一。

空谈太多无意义,接下来尝试实现!


自动按钮

现在先不考虑其它,单看按钮。

在插入文档之后,开启一个定时器,每隔2000ms执行一次状态更新。

setState的写法

那涉及到了iNow状态根据前一个状态更新,官方文档不建议这种写法:

  1. this.setState({
  2. return {
  3. iNow:this.state.iNow+1
  4. }
  5. })

因为状态更新可能是异步的。这样写很容易出问题。

事实上,官网提供了这样的写法:

  1. this.setState(function(prev,props){
  2. return {
  3. iNow:prev.iNow+1
  4. }
  5. })

在这里只用第一个参数就够了。

想当然的按钮

定时器应该是一个状态计算器。

所以按钮可以这么写:

  1. var Btns=React.createClass({
  2. getInitialState:function(){
  3. return ({
  4. iNow:0
  5. })
  6. },
  7. componentDidMount:function(){
  8. var _this=this;
  9. setInterval(function(){
  10. _this.setState(function(prev){
  11. //console.log(prev)
  12. if(prev.iNow==5){
  13. return {
  14. iNow:0
  15. };
  16. }else{
  17. return {
  18. iNow:prev.iNow+1
  19. };
  20. }
  21. })
  22. },2000);
  23. },
  24. render:function(){
  25. var arr=[];
  26. for(var i=0;i<6;i++){
  27. var btnContent=null;
  28. if(i==this.state.iNow){
  29. btnContent=
  30. <li key={i.toString()}><a style={{background:'rgba(255,255,255,0.5)'}} href="javascript:;"></a></li>
  31. }else{
  32. btnContent=
  33. <li key={i.toString()}><a href="javascript:;"></a></li>
  34. }
  35. arr.push(btnContent);
  36. }
  37. return (
  38. <ul id="btns">{arr}</ul>
  39. );
  40. }
  41. });

按钮就实现了。

看起来不错,但是这样写可能在未来造成极大的不便。

悬停交互

再强调一次价值观这个概念,按照React的价值观,状态应该从顶层传下去,况且在这个案例中,顶层Tabs组件做一件事就够了:状态机,在Btn组件插入到文档之后,打开这个定时器。底层组件比如Btns根据状态每隔2000ms通过props刷新变化。

同时,我还要实现一个简单的交互功能:当鼠标悬停在Tabs上时,不再允许iNow自动更新。——可以做一个bCheck开关,当Tabs组件鼠标移入/移出时,触发bCheck的来回变化。

此处可能有个小问题,就是鼠标一道按钮组上时,会造成bCheck抖动。但是最后又变回false。所以认为不影响。

很自然想到,bCheck为false时,关闭定时器。但是这样做又等于浪费了定时器的功能,回调方法中一旦关掉定时器,再重新定时器就不是一般的麻烦了,为什么不直接在定时器做判断呢?所以我认为不应该让定时器停下来。只需要改变定时器计算iNow的行为就行了。

  1. var Tabs=React.createClass({
  2. getInitialState:function(){
  3. return {
  4. iNow:0,
  5. bCheck:true//为false时不允许定时器计算更新iNow
  6. }
  7. },
  8. setInow:function(){
  9. var _this=this;
  10. var timer=setInterval(function(){
  11. if(_this.state.bCheck){
  12. //console.log(_this.state.bCheck)
  13. _this.setState(function(prev){
  14. if(prev.iNow==5){
  15. return {
  16. iNow:0
  17. };
  18. }else{
  19. return {
  20. iNow:prev.iNow+1
  21. };
  22. }
  23. });
  24. }else{
  25. console.log('该停了!')
  26. return false;
  27. }
  28. },2000);
  29. },
  30. checkSwitch:function(){
  31. this.setState(function(prev){
  32. return {
  33. bCheck:!prev.bCheck,
  34. }
  35. })
  36. },
  37. render:function(){
  38. return (
  39. <div id="tabs" onMouseOver={this.checkSwitch} onMouseOut={this.checkSwitch}>
  40. <Btns iNow={this.state.iNow} setInow={this.setInow}/>
  41. <Imgs/>
  42. </div>
  43. );
  44. }
  45. });
  46. var Btns=React.createClass({
  47. componentDidMount:function(){
  48. this.props.setInow();//插入后就执行回调方法
  49. },
  50. render:function(){
  51. var arr=[];
  52. for(var i=0;i<6;i++){
  53. var btnContent=null;
  54. if(i==this.props.iNow){
  55. btnContent=
  56. <li key={i.toString()}>
  57. <a style={{background:'rgba(255,255,255,0.5)'}} href="javascript:;"></a>
  58. </li>
  59. }else{
  60. btnContent=
  61. <li key={i.toString()}><a href="javascript:;"></a></li>
  62. }
  63. arr.push(btnContent);
  64. }
  65. return (
  66. <ul id="btns">{arr}</ul>
  67. );
  68. }
  69. });

图片动画

一件事三个步骤

图片组件虽说只是做一件事情(根据iNow渲染效果),但是也得分三步来做。

  • 首先,渲染前应该保证索引值非iNow的所有图片display为none。索引值为iNow的图片透明度为0。(初始化)

  • 其次,在首次插入文档完毕之后(componentDidMount),对第0张图执行startMove函数。

  • 第三,需要一个监听顶层iNow的方法。定时器已经给Btns组件用了,再用就会出错。

    留意到Imgs组件实际上只接受一个会变化的props那就是iNow。因此采用componentWillReceiveProps

生命周期方法

componentWillReceiveProps

组件接收到新的props时调用,并将其作为参数nextProps使用,此时可以更改组件propsstate

  1. componentWillReceiveProps: function(nextProps) {
  2. if (nextProps.bool) {
  3. this.setState({
  4. bool: true
  5. });
  6. }
  7. }

这里采用的两个组件周期方法都是组件真实存在时的方法。所以可以直接使用真实的DOM命令。

实现

  1. var Tabs=React.createClass({
  2. getInitialState:function(){
  3. return {
  4. iNow:0,
  5. bCheck:true
  6. };
  7. },
  8. setInow:function(){
  9. var _this=this;
  10. var timer=setInterval(function(){
  11. if(_this.state.bCheck){
  12. //console.log(_this.state.bCheck)
  13. _this.setState(function(prev){
  14. if(prev.iNow==5){
  15. return {
  16. iNow:0
  17. };
  18. }else{
  19. return {
  20. iNow:prev.iNow+1
  21. };
  22. }
  23. });
  24. }else{
  25. console.log('该停了!')
  26. return false;
  27. }
  28. },2000);
  29. },
  30. checkSwitch:function(){
  31. console.log(this.state.bCheck)
  32. this.setState(function(prev){
  33. return {
  34. bCheck:!prev.bCheck
  35. };
  36. });
  37. },
  38. render:function(){
  39. return (
  40. <div id="tabs"
  41. onMouseOver={this.checkSwitch}
  42. onMouseOut={this.checkSwitch}>
  43. <Btns iNow={this.state.iNow}
  44. setInow={this.setInow} />
  45. <Imgs iNow={this.state.iNow}/>
  46. </div>
  47. );
  48. }
  49. });
  50. var Btns=React.createClass({
  51. componentDidMount:function(){
  52. this.props.setInow();
  53. },
  54. render:function(){
  55. var arr=[];
  56. for(var i=0;i<6;i++){
  57. var btnsContent=null;
  58. if(i==this.props.iNow){
  59. btnsContent=
  60. <li key={i.toString()}>
  61. <a style={{background:'rgba(255,255,255,0.5)'}} href="javascript:;"></a>
  62. </li>
  63. }else{
  64. btnsContent=
  65. <li key={i.toString()}>
  66. <a href="javascript:;"></a>
  67. </li>
  68. }
  69. arr.push(btnsContent);
  70. }
  71. return (
  72. <ul id="btns">{arr}</ul>
  73. );
  74. }
  75. });
  76. var Imgs=React.createClass({
  77. componentDidMount:function(){//刚开始加载时,就执行动画函数
  78. var iNow=this.props.iNow;
  79. var obj=document.getElementById('imgs').getElementsByTagName('li')[iNow].childNodes[0];
  80. startMove(obj,{'opacity':100});
  81. },
  82. componentWillReceiveProps:function(nextProps){
  83. var obj=document.getElementById('imgs').getElementsByTagName('li')[nextProps.iNow].childNodes[0];
  84. //console.log(obj)
  85. startMove(obj,{'opacity':100});
  86. },
  87. // this.startMove:startMove(),
  88. render:function(){
  89. var arr=[];
  90. for(var i=0;i<6;i++){
  91. var imgsContent=null
  92. if(i==this.props.iNow){
  93. imgsContent=
  94. <li key={i.toString()}>
  95. <img style={{opacity:'0'}} src={'images/banner'+(i+1)+'.jpg'} />
  96. </li>
  97. arr.push(imgsContent);
  98. }else{
  99. imgsContent=
  100. <li key={i.toString()}>
  101. <img style={{display:'none'}} src={'images/banner'+(i+1)+'.jpg'} />
  102. </li>
  103. arr.push(imgsContent);
  104. }
  105. }
  106. return (
  107. <ul id="imgs">{arr}</ul>
  108. )
  109. }
  110. })
  111. ReactDOM.render(
  112. <Tabs/>,
  113. document.getElementById('example')
  114. );

看起来Imgs组件已经很完备了。——就它的功能来说已经没有什么需要添加了。


鼠标悬停改变iNow

这个事件只能在底层组件Btns上实现。所以要拿到悬停的索引值。

然后通过回调,把该按钮的索引值设置为整个组件Tabs的状态iNow。

为了干这两件事,还是用一个changeInow(e)函数来包装它们。

给谁绑定?加什么事件?

为了忠实原来的代码。我给a标签加onMouseOver事件。

加了事件直接,秉承这React的核心价值观(一个组件只干一件事),我把get到的index值通过this.props.setInow传递回去。只要顶层的iNow变了,下面的组件不管什么状态,都会乖乖听话了。

如何获取当前悬停的索引值?

在Jquery很容易使用index方法来获取索引值。但是在原生方法中,还得费一番周章。

给所有a绑定一个onMouseOver事件,假设该事件方法的参数为e,那么e.target就是该参数的方法。

这需要写一个getIndex方法

  1. ...
  2. getIndex:function(e){
  3. var list=e.target.parentNode.parentNode.childNodes;
  4. for(var i=0;i<list.length;i++){
  5. if(list[i]===e.target.parentNode){
  6. return i;
  7. }
  8. }
  9. },
  10. ...

拿到索引值之后

——就把它设置为顶层的iNow。

既然决定通过this.props.setInow回调,那么还得传一个索引值参数,回到顶层稍微修改下方法,就实现了。

全部代码:

  1. var Tabs=React.createClass({//顶层组件
  2. getInitialState:function(){
  3. return {
  4. iNow:0,
  5. bCheck:true
  6. };
  7. },
  8. setInow:function(index){//核心状态计算工具:依赖定时器进行实时刷新
  9. if(index!==undefined){//如果参数有内容。
  10. this.setState({
  11. iNow:index
  12. });
  13. }else{
  14. var _this=this;
  15. this.timer=setInterval(function(){
  16. if(_this.state.bCheck){
  17. //console.log(_this.state.bCheck)
  18. _this.setState(function(prev){
  19. if(prev.iNow==5){
  20. return {
  21. iNow:0
  22. };
  23. }else{
  24. return {
  25. iNow:prev.iNow+1
  26. };
  27. }
  28. });
  29. }else{
  30. //console.log('该停了!')
  31. return false;
  32. }
  33. },2000);
  34. }
  35. },
  36. checkSwitch:function(){
  37. //console.log(this.state.bCheck)
  38. this.setState(function(prev){
  39. return {
  40. bCheck:!prev.bCheck
  41. };
  42. });
  43. },
  44. render:function(){
  45. return (
  46. <div id="tabs"
  47. onMouseOver={this.checkSwitch}
  48. onMouseOut={this.checkSwitch}>
  49. <Btns iNow={this.state.iNow}
  50. setInow={this.setInow} />
  51. <Imgs iNow={this.state.iNow}/>
  52. </div>
  53. );
  54. }
  55. });
  56. var Btns=React.createClass({
  57. componentDidMount:function(){
  58. this.props.setInow();
  59. },
  60. getIndex:function(e){//获取a的父级索引值
  61. var list=e.target.parentNode.parentNode.childNodes;
  62. for(var i=0;i<list.length;i++){
  63. if(list[i]===e.target.parentNode){
  64. return i;
  65. }
  66. }
  67. },
  68. changeInow:function(e){//回调方法
  69. //console.log($(e.target).parent().index());
  70. //console.log(this.getIndex(e));
  71. var index=this.getIndex(e);
  72. this.props.setInow(index)
  73. },
  74. render:function(){
  75. var arr=[];
  76. for(var i=0;i<6;i++){
  77. var btnsContent=null;
  78. var index=i;
  79. if(i==this.props.iNow){
  80. btnsContent=
  81. <li key={i.toString()}>
  82. <a onMouseOver={this.changeInow} style={{background:'rgba(255,255,255,0.5)'}} href="javascript:;"></a>
  83. </li>
  84. }else{
  85. btnsContent=
  86. <li key={i.toString()}>
  87. <a onMouseOver={this.changeInow} href="javascript:;"></a>
  88. </li>
  89. }
  90. arr.push(btnsContent);
  91. }
  92. return (
  93. <ul id="btns">{arr}</ul>
  94. );
  95. }
  96. });
  97. var Imgs=React.createClass({
  98. componentDidMount:function(){//刚开始加载时,就执行动画函数
  99. var iNow=this.props.iNow;
  100. var obj=document.getElementById('imgs').getElementsByTagName('li')[iNow].childNodes[0];
  101. startMove(obj,{'opacity':100});
  102. },
  103. componentWillReceiveProps:function(nextProps){
  104. var obj=document.getElementById('imgs').getElementsByTagName('li')[nextProps.iNow].childNodes[0];
  105. //console.log(obj)
  106. startMove(obj,{'opacity':100});
  107. },
  108. // this.startMove:startMove(),
  109. render:function(){
  110. var arr=[];
  111. for(var i=0;i<6;i++){
  112. var imgsContent=null
  113. if(i==this.props.iNow){
  114. imgsContent=
  115. <li key={i.toString()}>
  116. <img style={{opacity:'0'}} src={'images/banner'+(i+1)+'.jpg'} />
  117. </li>
  118. arr.push(imgsContent);
  119. }else{
  120. imgsContent=
  121. <li key={i.toString()}>
  122. <img style={{display:'none'}} src={'images/banner'+(i+1)+'.jpg'} />
  123. </li>
  124. arr.push(imgsContent);
  125. }
  126. }
  127. return (
  128. <ul id="imgs">{arr}</ul>
  129. )
  130. }
  131. })
  132. ReactDOM.render(
  133. <Tabs/>,
  134. document.getElementById('example')
  135. );

完善

我们要让这个组件可复用,换言之就是把之前写死的东西比如图片数量,样式,id名都变成Tabs的props属性。

这下工作量够了吧。原来50多行的东西改写完之后大概150多行。

  1. var Tabs=React.createClass({//顶层组件
  2. getInitialState:function(){
  3. return {
  4. iNow:0,
  5. bCheck:true
  6. };
  7. },
  8. setInow:function(index){//核心状态计算工具:依赖定时器进行实时刷新
  9. if(index!==undefined){//如果参数有内容。
  10. this.setState({
  11. iNow:index
  12. });
  13. }else{
  14. var _this=this;
  15. this.timer=setInterval(function(){
  16. if(_this.state.bCheck){
  17. //console.log(_this.state.bCheck)
  18. _this.setState(function(prev){
  19. if(prev.iNow==this.props.nums-1){
  20. return {
  21. iNow:0
  22. };
  23. }else{
  24. return {
  25. iNow:prev.iNow+1
  26. };
  27. }
  28. });
  29. }else{
  30. //console.log('该停了!')
  31. return false;
  32. }
  33. },this.props.timer);
  34. }
  35. },
  36. checkSwitch:function(){
  37. //console.log(this.state.bCheck)
  38. this.setState(function(prev){
  39. return {
  40. bCheck:!prev.bCheck
  41. };
  42. });
  43. },
  44. render:function(){
  45. return (
  46. <div id={this.props.idNames.main}
  47. onMouseOver={this.checkSwitch}
  48. onMouseOut={this.checkSwitch}>
  49. <Btns iNow={this.state.iNow}
  50. setInow={this.setInow}
  51. nums={this.props.nums}
  52. idNames={this.props.idNames} />
  53. <Imgs iNow={this.state.iNow}
  54. nums={this.props.nums}
  55. idNames={this.props.idNames}
  56. imgType={this.props.imgType} />
  57. </div>
  58. );
  59. }
  60. });
  61. var Btns=React.createClass({
  62. componentDidMount:function(){
  63. this.props.setInow();
  64. },
  65. getIndex:function(e){//获取a的父级索引值
  66. var list=e.target.parentNode.parentNode.childNodes;
  67. for(var i=0;i<list.length;i++){
  68. if(list[i]===e.target.parentNode){
  69. return i;
  70. }
  71. }
  72. },
  73. changeInow:function(e){//回调方法
  74. //console.log($(e.target).parent().index());
  75. //console.log(this.getIndex(e));
  76. var index=this.getIndex(e);
  77. this.props.setInow(index)
  78. },
  79. render:function(){
  80. var arr=[];
  81. for(var i=0;i<this.props.nums;i++){
  82. var btnsContent=null;
  83. var index=i;
  84. if(i==this.props.iNow){
  85. btnsContent=
  86. <li key={i.toString()}>
  87. <a onMouseOver={this.changeInow} id={this.props.idNames.active} href="javascript:;"></a>
  88. </li>
  89. }else{
  90. btnsContent=
  91. <li key={i.toString()}>
  92. <a onMouseOver={this.changeInow} href="javascript:;"></a>
  93. </li>
  94. }
  95. arr.push(btnsContent);
  96. }
  97. return (
  98. <ul id={this.props.idNames.btns}>{arr}</ul>
  99. );
  100. }
  101. });
  102. var Imgs=React.createClass({
  103. componentDidMount:function(){//刚开始加载时,就执行动画函数
  104. var iNow=this.props.iNow;
  105. var obj=document.getElementById(this.props.idNames.imgs).getElementsByTagName('li')[iNow].childNodes[0];
  106. startMove(obj,{'opacity':100});
  107. },
  108. componentWillReceiveProps:function(nextProps){//每当收到新的props就执行动画
  109. var obj=document.getElementById(this.props.idNames.imgs).getElementsByTagName('li')[nextProps.iNow].childNodes[0];
  110. //console.log(obj)
  111. startMove(obj,{'opacity':100});
  112. },
  113. render:function(){
  114. var arr=[];
  115. for(var i=0;i<this.props.nums;i++){
  116. var imgsContent=null;
  117. var src=this.props.imgType.url+this.props.imgType.name+(i+1)+'.'+this.props.imgType.type;
  118. if(i==this.props.iNow){
  119. imgsContent=
  120. <li key={i.toString()}>
  121. <img style={{opacity:'0'}} src={src} />
  122. </li>
  123. arr.push(imgsContent);
  124. }else{
  125. imgsContent=
  126. <li key={i.toString()}>
  127. <img style={{display:'none'}} src={src} />
  128. </li>
  129. arr.push(imgsContent);
  130. }
  131. }
  132. return (
  133. <ul id={this.props.idNames.imgs}>{arr}</ul>
  134. )
  135. }
  136. })
  137. ReactDOM.render(
  138. <Tabs
  139. nums={6}
  140. timer={2000}
  141. idNames={
  142. {
  143. main:"tabs",
  144. btns:"btns",
  145. imgs:"imgs",
  146. active:"btn-active"
  147. }
  148. }
  149. imgType={
  150. {
  151. type:"jpg",
  152. url:"images/",
  153. name:"banner"
  154. }
  155. }
  156. />,
  157. document.getElementById('example')
  158. );

其中多设置了一个#btn-active样式。

  1. #btn-active{
  2. background:rgba(255,255,255,0.5)!important;
  3. }

是不是好长好长呢?

demo地址:

http://djtao.top/tabs/

但是这个确实是一个可复用的,而且还是原生js写成的组件。

不得不说,作为一个初学几天的人,写这东西时的时候遭遇好多的坑。但是最后“蓦然回首,那人却在灯火阑珊处”,也有种人生三境界的感悟了!

React视角下的轮播图的更多相关文章

  1. React Native 如何做轮播图 react-native-swiper

    //:仿饿了么github:https://github.com/stoneWeb/elm-react-native 欢迎各位同学加入: React-Native群:397885169 大前端群:54 ...

  2. React 轮播图实现

    接到项目, 用react和material-ui实现轮播图. 搜索了一些方法参考, 不论语言/框架的使用,大体上分为两种思路 超宽列表实现法 在原生JS或者JQuery中,轮播图的实现一般是这样子的 ...

  3. React中使用CSSTransitionGroup插件实现轮播图

    动画效果,是一个页面上必不可少的功能,学习一个新的东西,当然就要学习,如何用新的东西,用它的方法去实现以前的东西啦.今天呢,我就在这里介绍一个试用react-addons-css-transition ...

  4. React Native学习(六)—— 轮播图

    本文基于React Native 0.52 Demo上传到Git了,有需要可以看看,写了新内容会上传的.Git地址 https://github.com/gingerJY/React-Native-D ...

  5. reactjs-swiper react轮播图组件基于swiper

    react轮播图组件基于swiper demo地址:http://reactjs-ui.github.io/reactjs-swiper/simple.html 1. 下载安装 npm install ...

  6. react轮播图----react-slick

    1.安装 npm install react-slick; //安装样式 npm install slick carousel; 再在App.css中引入 @import "~slick-c ...

  7. React Native 之轮播图swiper组件

    注释:swiper组件是第三方组件 所以在使用之前应该先在命令行安装,然后将第三方的模块引入(第三方模块地址:https://github.com/leecade/react-native-swipe ...

  8. React Native布局实践:开发京东client首页(三)——轮播图的实现

    上篇文章中,我们一起构建了京东client的TabBar.在本文中.将继续向大家介绍京东client首页轮播图及其下发功能button的开发方法,如今就让我们開始吧! 1.相关控件调研 眼下在Gith ...

  9. 前端笔记之JavaScript面向对象(四)组件化开发&轮播图|俄罗斯方块实战

    一.组件化开发 1.1组件化概述 页面特效的制作,特别需要HTML.CSS有固定的布局,所以说现在越来越流行组件开发的模式,就是用JS写一个类,当你实例化这个类的时候,页面上的效果布局也能自动完成. ...

随机推荐

  1. ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock

    ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock 通过终端安装程序sudo apt-get install xxx时出错:E: Could not ...

  2. mount常用挂载命令

    挂接命令(mount) 首先,介绍一下挂接(mount)命令的使用方法,mount命令参数非常多,这里主要讲一下今天我们要用到的. 命令格式: mount [-t vfstype] [-o optio ...

  3. Ubuntu 一直掉线 以及连不上网 解决办法

    新装了一个Ubuntu 系统16.04才使用的时候一切顺利 但是过了两三天  就会出现 频繁掉线的情况 于是上网找大神们的解决办法 把文件 /etc/ppp/options 里面的一个数值改大一点 l ...

  4. 常用算法——排序(二)

    简单选择排序法 选择排序(Selection Sort)的基本思想:对n个记录进行扫描,选择最小的记录,将其输出,接着在剩下的n-1个记录中扫描,选择最小的记录将其输出,--不断重复这个过程,直到只剩 ...

  5. spring.net 框架分析(三)ContextRegistry.GetContext()

    我们通过ContextRegistry.GetContext()建立了一个IApplicationContext得实例,那么这个实例具体是怎么建立的了. 我们来分析一下容器实例建立的过程: 我们在配置 ...

  6. [LeetCode] Battleships in a Board 平板上的战船

    Given an 2D board, count how many different battleships are in it. The battleships are represented w ...

  7. [LeetCode] Divide Two Integers 两数相除

    Divide two integers without using multiplication, division and mod operator. If it is overflow, retu ...

  8. Android开发之带你轻松集成友盟统计

    友盟统计是什么呢?为什么要集成他呢? 当我们需要获取自己写的软件的装机量和用户使用信息时,这时我们可以集成友盟统计. 首先到友盟统计中注册账号什么的就不废话了,直接看创建项目: 在个人中心中的管理里面 ...

  9. 【ios开发】UITableViewCell的重用

    移动开发需要解决的一个问题就是资源稀缺的问题.多数情况下是内存问题. 虽然现在的手机都号称大内存,高配置.但是移动app所占用的资源也在跟着不断膨胀, 也是造成内存不足的主要原因. 在前面的例子中,还 ...

  10. re

    Python3正则表达式应用: 目的:获取匹配的字符串 输出:直接输出或是.group() / .group(0) 常用函数: re.compile 可以把正则表达式编译成一个正则表达式对象,这样可以 ...