最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】ReactJS的app中首页轮播图静止不循环滚动切换

ReactJS crifan 3085浏览 0评论

之前的别人的代码,基于:

https://github.com/amio/re-carousel

实现了首页的滚动轮播图,且在模拟器:

Mac的Chrome的开发者工具,模拟iPhone6:

Mac的Safari开发者工具,模拟iPhone:

都是正常的,可以轮播的:

但是仅限于鼠标(模拟触屏)拖动,才可以滚动

不拖动,则始终无法轮播。

即:拖动可以切换图片,但是无法自动轮播。

但是到了手机上,不论是iPhone还是Android,连拖动都不能切换图片,更别说轮播了,更不工作。

需要说明的是:

手机上的是原生的app内部嵌入的webview,打开的首页,不知道内嵌webview是否有影响。

此处相关代码为:

import Carousel from ‘components/carousel’;
      <div class={style.container}>
        <Carousel style={{
          width: ‘7.5rem’,
          height: ‘3rem’
        }}>
          <img src={imgTop01} />
          <img src={imgTop02} />
          <img src={imgTop01} />
          <img src={imgTop02} />
        </Carousel>

/src/components/carousel/index.js

import { h, Component } from ‘preact’;
import ReCarousel from ‘../re-carousel/carousel’;
// see https://github.com/amio/re-carousel
export default class Carousel extends Component {
  render() {
    return (
      <ReCarousel auto loop style={this.props.style}>
        {
          this.props.children.map(child => {
            return (
              <div class=”swiper-slide” data-swiper-autoplay=”2000″>
                {child}
              </div>
            );
          })
        }
      </ReCarousel>
    );
  }
}

src/components/re-carousel

中代码很多:

amio/re-carousel: Minimal carousel component for React.

53个star还凑合。

Reactjs Carousel not work

reactjs – How to use the carousel component from Bootstrap 4 with React.js? – Stack Overflow

jcursoli/react-carousel

leandrowd/react-responsive-carousel: React.js Responsive Carousel (with Swipe)

239个star

demo:

‎react-responsive-carousel.js.org

react-responsive-carousel/index.js at master · leandrowd/react-responsive-carousel

css – bootstrap carousel does not switch between slides when used in reactjs – Stack Overflow

算了,还是基于原先的代码,去调试看看,为何没有自动开始轮播。

调试发现,除了第1,3张图片正常:

第2,4,好像区域都不对啊:

然后发现不能自动轮播的原因:

之前代码写的逻辑错误,componentDidMount中,在调用this.prepareAutoSlide();之前,没有设置this.mounted = true;导致其内部是通过:

if (this.props.loop && this.props.auto && this.mounted) {

判断失败,无法执行自动轮播。

解决办法:

把:this.mounted = true;放到:this.prepareAutoSlide();前面:

  componentDidMount () {
    console.log(`Carousel componentDidMount`);
    //Note: make sure before prepareAutoSlide set mounted to true !!!
    this.mounted = true;
    this.prepareAutoSlide();
    for (let i = 1; i < this.state.frames.length; i++) {
      console.log(`i=${i},this.refMap[i]=${this.refMap[i]}`);
      this.refMap[i].style.opacity = 0;
    }
  }

即可开启自动轮播。

但是现在有个问题:

可以自动轮播,但是触屏无法滑动切换图片。

经过一番折腾,把代码中的EventListener的部分去掉,使用React自带的事件函数:

import { h, Component } from ‘preact’;
import IndicatorDots from ‘./indicator-dots’;
import objectAssign from ‘./objectAssign’;
const styles = {
  wrapper: {
    width: ‘100%’,
    height: ‘100%’,
    overflow: ‘hidden’,
    position: ‘relative’
  },
  frame: {
    width: ‘100%’,
    height: ‘100%’,
    position: ‘absolute’
  }
};
class RefHelper extends Component {
  componentDidMount() {
    const { callback } = this.props;
    callback && callback(this.base);
  }
  render() {
    const { children, style } = this.props;
    return (
      <div style={style}>{children}</div>
    );
  }
}
class Carousel extends Component {
  constructor (props) {
    super(props);
    this.state = {
      frames: [].concat(props.frames || props.children || []),
      current: 0
    };
    this.refMap = {};
    this.mounted = false;
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
    this.autoSlide = this.autoSlide.bind(this);
    // this.prev = this.prev.bind(this);
    // this.next = this.next.bind(this);
    if (props.loop === false && props.auto) {
      console.warn(‘[re-carousel] Auto-slide only works in loop mode.’);
    }
    console.log(`Carousel constructor: props.auto=${props.auto},props.loop=${props.loop},props.style=${JSON.stringify(props.style)}`);
  }
  componentDidMount () {
    console.log(`Carousel componentDidMount`);
    //Note: make sure before prepareAutoSlide set mounted to true !!!
    this.mounted = true;
    this.prepareAutoSlide();
    for (let i = 1; i < this.state.frames.length; i++) {
      console.log(`i=${i},this.refMap[i]=${this.refMap[i]}`);
      this.refMap[i].style.opacity = 0;
    }
  }
  componentWillUnmount () {
    console.log(`Carousel componentWillUnmount`);
    this.clearAutoTimeout();
    this.mounted = false;
  }
  onTouchStart (e) {
    console.log(`Carousel onTouchStart`);
    if (this.state.total < 2) return;
    this.clearAutoTimeout();
    this.updateFrameSize();
    this.prepareSiblingFrames();
    const { pageX, pageY } = (e.touches && e.touches[0]) || e;
    this.setState({
      startX: pageX,
      startY: pageY,
      deltaX: 0,
      deltaY: 0
    });
    // this.base.addEventListener(‘touchmove’, this.onTouchMove, {passive: true});
    // this.base.addEventListener(‘mousemove’, this.onTouchMove, {passive: true});
    // this.base.addEventListener(‘touchend’, this.onTouchEnd, true);
    // this.base.addEventListener(‘mouseup’, this.onTouchEnd, true);
  }
  onTouchMove (e) {
    console.log(`Carousel onTouchMove`);
    if (e.touches && e.touches.length > 1) return;
    this.clearAutoTimeout();
    const { pageX, pageY } = (e.touches && e.touches[0]) || e;
    let deltaX = pageX – this.state.startX;
    let deltaY = pageY – this.state.startY;
    this.setState({
      deltaX,
      deltaY
    });
    // when reach frames edge in non-loop mode, reduce drag effect.
    if (!this.props.loop) {
      if (this.state.current === this.state.frames.length – 1) {
        deltaX < 0 && (deltaX /= 3);
        deltaY < 0 && (deltaY /= 3);
      }
      if (this.state.current === 0) {
        deltaX > 0 && (deltaX /= 3);
        deltaY > 0 && (deltaY /= 3);
      }
    }
    this.moveFramesBy(deltaX, deltaY);
  }
  onTouchEnd () {
    console.log(`Carousel onTouchEnd`);
    const direction = this.decideEndPosition();
    direction && this.transitFramesTowards(direction);
    // // cleanup
    // this.base.removeEventListener(‘touchmove’, this.onTouchMove);
    // this.base.removeEventListener(‘mousemove’, this.onTouchMove);
    // this.base.removeEventListener(‘touchend’, this.onTouchEnd, true);
    // this.base.removeEventListener(‘mouseup’, this.onTouchEnd, true);
    setTimeout(() => this.prepareAutoSlide(), this.props.duration);
  }
  decideEndPosition () {
    console.log(`Carousel decideEndPosition`);
    const { deltaX = 0, deltaY = 0, current, frames } = this.state;
    const { axis, loop, minMove } = this.props;
    switch (axis) {
      case ‘x’:
        if (loop === false) {
          if (current === 0 && deltaX > 0) return ‘origin’;
          if (current === frames.length – 1 && deltaX < 0) return ‘origin’;
        }
        if (Math.abs(deltaX) < minMove) return ‘origin’;
        return deltaX > 0 ? ‘right’ : ‘left’;
      case ‘y’:
        if (loop === false) {
          if (current === 0 && deltaY > 0) return ‘origin’;
          if (current === frames.length – 1 && deltaY < 0) return ‘origin’;
        }
        if (Math.abs(deltaY) < minMove) return ‘origin’;
        return deltaY > 0 ? ‘down’ : ‘up’;
      default:
    }
  }
  moveFramesBy (deltaX, deltaY) {
    console.log(`Carousel moveFramesBy: deltaX=${deltaX},deltaY=${deltaY}`);
    const { prev, current, next } = this.state.movingFrames;
    const { frameWidth, frameHeight } = this.state;
    switch (this.props.axis) {
      case ‘x’:
        translateXY(current, deltaX, 0);
        if (deltaX < 0) {
          translateXY(next, deltaX + frameWidth, 0);
        } else {
          translateXY(prev, deltaX – frameWidth, 0);
        }
        break;
      case ‘y’:
        translateXY(current, 0, deltaY);
        if (deltaY < 0) {
          translateXY(next, 0, deltaY + frameHeight);
        } else {
          translateXY(prev, 0, deltaY – frameHeight);
        }
        break;
      default:
    }
  }
  prepareAutoSlide () {
    console.log(`Carousel prepareAutoSlide: this.state.frames.length=${this.state.frames.length}`);
    if (this.state.frames.length < 2) return;
    this.clearAutoTimeout();
    this.updateFrameSize(() => {
      this.prepareSiblingFrames();
    });
    if (this.props.loop && this.props.auto && this.mounted) {
      // auto slide only avalible in loop mode
      const slideTimeoutID = setTimeout(this.autoSlide, this.props.interval);
      this.setState({ slider: slideTimeoutID });
    }
  }
  // auto slide to ‘next’ or ‘prev’
  autoSlide (rel) {
    console.log(`Carousel autoSlide: rel=${rel}`);
    this.clearAutoTimeout();
    switch (rel) {
      case ‘prev’:
        this.transitFramesTowards(this.props.axis === ‘x’ ? ‘right’ : ‘down’);
        break;
      case ‘next’:
      default:
        this.transitFramesTowards(this.props.axis === ‘x’ ? ‘left’ : ‘up’);
    }
    // prepare next move after animation
    setTimeout(() => this.prepareAutoSlide(), this.props.duration);
  }
  // next () {
  //   const { current, frames } = this.state;
  //   if (!this.props.loop && current === frames.length – 1) return false;
  //   this.autoSlide(‘next’);
  // }
  // prev () {
  //   if (!this.props.loop && this.state.current === 0) return false;
  //   this.autoSlide(‘prev’);
  // }
  clearAutoTimeout () {
    console.log(`Carousel clearAutoTimeout: this.state.slider=${this.state.slider}`);
    clearTimeout(this.state.slider);
  }
  updateFrameSize (cb) {
    console.log(`Carousel updateFrameSize`);
    const { width, height } = window.getComputedStyle(this.base);
    this.setState({
      frameWidth: parseFloat(width.split(‘px’)[0]),
      frameHeight: parseFloat(height.split(‘px’)[0])
    }, cb);
    console.log(`this.state.frameWidth=${this.state.frameWidth},this.state.frameHeight=${this.state.frameHeight}`);
  }
  prepareSiblingFrames () {
    console.log(`Carousel prepareSiblingFrames`);
    const siblings = {
      current: this.refMap[this.getFrameId()],
      prev: this.refMap[this.getFrameId(‘prev’)],
      next: this.refMap[this.getFrameId(‘next’)]
    };
    if (!this.props.loop) {
      this.state.current === 0 && (siblings.prev = undefined);
      this.state.current === this.state.frames.length – 1 && (siblings.next = undefined);
    }
    this.setState({ movingFrames: siblings });
    // prepare frames position
    translateXY(siblings.current, 0, 0);
    if (this.props.axis === ‘x’) {
      translateXY(siblings.prev, -this.state.frameWidth, 0);
      translateXY(siblings.next, this.state.frameWidth, 0);
    } else {
      translateXY(siblings.prev, 0, -this.state.frameHeight);
      translateXY(siblings.next, 0, this.state.frameHeight);
    }
    return siblings;
  }
  getFrameId (pos) {
    const { frames, current } = this.state;
    const total = frames.length;
    switch (pos) {
      case ‘prev’:
        return (current – 1 + total) % total;
      case ‘next’:
        return (current + 1) % total;
      default:
        return current;
    }
  }
  transitFramesTowards (direction) {
    const { prev, current, next } = this.state.movingFrames;
    const { duration, axis } = this.props;
    let newCurrentId = this.state.current;
    switch (direction) {
      case ‘up’:
        translateXY(current, 0, -this.state.frameHeight, duration);
        translateXY(next, 0, 0, duration);
        newCurrentId = this.getFrameId(‘next’);
        break;
      case ‘down’:
        translateXY(current, 0, this.state.frameHeight, duration);
        translateXY(prev, 0, 0, duration);
        newCurrentId = this.getFrameId(‘prev’);
        break;
      case ‘left’:
        translateXY(current, -this.state.frameWidth, 0, duration);
        translateXY(next, 0, 0, duration);
        newCurrentId = this.getFrameId(‘next’);
        break;
      case ‘right’:
        translateXY(current, this.state.frameWidth, 0, duration);
        translateXY(prev, 0, 0, duration);
        newCurrentId = this.getFrameId(‘prev’);
        break;
      default: // back to origin
        translateXY(current, 0, 0, duration);
        if (axis === ‘x’) {
          translateXY(prev, -this.state.frameWidth, 0, duration);
          translateXY(next, this.state.frameWidth, 0, duration);
        } else if (axis === ‘y’) {
          translateXY(prev, 0, -this.state.frameHeight, duration);
          translateXY(next, 0, this.state.frameHeight, duration);
        }
    }
    this.setState({ current: newCurrentId });
  }
  // debugFrames () {
  //   console.log(‘>>> DEBUG-FRAMES: current’, this.state.current)
  //   const len = this.state.frames.length
  //   for (let i = 0; i < len; ++i) {
  //     const ref = this.refMap[i]
  //     console.info(ref.innerText.trim(), ref.style.transform)
  //   }
  // }
  render () {
    const { frames, current } = this.state;
    // const { widgets, axis, loop, auto, interval } = this.props;
        // onMouseDown={this.onTouchStart}
    return (
      <div
        style={objectAssign(styles.wrapper, this.props.style)}
        onTouchMove={this.onTouchMove}
        onTouchStart={this.onTouchStart}
        onTouchEnd={this.onTouchEnd}
      >
        {
          frames.map((frame, i) => {
            const frameStyle = objectAssign({zIndex: 99 – i}, styles.frame);
            const cb = (el) => this.refMap[i] = el;
            return (
              <RefHelper key={i} style={frameStyle} callback={cb}>
                { frame }
              </RefHelper>
            );
          })
        }
        {
          <IndicatorDots index={current} total={frames.length}/>
        }
        {
          // widgets && [].concat(widgets).map((Widget, i) => (
          //   <Widget
          //     key={i}
          //     index={current}
          //     total={frames.length}
          //     prevHandler={this.prev}
          //     nextHandler={this.next}
          //     axis={axis} loop={loop} auto={auto} interval={interval} />
          // ))
        }
        { this.props.frames && this.props.children }
      </div>
    );
  }
}
Carousel.defaultProps = {
  axis: ‘x’,
  auto: false,
  loop: false,
  interval: 5000,
  duration: 300,
  minMove: 42
};
function translateXY (el, x, y, duration = 0) {
  if (!el) return;
  el.style.opacity = ‘1’;
  // animation
  el.style.transitionDuration = duration + ‘ms’;
  el.style.webkitTransitionDuration = duration + ‘ms’;
  el.style.transfrom = `translate(${x}px, ${y}px)`;
  el.style.webkitTransform = `translate(${x}px, ${y}px) translateZ(0)`;
}
export default Carousel;

但是结果是:

可以自动轮播了

可以手动滑动切换图片:向右滑动,正常切换图片。但是向左滑动,切换图片还是向右滑动的切换的。

不爽。

算了,去试试看起来不错的:

leandrowd/react-responsive-carousel: React.js Responsive Carousel (with Swipe)

➜  ucowsapp git:(master) ✗ npm install react-responsive-carousel –save
npm WARN [email protected] No repository field.
npm WARN [email protected] No license field.
added 2 packages in 17.155s

然后去试试,然后效果很好。

【总结】

此处用之前的

https://github.com/amio/re-carousel

实现的轮播图,有各种问题,而且功能也不够强。

虽然经过优化,但是还是存在一些小问题。

最后换用

https://github.com/leandrowd/react-responsive-carousel

完美实现了轮播的效果。

先去安装:

npm install react-responsive-carousel –save

代码:

src/container/main/index.js

import { Carousel } from ‘react-responsive-carousel’;
import ‘react-responsive-carousel/lib/styles/carousel.css’;
  render() {
    return (
      <div class={style.container}>
        <Carousel
          axis=”horizontal”
          showThumbs={false}
          showStatus={false}
          showArrows={true}
          infiniteLoop={true}
          autoPlay={true}
          dynamicHeight={true}
          emulateTouch={true}
        >
          <div>
              <img src={imgTop01} />
          </div>
          <div>
              <img src={imgTop02} />
          </div>
          <div>
              <img src={imgTop01} />
          </div>
          <div>
              <img src={imgTop02} />
          </div>
        </Carousel>
 。。。
      </div>
    );
  }

效果:

注意:

此处要(注意到人家的提示

// Don’t forget to include the css in your page 

// <link rel=”stylesheet” href=”carousel.css”/>”

去导入对应的css:

此处的写法是:

import ‘react-responsive-carousel/lib/styles/carousel.css’;

当然,也可以减少体积,用:

import ‘react-responsive-carousel/lib/styles/carousel.min.css’;

否则,就会出现错乱的效果,图片从上到下平铺了:

转载请注明:在路上 » 【已解决】ReactJS的app中首页轮播图静止不循环滚动切换

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
89 queries in 0.187 seconds, using 22.17MB memory