From 4ae3357401b6b001c791a941e54125457bbed76e Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 12 Jun 2019 16:04:27 +0530 Subject: [PATCH 1/8] Set pan handlers on the circular image Idea is to make sure the touchable are is as close as the actual circular part of the image. So that pageX and pageY values are as expected. --- ColorWheel.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index fb1377f..6e215ee 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -202,16 +202,17 @@ export class ColorWheel extends Component { ref={node => { this.self = node }} - {...panHandlers} onLayout={nativeEvent => this.onLayout(nativeEvent)} style={[styles.coverResponder, this.props.style]}> From e7ca96ab82c742cc9ab87dac3e127fc95f7716fa Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 12 Jun 2019 16:08:29 +0530 Subject: [PATCH 2/8] Set pointerEvents none on draggable thumb This will make sure touch works, on pressing right on top of the draggable thumb. --- ColorWheel.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index 6e215ee..a53d46c 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -203,7 +203,8 @@ export class ColorWheel extends Component { this.self = node }} onLayout={nativeEvent => this.onLayout(nativeEvent)} - style={[styles.coverResponder, this.props.style]}> + style={[styles.coverResponder, this.props.style]} + pointerEvents={'box-none'} > - + ) } From 1286e6b4ec18dd5fa4a2790c98f3decbbeaa061d Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 12 Jun 2019 17:23:59 +0530 Subject: [PATCH 3/8] Update thumb color and position on pan grant and move This commit updates the thumb color and position during onPanResponderGrant and onPanResponderMove if they are inside bounds. This will make sure on pressing anywhere on the circular slide and on dragging finger from outside, the slider works properly. --- ColorWheel.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index a53d46c..7fb7b5f 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -25,28 +25,31 @@ export class ColorWheel extends Component { currentColor: props.initialColor, pan: new Animated.ValueXY(), radius: 0, + panHandlerReady: true, } } componentDidMount = () => { this._panResponder = PanResponder.create({ - onStartShouldSetPanResponderCapture: ({nativeEvent}) => { - if (this.outBounds(nativeEvent)) return - this.updateColor({nativeEvent}) - this.setState({panHandlerReady: true}) - - this.state.pan.setValue({ - x: -this.state.left + nativeEvent.pageX - this.props.thumbSize / 2, - y: -this.state.top + nativeEvent.pageY - this.props.thumbSize / 2, - }) + onStartShouldSetPanResponderCapture: () => { return true }, onStartShouldSetPanResponder: () => true, onMoveShouldSetPanResponderCapture: () => true, - onPanResponderGrant: () => true, + onPanResponderGrant: ({nativeEvent}) => { + if (this.outBounds(nativeEvent)) return + if (this.state.panHandlerReady) { + this.updateColorAndThumbPosition(nativeEvent); + } + }, onPanResponderMove: (event, gestureState) => { if (this.outBounds(gestureState)) return + if (this.state.panHandlerReady) { + const {nativeEvent} = event; + this.updateColorAndThumbPosition(nativeEvent); + } + this.resetPanHandler() return Animated.event( [ @@ -75,6 +78,15 @@ export class ColorWheel extends Component { }) } + updateColorAndThumbPosition (nativeEvent) { + this.updateColor({nativeEvent}) + + this.state.pan.setValue({ + x: -this.state.left + nativeEvent.pageX - this.props.thumbSize / 2, + y: -this.state.top + nativeEvent.pageY - this.props.thumbSize / 2, + }) + } + onLayout () { this.measureOffset() } From e9d39289fbeab545ab54ea07e4a7dac2a789fdb0 Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 12 Jun 2019 18:39:43 +0530 Subject: [PATCH 4/8] Handle thumb updation using a distinct state variable --- ColorWheel.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index 7fb7b5f..798dae9 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -26,6 +26,7 @@ export class ColorWheel extends Component { pan: new Animated.ValueXY(), radius: 0, panHandlerReady: true, + didUpdateThumb: false, } } @@ -38,14 +39,14 @@ export class ColorWheel extends Component { onMoveShouldSetPanResponderCapture: () => true, onPanResponderGrant: ({nativeEvent}) => { if (this.outBounds(nativeEvent)) return - if (this.state.panHandlerReady) { + if (!this.state.didUpdateThumb) { this.updateColorAndThumbPosition(nativeEvent); } }, onPanResponderMove: (event, gestureState) => { if (this.outBounds(gestureState)) return - if (this.state.panHandlerReady) { + if (!this.state.didUpdateThumb) { const {nativeEvent} = event; this.updateColorAndThumbPosition(nativeEvent); } @@ -64,7 +65,10 @@ export class ColorWheel extends Component { }, onMoveShouldSetPanResponder: () => true, onPanResponderRelease: ({nativeEvent}) => { - this.setState({panHandlerReady: true}) + this.setState({ + panHandlerReady: true, + didUpdateThumb: false, + }) this.state.pan.flattenOffset() const {radius} = this.calcPolar(nativeEvent) if (radius < 0.1) { @@ -85,6 +89,9 @@ export class ColorWheel extends Component { x: -this.state.left + nativeEvent.pageX - this.props.thumbSize / 2, y: -this.state.top + nativeEvent.pageY - this.props.thumbSize / 2, }) + this.setState({ + didUpdateThumb: true, + }) } onLayout () { From 603ccb2758a0e0583c9bb5291573b1eaed88a755 Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 12 Jun 2019 18:51:53 +0530 Subject: [PATCH 5/8] Keep draggable thumb inside the circle Hence the image is the pan responder and not the outer View it is important to keep some(atleast half) of the draggable thumb inside the circular Image, for drag to be possible if user press right on the thumb. --- ColorWheel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index 798dae9..8da12e2 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -227,8 +227,8 @@ export class ColorWheel extends Component { Date: Wed, 8 Apr 2020 13:57:12 +0530 Subject: [PATCH 6/8] Add support to update 'currentColor' after initial render Changing the value of 'toggleMeToforceUpdateInitialColor' will update the currentColor, with what ever value is passed as initialColor. --- ColorWheel.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/ColorWheel.js b/ColorWheel.js index 8da12e2..462dd9f 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -18,12 +18,29 @@ export class ColorWheel extends Component { onColorChange: () => {}, } + static getDerivedStateFromProps(props: Object, state: Object): null | Object { + const { + toggleMeToforceUpdateInitialColor, + } = props; + if (toggleMeToforceUpdateInitialColor !== state.toggleMeToforceUpdateInitialColor) { + return { + toggleMeToforceUpdateInitialColor, + }; + } + return null; + } + constructor (props) { super(props) + const { + initialColor, + toggleMeToforceUpdateInitialColor = 0, + } = props; this.state = { offset: {x: 0, y: 0}, - currentColor: props.initialColor, + currentColor: initialColor, pan: new Animated.ValueXY(), + toggleMeToforceUpdateInitialColor, radius: 0, panHandlerReady: true, didUpdateThumb: false, @@ -82,6 +99,16 @@ export class ColorWheel extends Component { }) } + componentDidUpdate(prevProps: Object, prevState: Object) { + const { + initialColor, + } = this.props; + + if (initialColor && this.state.toggleMeToforceUpdateInitialColor !== prevState.toggleMeToforceUpdateInitialColor) { + this.forceUpdate(initialColor); + } + } + updateColorAndThumbPosition (nativeEvent) { this.updateColor({nativeEvent}) From 2f1e0467ab5d2a0379d9b4d4bce294e32ffeaa78 Mon Sep 17 00:00:00 2001 From: Rimnesh Fernandez Date: Wed, 8 Apr 2020 15:11:33 +0530 Subject: [PATCH 7/8] Do not use arrow function inside render --- ColorWheel.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index 462dd9f..d3f15a9 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -121,7 +121,7 @@ export class ColorWheel extends Component { }) } - onLayout () { + onLayout = () => { this.measureOffset() } @@ -227,6 +227,10 @@ export class ColorWheel extends Component { }).start() } + setRef = (ref) => { + this.self = ref; + } + render () { const {radius} = this.state const thumbStyle = [ @@ -245,10 +249,8 @@ export class ColorWheel extends Component { return ( { - this.self = node - }} - onLayout={nativeEvent => this.onLayout(nativeEvent)} + ref={this.setRef} + onLayout={this.onLayout} style={[styles.coverResponder, this.props.style]} pointerEvents={'box-none'} > Date: Tue, 22 Nov 2022 13:37:26 +0530 Subject: [PATCH 8/8] Fix rendering issue in iOS Also expose new method onGrant --- ColorWheel.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/ColorWheel.js b/ColorWheel.js index d3f15a9..1b5886b 100644 --- a/ColorWheel.js +++ b/ColorWheel.js @@ -52,10 +52,13 @@ export class ColorWheel extends Component { onStartShouldSetPanResponderCapture: () => { return true }, - onStartShouldSetPanResponder: () => true, - onMoveShouldSetPanResponderCapture: () => true, + onStartShouldSetPanResponder: () => { return true }, + onMoveShouldSetPanResponderCapture: () => { return true }, onPanResponderGrant: ({nativeEvent}) => { if (this.outBounds(nativeEvent)) return + if (this.props.onGrant) { + this.props.onGrant(); + } if (!this.state.didUpdateThumb) { this.updateColorAndThumbPosition(nativeEvent); } @@ -121,11 +124,11 @@ export class ColorWheel extends Component { }) } - onLayout = () => { - this.measureOffset() + onLayout = ({nativeEvent: {layout}}) => { + this.measureOffset(layout) } - measureOffset () { + measureOffset (layout) { /* * const {x, y, width, height} = nativeEvent.layout * onlayout values are different than measureInWindow @@ -133,19 +136,22 @@ export class ColorWheel extends Component { * but in measureInWindow they are relative to the window */ this.self.measureInWindow((x, y, width, height) => { + // In iOS measureInWindow sometimes returns height/width zero. Hence use width/height from layout in that case + const _w = width || layout.width; + const _h = height || layout.height const window = Dimensions.get('window') - const absX = x % width - const radius = Math.min(width, height) / 2 + const absX = x % _w + const radius = Math.min(_w, _h) / 2 const offset = { - x: absX + width / 2, - y: y % window.height + height / 2, + x: absX + _w / 2, + y: y % window.height + _h / 2, } this.setState({ offset, radius, - height, - width, + height: _h, + width: _w, top: y % window.height, left: absX, }) @@ -200,14 +206,14 @@ export class ColorWheel extends Component { const hsv = {h: deg, s: 100 * radius, v: 100}; const currentColor = colorsys.hsv2Hex(hsv) this.setState({hsv, currentColor}) - this.props.onColorChange(hsv); + this.props.onColorChange(hsv, false); } forceUpdate = color => { const {h, s, v} = colorsys.hex2Hsv(color) const {left, top} = this.calcCartesian(h, s / 100) this.setState({currentColor: color}) - this.props.onColorChange({h, s, v}) + this.props.onColorChange({h, s, v}, true) this.state.pan.setValue({ x: left - this.props.thumbSize / 2, y: top - this.props.thumbSize / 2, @@ -218,7 +224,7 @@ export class ColorWheel extends Component { const {h, s, v} = colorsys.hex2Hsv(color) const {left, top} = this.calcCartesian(h, s / 100) this.setState({currentColor: color}) - this.props.onColorChange({h, s, v}) + this.props.onColorChange({h, s, v}, false) Animated.spring(this.state.pan, { toValue: { x: left - this.props.thumbSize / 2, @@ -254,6 +260,7 @@ export class ColorWheel extends Component { style={[styles.coverResponder, this.props.style]} pointerEvents={'box-none'} >