之前的别人的代码,基于:
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
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中首页轮播图静止不循环滚动切换