From afdbd48a8a2ff6f91699deb918dcf89725107c0a Mon Sep 17 00:00:00 2001 From: dmacpro91 Date: Tue, 4 Jun 2024 12:14:54 -0400 Subject: [PATCH 1/5] Slider Fixes Fixed bug with not being able to obtain edge values of slider (0.0, 1.0) on mobile. Corrected slider thumb touch position to center itself to the touch rather than to the right of the touch. This should fix the bugs mentioned in most cases, but if the view is nested in more containers we may need to obtain the padding/margin offsets for the container view. I'll address that if there's enough demand. --- ColorPicker.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/ColorPicker.js b/ColorPicker.js index 5804b4f..4e90794 100644 --- a/ColorPicker.js +++ b/ColorPicker.js @@ -159,6 +159,8 @@ const flipInterpolationConfig = { module.exports = class ColorPicker extends Component { // testData = {} // testView = {forceUpdate(){}} + sliderXOffset = 0 + sliderYOffset = 0 color = { h: 0, s: 0, v: 100 } slideX = new Animated.Value(0) slideY = new Animated.Value(0) @@ -245,7 +247,8 @@ module.exports = class ColorPicker extends Component { if (this.props.disabled) return false; const { nativeEvent } = event if (this.outOfSlider(nativeEvent)) return - this.sliderMovement(event, gestureState) + const adjusted = this.sliderThumbAdjuster(event) + this.sliderMovement(adjusted, gestureState) this.updateValue({ nativeEvent }) return true }, @@ -265,8 +268,9 @@ module.exports = class ColorPicker extends Component { if (this.props.disabled) return; if (event && event.nativeEvent && typeof event.nativeEvent.preventDefault == 'function') event.nativeEvent.preventDefault() if (event && event.nativeEvent && typeof event.nativeEvent.stopPropagation == 'function') event.nativeEvent.stopPropagation() - if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; - this.sliderMovement(event, gestureState) + if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; //Fix this too (hit slop) + const adjusted = this.sliderThumbAdjuster(event) + this.sliderMovement(adjusted, gestureState) }, onMoveShouldSetPanResponder: () => true, onPanResponderRelease: (event, gestureState) => { @@ -429,12 +433,28 @@ module.exports = class ColorPicker extends Component { const row = this.props.row const loc = row ? nativeEvent.locationY : nativeEvent.locationX const { width, height } = this.sliderMeasure - return (loc > (row ? height - width : width - height)) + return (loc > (row ? (height - width) + this.props.sliderSize/2 : (width - height) + this.props.sliderSize/2)) } val(v) { const d = this.props.discrete, r = 11 * Math.round(v / 11) return d ? (r >= 99 ? 100 : r) : v } + sliderThumbAdjuster(event) { + const sliderThumbSize = this.props.sliderSize + const row = this.props.row + let correctedTouch = row ? event.nativeEvent.locationY : event.nativeEvent.locationX + correctedTouch -= sliderThumbSize/2 + const min = row ? this.sliderYOffset : this.sliderXOffset + const max = row ? this.sliderYOffset + this.sliderLength : this.sliderXOffset + this.sliderLength + if(correctedTouch < min) + correctedTouch = min; + else if(correctedTouch > max) + correctedTouch = max + return {nativeEvent: { + locationX: row ? event.nativeEvent.locationX : correctedTouch, + locationY: row ? correctedTouch : event.nativeEvent.locationY, + }} + } ratio(nativeEvent) { const row = this.props.row const loc = row ? nativeEvent.locationY : nativeEvent.locationX @@ -706,7 +726,11 @@ module.exports = class ColorPicker extends Component { } {!swatchesOnly && !sliderHidden && (discrete ? {this.disc} - : + : { + const dimens = event.nativeEvent.layout; + this.sliderXOffset = dimens.x; + this.sliderYOffset = dimens.y; + }}> From fa2817509689d8aa04e1c6d90f2dfdc7a64295c7 Mon Sep 17 00:00:00 2001 From: dmacpro91 Date: Tue, 4 Jun 2024 12:18:21 -0400 Subject: [PATCH 2/5] Slider Fixes Fixed bug with not being able to obtain edge values of slider (0.0, 1.0) on mobile. Corrected slider thumb touch position to center itself to the touch rather than to the right of the touch. This should fix the bugs mentioned in most cases, but if the view is nested in more containers we may need to obtain the padding/margin offsets for the container view. I'll address that if there's enough demand. --- ColorPicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ColorPicker.js b/ColorPicker.js index 4e90794..0673e02 100644 --- a/ColorPicker.js +++ b/ColorPicker.js @@ -268,7 +268,7 @@ module.exports = class ColorPicker extends Component { if (this.props.disabled) return; if (event && event.nativeEvent && typeof event.nativeEvent.preventDefault == 'function') event.nativeEvent.preventDefault() if (event && event.nativeEvent && typeof event.nativeEvent.stopPropagation == 'function') event.nativeEvent.stopPropagation() - if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; //Fix this too (hit slop) + if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; const adjusted = this.sliderThumbAdjuster(event) this.sliderMovement(adjusted, gestureState) }, From 4996df7d7d95007201b0a732c7e8a98320418fc2 Mon Sep 17 00:00:00 2001 From: dmacpro91 Date: Tue, 4 Jun 2024 13:00:57 -0400 Subject: [PATCH 3/5] Sync --- ColorPicker.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ColorPicker.js b/ColorPicker.js index 0673e02..40d9d58 100644 --- a/ColorPicker.js +++ b/ColorPicker.js @@ -269,6 +269,7 @@ module.exports = class ColorPicker extends Component { if (event && event.nativeEvent && typeof event.nativeEvent.preventDefault == 'function') event.nativeEvent.preventDefault() if (event && event.nativeEvent && typeof event.nativeEvent.stopPropagation == 'function') event.nativeEvent.stopPropagation() if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; + console.log(`x: ${event.nativeEvent.locationX}`) const adjusted = this.sliderThumbAdjuster(event) this.sliderMovement(adjusted, gestureState) }, @@ -422,7 +423,7 @@ module.exports = class ColorPicker extends Component { outOfBox(measure, gestureState) { const { x, y, width, height } = measure const { moveX, moveY, x0, y0 } = gestureState - // console.log(`${moveX} , ${moveY} / ${x} , ${y} / ${locationX} , ${locationY}`); + // console.log(`${moveX} , ${moveY} / ${x} , ${y} / `); return !(moveX >= x && moveX <= x + width && moveY >= y && moveY <= y + height) } outOfWheel(nativeEvent) { @@ -455,6 +456,16 @@ module.exports = class ColorPicker extends Component { locationY: row ? correctedTouch : event.nativeEvent.locationY, }} } + sliderMeasureAdjuster(hitSlop) { + const row = this.props.row + const {x, y, width, height} = this.sliderMeasure + return { + x: row ? x - hitSlop : x, + y: row ? y : y - hitSlop, + width: row ? width + hitSlop*2 : width, + height: row ? height : height + hitSlop*2, + } + } ratio(nativeEvent) { const row = this.props.row const loc = row ? nativeEvent.locationY : nativeEvent.locationX @@ -725,20 +736,20 @@ module.exports = class ColorPicker extends Component { } } {!swatchesOnly && !sliderHidden && (discrete - ? {this.disc} - : { + ? {this.disc} + : { const dimens = event.nativeEvent.layout; this.sliderXOffset = dimens.x; this.sliderYOffset = dimens.y; }}> - - - - {(this.props.sliderLoadingIndicator ? this.state.sliderImageLoaded : true) && } - { this.slider = r }}> - {!!this.props.sliderLoadingIndicator && !this.state.sliderImageLoaded && this.props.sliderLoadingIndicator} + + + + {(this.props.sliderLoadingIndicator ? this.state.sliderImageLoaded : true) && } + { this.slider = r }}> + {!!this.props.sliderLoadingIndicator && !this.state.sliderImageLoaded && this.props.sliderLoadingIndicator} + - )} {swatches && swatchesLast && {this.swatches}} From 3a84701585e7c61efbf8a22f1a670294104b291f Mon Sep 17 00:00:00 2001 From: dmacpro91 Date: Wed, 5 Jun 2024 12:48:45 -0400 Subject: [PATCH 4/5] HitSlop for Slider Made slider more user-friendly on mobile by continuing to read the touch events even when the touch is moved slightly (sliderHitSlop points) out of the bounds of the slider's box once started. outOfSlider is now unnecessary due to slider bounds being handled in sliderThumbAdjuster. There is a bug in the implementation of locationX on Android so using pageX (with appropriate padding/margin offset calculations) is more reliable. Still may need more padding/margin calculations for specific use-cases, but very usable currently. --- ColorPicker.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ColorPicker.js b/ColorPicker.js index 40d9d58..6f36013 100644 --- a/ColorPicker.js +++ b/ColorPicker.js @@ -180,6 +180,7 @@ module.exports = class ColorPicker extends Component { discrete: false, // use swatches of shades instead of slider discreteLength: 10, // number of swatches of shades, should be > 1 sliderHidden: false, // if true the slider is hidden + sliderHitSlop: undefined, //defines how far the touch event can move away from the slider once started swatches: true, // show color swatches swatchesLast: true, // if false swatches are shown before wheel swatchesOnly: false, // show swatch only and hide wheel and slider @@ -246,10 +247,9 @@ module.exports = class ColorPicker extends Component { onStartShouldSetPanResponderCapture: (event, gestureState) => { if (this.props.disabled) return false; const { nativeEvent } = event - if (this.outOfSlider(nativeEvent)) return const adjusted = this.sliderThumbAdjuster(event) this.sliderMovement(adjusted, gestureState) - this.updateValue({ nativeEvent }) + this.updateValue(adjusted) return true }, onStartShouldSetPanResponder: () => true, @@ -268,8 +268,7 @@ module.exports = class ColorPicker extends Component { if (this.props.disabled) return; if (event && event.nativeEvent && typeof event.nativeEvent.preventDefault == 'function') event.nativeEvent.preventDefault() if (event && event.nativeEvent && typeof event.nativeEvent.stopPropagation == 'function') event.nativeEvent.stopPropagation() - if (this.outOfSlider(event.nativeEvent) || this.outOfBox(this.sliderMeasure, gestureState)) return; - console.log(`x: ${event.nativeEvent.locationX}`) + if (this.outOfBox(this.sliderMeasureAdjuster(this.props.sliderHitSlop), gestureState)) return; const adjusted = this.sliderThumbAdjuster(event) this.sliderMovement(adjusted, gestureState) }, @@ -430,12 +429,6 @@ module.exports = class ColorPicker extends Component { const { radius } = this.polar(nativeEvent) return radius > 1 } - outOfSlider(nativeEvent) { - const row = this.props.row - const loc = row ? nativeEvent.locationY : nativeEvent.locationX - const { width, height } = this.sliderMeasure - return (loc > (row ? (height - width) + this.props.sliderSize/2 : (width - height) + this.props.sliderSize/2)) - } val(v) { const d = this.props.discrete, r = 11 * Math.round(v / 11) return d ? (r >= 99 ? 100 : r) : v @@ -443,8 +436,9 @@ module.exports = class ColorPicker extends Component { sliderThumbAdjuster(event) { const sliderThumbSize = this.props.sliderSize const row = this.props.row - let correctedTouch = row ? event.nativeEvent.locationY : event.nativeEvent.locationX + let correctedTouch = row ? event.nativeEvent.pageY : event.nativeEvent.pageX correctedTouch -= sliderThumbSize/2 + correctedTouch -= row ? this.sliderMeasure.y : this.sliderMeasure.x const min = row ? this.sliderYOffset : this.sliderXOffset const max = row ? this.sliderYOffset + this.sliderLength : this.sliderXOffset + this.sliderLength if(correctedTouch < min) From dee396f3aff3983ac022a59e5f9ca43351bf29a0 Mon Sep 17 00:00:00 2001 From: dmacpro91 Date: Tue, 11 Jun 2024 11:41:34 -0400 Subject: [PATCH 5/5] Updated README.md Corrected default value for sliderHitSlop. --- ColorPicker.js | 2 +- README.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ColorPicker.js b/ColorPicker.js index 6f36013..62d1c2e 100644 --- a/ColorPicker.js +++ b/ColorPicker.js @@ -180,7 +180,7 @@ module.exports = class ColorPicker extends Component { discrete: false, // use swatches of shades instead of slider discreteLength: 10, // number of swatches of shades, should be > 1 sliderHidden: false, // if true the slider is hidden - sliderHitSlop: undefined, //defines how far the touch event can move away from the slider once started + sliderHitSlop: 0, //defines how far the touch event can move away from the slider once started swatches: true, // show color swatches swatchesLast: true, // if false swatches are shown before wheel swatchesOnly: false, // show swatch only and hide wheel and slider diff --git a/README.md b/README.md index a739978..fe72343 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,9 @@ export default App `discreteLength: 10` number of swatches of shades, should be > 1 -`sliderHidden: false` if true the slider is hidden +`sliderHidden: false` if true the slider is hidden + +`sliderHitSlop: 0` defines how far the touch event can move away from the slider once started `swatches: true` show color swatches