From bbce39f3b2ec8a317109f9e1fc7555d5463e40f1 Mon Sep 17 00:00:00 2001 From: liuloppan Date: Sun, 1 Dec 2019 21:15:08 +0100 Subject: [PATCH 1/5] Added 'currentFrameIndex' and 'setFrameHandler' * Added functionality to pass currentFrameIndex as props in the cases where you may have a certain order but not necessarily want to start at index 0 * Added functionality to set a frame index directly with setFrame(index) which can be passed as the prop 'setFrameHandler' to Widgets in a similar fashion as 'nextHandler' and 'prevHandler' --- src/carousel.js | 110 +++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/src/carousel.js b/src/carousel.js index 97fac75..d2b4f07 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -20,7 +20,7 @@ class Carousel extends React.Component { this.state = { frames: [].concat(props.frames || props.children || []), - current: 0 + currentFrameIndex: props.currentFrameIndex } this.mounted = false @@ -32,6 +32,7 @@ class Carousel extends React.Component { this.autoSlide = this.autoSlide.bind(this) this.prev = this.prev.bind(this) this.next = this.next.bind(this) + this.setFrame = this.setFrame.bind(this) if (props.loop === false && props.auto) { console.warn('[re-carousel] Auto-slide only works in loop mode.') @@ -69,7 +70,7 @@ class Carousel extends React.Component { const frames = [].concat(nextProps.frames || nextProps.children || []) const nextState = { frames } if (frames.length && frames.length !== prevState.frames.length) { - nextState.current = 0 + nextState.currentFrameIndex = 0 } return nextState } @@ -90,7 +91,7 @@ class Carousel extends React.Component { } onTouchStart (e) { - if (this.state.total < 2) return + if (this.state.frames.length < 2) return; // e.preventDefault() this.clearAutoTimeout() @@ -133,11 +134,11 @@ class Carousel extends React.Component { // when reach frames edge in non-loop mode, reduce drag effect. if (!this.props.loop) { - if (this.state.current === this.state.frames.length - 1) { + if (this.state.currentFrameIndex === this.state.frames.length - 1) { deltaX < 0 && (deltaX /= 3) deltaY < 0 && (deltaY /= 3) } - if (this.state.current === 0) { + if (this.state.currentFrameIndex === 0) { deltaX > 0 && (deltaX /= 3) deltaY > 0 && (deltaY /= 3) } @@ -159,21 +160,21 @@ class Carousel extends React.Component { } decideEndPosition () { - const { deltaX = 0, deltaY = 0, current, frames } = this.state + const { deltaX = 0, deltaY = 0, currentFrameIndex, 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 (currentFrameIndex === 0 && deltaX > 0) return 'origin' + if (currentFrameIndex === 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 (currentFrameIndex === 0 && deltaY > 0) return 'origin' + if (currentFrameIndex === frames.length - 1 && deltaY < 0) return 'origin' } if (Math.abs(deltaY) < minMove) return 'origin' return deltaY > 0 ? 'down' : 'up' @@ -182,12 +183,12 @@ class Carousel extends React.Component { } moveFramesBy (deltaX, deltaY) { - const { prev, current, next } = this.state.movingFrames + const { prev, currentFrameIndex, next } = this.state.movingFrames const { frameWidth, frameHeight } = this.state switch (this.props.axis) { case 'x': - translateXY(current, deltaX, 0) + translateXY(currentFrameIndex, deltaX, 0) if (deltaX < 0) { translateXY(next, deltaX + frameWidth, 0) } else { @@ -195,7 +196,7 @@ class Carousel extends React.Component { } break case 'y': - translateXY(current, 0, deltaY) + translateXY(currentFrameIndex, 0, deltaY) if (deltaY < 0) { translateXY(next, 0, deltaY + frameHeight) } else { @@ -239,13 +240,13 @@ class Carousel extends React.Component { } next () { - const { current, frames } = this.state - if (!this.props.loop && current === frames.length - 1) return false + const { currentFrameIndex, frames } = this.state + if (!this.props.loop && currentFrameIndex === frames.length - 1) return false this.autoSlide('next') } prev () { - if (!this.props.loop && this.state.current === 0) return false + if (!this.props.loop && this.state.currentFrameIndex === 0) return false const { prev, next } = this.state.movingFrames if (prev === next) { @@ -262,21 +263,39 @@ class Carousel extends React.Component { this.autoSlide('prev') } + async setFrame(index) { + const diff = Math.abs(index - this.state.currentFrameIndex); + + if (index < this.state.currentFrameIndex) { + for (let i = 0; i < diff; i++) { + // wait for the promise to resolve before advancing the for loop + await this.prev(); + } + } else if (index > this.state.currentFrameIndex) { + for (let i = 0; i < diff; i++) { + // wait for the promise to resolve before advancing the for loop + await this.next(); + } + } + } + clearAutoTimeout () { clearTimeout(this.state.slider) } updateFrameSize (cb) { - const { width, height } = window.getComputedStyle(this.refs.wrapper) - this.setState({ - frameWidth: parseFloat(width.split('px')[0]), - frameHeight: parseFloat(height.split('px')[0]) - }, cb) + if (this.refs.wrapper) { + const { width, height } = window.getComputedStyle(this.refs.wrapper) + this.setState({ + frameWidth: parseFloat(width.split('px')[0]), + frameHeight: parseFloat(height.split('px')[0]) + }, cb) + } } getSiblingFrames () { return { - current: this.refs['f' + this.getFrameId()], + currentFrameIndex: this.refs['f' + this.getFrameId()], prev: this.refs['f' + this.getFrameId('prev')], next: this.refs['f' + this.getFrameId('next')] } @@ -286,14 +305,14 @@ class Carousel extends React.Component { const siblings = this.getSiblingFrames() if (!this.props.loop) { - this.state.current === 0 && (siblings.prev = undefined) - this.state.current === this.state.frames.length - 1 && (siblings.next = undefined) + this.state.currentFrameIndex === 0 && (siblings.prev = undefined) + this.state.currentFrameIndex === this.state.frames.length - 1 && (siblings.next = undefined) } this.setState({ movingFrames: siblings }) // prepare frames position - translateXY(siblings.current, 0, 0) + translateXY(siblings.currentFrameIndex, 0, 0) if (this.props.axis === 'x') { translateXY(siblings.prev, -this.state.frameWidth, 0) translateXY(siblings.next, this.state.frameWidth, 0) @@ -306,46 +325,46 @@ class Carousel extends React.Component { } getFrameId (pos) { - const { frames, current } = this.state + const { frames, currentFrameIndex } = this.state const total = frames.length switch (pos) { case 'prev': - return (current - 1 + total) % total + return (currentFrameIndex - 1 + total) % total case 'next': - return (current + 1) % total + return (currentFrameIndex + 1) % total default: - return current + return currentFrameIndex } } transitFramesTowards (direction) { - const { prev, current, next } = this.state.movingFrames + const { prev, currentFrameIndex, next } = this.state.movingFrames const { duration, axis, onTransitionEnd } = this.props - let newCurrentId = this.state.current + let newcurrentFrameIndexId = this.state.currentFrameIndex switch (direction) { case 'up': - translateXY(current, 0, -this.state.frameHeight, duration) + translateXY(currentFrameIndex, 0, -this.state.frameHeight, duration) translateXY(next, 0, 0, duration) - newCurrentId = this.getFrameId('next') + newcurrentFrameIndexId = this.getFrameId('next') break case 'down': - translateXY(current, 0, this.state.frameHeight, duration) + translateXY(currentFrameIndex, 0, this.state.frameHeight, duration) translateXY(prev, 0, 0, duration) - newCurrentId = this.getFrameId('prev') + newcurrentFrameIndexId = this.getFrameId('prev') break case 'left': - translateXY(current, -this.state.frameWidth, 0, duration) + translateXY(currentFrameIndex, -this.state.frameWidth, 0, duration) translateXY(next, 0, 0, duration) - newCurrentId = this.getFrameId('next') + newcurrentFrameIndexId = this.getFrameId('next') break case 'right': - translateXY(current, this.state.frameWidth, 0, duration) + translateXY(currentFrameIndex, this.state.frameWidth, 0, duration) translateXY(prev, 0, 0, duration) - newCurrentId = this.getFrameId('prev') + newcurrentFrameIndexId = this.getFrameId('prev') break default: // back to origin - translateXY(current, 0, 0, duration) + translateXY(currentFrameIndex, 0, 0, duration) if (axis === 'x') { translateXY(prev, -this.state.frameWidth, 0, duration) translateXY(next, this.state.frameWidth, 0, duration) @@ -357,11 +376,11 @@ class Carousel extends React.Component { onTransitionEnd && setTimeout(() => onTransitionEnd(this.getSiblingFrames()), duration) - this.setState({ current: newCurrentId }) + this.setState({currentFrameIndex: newcurrentFrameIndexId }) } // debugFrames () { - // console.log('>>> DEBUG-FRAMES: current', this.state.current) + // console.log('>>> DEBUG-FRAMES: currentFrameIndex', this.state.currentFrameIndex) // const len = this.state.frames.length // for (let i = 0; i < len; ++i) { // const ref = this.refs['f' + i] @@ -370,7 +389,7 @@ class Carousel extends React.Component { // } render () { - const { frames, current } = this.state + const { frames, currentFrameIndex } = this.state const { widgets, axis, loop, auto, interval } = this.props const wrapperStyle = objectAssign(styles.wrapper, this.props.style) @@ -394,10 +413,11 @@ class Carousel extends React.Component { widgets && [].concat(widgets).map((Widget, i) => ( )) } @@ -409,6 +429,7 @@ class Carousel extends React.Component { Carousel.propTypes = { axis: PropTypes.oneOf(['x', 'y']), auto: PropTypes.bool, + currentFrameIndex: PropTypes.number, loop: PropTypes.bool, interval: PropTypes.number, duration: PropTypes.number, @@ -422,6 +443,7 @@ Carousel.propTypes = { Carousel.defaultProps = { axis: 'x', auto: false, + currentFrameIndex: 0, loop: false, interval: 5000, duration: 300, From f89021172155e813507747f5d6fd33fb536aa33f Mon Sep 17 00:00:00 2001 From: liuloppan Date: Sun, 1 Dec 2019 21:30:21 +0100 Subject: [PATCH 2/5] Changed setFrame function setFrame now adds timeouts for 'next' and 'prev' which creates a smoother transition between frames. --- src/carousel.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/carousel.js b/src/carousel.js index d2b4f07..f2ff8ad 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -263,18 +263,16 @@ class Carousel extends React.Component { this.autoSlide('prev') } - async setFrame(index) { + setFrame (index, ms = 450) { const diff = Math.abs(index - this.state.currentFrameIndex); - if (index < this.state.currentFrameIndex) { for (let i = 0; i < diff; i++) { - // wait for the promise to resolve before advancing the for loop - await this.prev(); + setTimeout(() => this.prev(), i * ms); } - } else if (index > this.state.currentFrameIndex) { + } + else if (index > this.state.currentFrameIndex) { for (let i = 0; i < diff; i++) { - // wait for the promise to resolve before advancing the for loop - await this.next(); + setTimeout(() => this.next(), i * ms); } } } From ae0e3666755394ac9873072efbae1ce47cb52634 Mon Sep 17 00:00:00 2001 From: Lovisa Hassler Date: Tue, 7 Apr 2020 01:12:43 +0200 Subject: [PATCH 3/5] Fixed setFrame directly with no ms delay --- src/carousel.js | 40 +++++++++++++++++++++++++++++++--------- src/index.js | 8 ++++++-- src/indicator-dots.js | 10 +++++----- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/carousel.js b/src/carousel.js index f2ff8ad..c6d340e 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -47,6 +47,10 @@ class Carousel extends React.Component { this.refs.wrapper.addEventListener('touchmove', this.onTouchMove, {capture: true}) this.refs.wrapper.addEventListener('touchend', this.onTouchEnd, {capture: true}) window.addEventListener('resize', this.onResize); + + if (this.props.currentFrameIndex) { + this.setFrame(this.props.currentFrameIndex); + } } componentWillUnmount () { @@ -263,7 +267,16 @@ class Carousel extends React.Component { this.autoSlide('prev') } - setFrame (index, ms = 450) { + setFrame (index, ms = undefined) { + + if (!ms) { + this.setState({ + currentFrameIndex: index + }) + this.prepareSiblingFrames(true, index); + return; + } + const diff = Math.abs(index - this.state.currentFrameIndex); if (index < this.state.currentFrameIndex) { for (let i = 0; i < diff; i++) { @@ -299,8 +312,17 @@ class Carousel extends React.Component { } } - prepareSiblingFrames () { - const siblings = this.getSiblingFrames() + getSiblingFramesByOverride (index) { + return { + currentFrameIndex: this.refs['f' + index], + prev: this.refs['f' + this.getFrameId('prev', index)], + next: this.refs['f' + this.getFrameId('next', index)] + } + } + + prepareSiblingFrames (override = false, index = undefined) { + + const siblings = override ? this.getSiblingFramesByOverride(index) : this.getSiblingFrames() if (!this.props.loop) { this.state.currentFrameIndex === 0 && (siblings.prev = undefined) @@ -322,16 +344,16 @@ class Carousel extends React.Component { return siblings } - getFrameId (pos) { - const { frames, currentFrameIndex } = this.state + getFrameId (pos, current=this.state.currentFrameIndex) { + const { frames } = this.state const total = frames.length switch (pos) { case 'prev': - return (currentFrameIndex - 1 + total) % total + return (current - 1 + total) % total case 'next': - return (currentFrameIndex + 1) % total + return (current + 1) % total default: - return currentFrameIndex + return current } } @@ -401,7 +423,7 @@ class Carousel extends React.Component { onMouseDown={this.onTouchStart} > { frames.map((frame, i) => { - const frameStyle = objectAssign({zIndex: 99 - i}, styles.frame) + const frameStyle = objectAssign({zIndex: 99 - (Math.abs(currentFrameIndex - i))}, styles.frame) return
{frame}
}) } diff --git a/src/index.js b/src/index.js index 5daac2e..54f8fa9 100644 --- a/src/index.js +++ b/src/index.js @@ -24,10 +24,14 @@ class App extends React.Component { vertical - -

FRAME 1

+ +

FRAME 1

FRAME 2

FRAME 3

+

FRAME 4

+

FRAME 5

+

FRAME 6

+

FRAME 7

) diff --git a/src/indicator-dots.js b/src/indicator-dots.js index 41fd2eb..c4a4fcc 100644 --- a/src/indicator-dots.js +++ b/src/indicator-dots.js @@ -5,14 +5,14 @@ function Dot (props) { return ( + }} onClick={props.onClick}/> ) } @@ -32,7 +32,7 @@ export default function IndicatorDots (props) { return (
{ Array.apply(null, Array(props.total)).map((x, i) => { - return + return {props.setFrameHandler(i)}} /> }) }
) From ad0eb198a784a41c21076ab9fbcf916de2248ec6 Mon Sep 17 00:00:00 2001 From: Lovisa Hassler Date: Mon, 27 Apr 2020 22:58:15 +0200 Subject: [PATCH 4/5] Changed name of 'currentFrameIndex' to 'onStartFrameIndex' --- README.md | 1 + src/carousel.js | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5b388d1..14dee02 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ All attributes are optional. - `auto` {Boolean} `true` or `false` (`false` by default) toggle auto sliding. - `interval` {Number} (`4000`ms by default) interval for auto sliding. - `duration` {Number} (`300`ms by default) duration for animation. +- `onStartFrameIndex` {Number} (`0` by default) the index of the frame on first render. - `onTransitionEnd` {Function({ prev: HTMLElement, current: HTMLElement, next: HTMLElement})} on frames transition end callback. - `widgets` {Array of ReactClass} Indicator and switcher could be various, so it's not builtin. Here's some example custom widgets diff --git a/src/carousel.js b/src/carousel.js index c6d340e..3b5d125 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -20,7 +20,7 @@ class Carousel extends React.Component { this.state = { frames: [].concat(props.frames || props.children || []), - currentFrameIndex: props.currentFrameIndex + currentFrameIndex: props.onStartFrameIndex } this.mounted = false @@ -48,8 +48,8 @@ class Carousel extends React.Component { this.refs.wrapper.addEventListener('touchend', this.onTouchEnd, {capture: true}) window.addEventListener('resize', this.onResize); - if (this.props.currentFrameIndex) { - this.setFrame(this.props.currentFrameIndex); + if (this.state.currentFrameIndex) { + this.setFrame(this.state.currentFrameIndex); } } @@ -449,7 +449,7 @@ class Carousel extends React.Component { Carousel.propTypes = { axis: PropTypes.oneOf(['x', 'y']), auto: PropTypes.bool, - currentFrameIndex: PropTypes.number, + onStartFrameIndex: PropTypes.number, loop: PropTypes.bool, interval: PropTypes.number, duration: PropTypes.number, @@ -463,7 +463,7 @@ Carousel.propTypes = { Carousel.defaultProps = { axis: 'x', auto: false, - currentFrameIndex: 0, + onStartFrameIndex: 0, loop: false, interval: 5000, duration: 300, From 25f1845f00b88a9797e718e6ef7d5df831b7d949 Mon Sep 17 00:00:00 2001 From: Lovisa Hassler Date: Mon, 27 Apr 2020 23:05:36 +0200 Subject: [PATCH 5/5] Cleanup --- src/carousel.js | 12 ++++++------ src/index.js | 8 ++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/carousel.js b/src/carousel.js index 3b5d125..f3ab1e0 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -361,27 +361,27 @@ class Carousel extends React.Component { const { prev, currentFrameIndex, next } = this.state.movingFrames const { duration, axis, onTransitionEnd } = this.props - let newcurrentFrameIndexId = this.state.currentFrameIndex + let newCurrentFrameIndexId = this.state.currentFrameIndex switch (direction) { case 'up': translateXY(currentFrameIndex, 0, -this.state.frameHeight, duration) translateXY(next, 0, 0, duration) - newcurrentFrameIndexId = this.getFrameId('next') + newCurrentFrameIndexId = this.getFrameId('next') break case 'down': translateXY(currentFrameIndex, 0, this.state.frameHeight, duration) translateXY(prev, 0, 0, duration) - newcurrentFrameIndexId = this.getFrameId('prev') + newCurrentFrameIndexId = this.getFrameId('prev') break case 'left': translateXY(currentFrameIndex, -this.state.frameWidth, 0, duration) translateXY(next, 0, 0, duration) - newcurrentFrameIndexId = this.getFrameId('next') + newCurrentFrameIndexId = this.getFrameId('next') break case 'right': translateXY(currentFrameIndex, this.state.frameWidth, 0, duration) translateXY(prev, 0, 0, duration) - newcurrentFrameIndexId = this.getFrameId('prev') + newCurrentFrameIndexId = this.getFrameId('prev') break default: // back to origin translateXY(currentFrameIndex, 0, 0, duration) @@ -396,7 +396,7 @@ class Carousel extends React.Component { onTransitionEnd && setTimeout(() => onTransitionEnd(this.getSiblingFrames()), duration) - this.setState({currentFrameIndex: newcurrentFrameIndexId }) + this.setState({currentFrameIndex: newCurrentFrameIndexId }) } // debugFrames () { diff --git a/src/index.js b/src/index.js index 54f8fa9..5daac2e 100644 --- a/src/index.js +++ b/src/index.js @@ -24,14 +24,10 @@ class App extends React.Component { vertical - -

FRAME 1

+ +

FRAME 1

FRAME 2

FRAME 3

-

FRAME 4

-

FRAME 5

-

FRAME 6

-

FRAME 7

)