I have a React application that is utilizing Material UI. The application has slider ponents implemented in a lot of places (). When using a touch screen device, I am unintentionally impacting slider ponents (the value is getting changed) while trying to scroll up or down the page. This does not happen with other page ponents, I suspect this is because sliders do (and should) respond to swipe events.
Material UI has documentation that implies a way to discern between a "scroll" and a "swipe" (.html#types-of-gestures). Is there a way for me to indicate to my slider ponents that a "scroll" should be ignored. Or, can I discern between a vertical or horizontal swipe, telling the slider to ignore vertical swipes?
I have a React application that is utilizing Material UI. The application has slider ponents implemented in a lot of places (https://material.io/ponents/sliders). When using a touch screen device, I am unintentionally impacting slider ponents (the value is getting changed) while trying to scroll up or down the page. This does not happen with other page ponents, I suspect this is because sliders do (and should) respond to swipe events.
Material UI has documentation that implies a way to discern between a "scroll" and a "swipe" (https://material.io/design/interaction/gestures.html#types-of-gestures). Is there a way for me to indicate to my slider ponents that a "scroll" should be ignored. Or, can I discern between a vertical or horizontal swipe, telling the slider to ignore vertical swipes?
Share Improve this question asked Nov 10, 2020 at 20:48 Jason GrossJason Gross 3101 silver badge11 bronze badges 3- I have the same question! – mvaldetaro Commented Nov 23, 2020 at 20:07
- 2 Supply code example of your current usage for more help. – morganney Commented Sep 17, 2021 at 13:31
- Does the Material UI page you linked to on sliders have the same undesired effect of changing the slider values as you scroll up and down on your mobile device? If not, then it is something with your implementation which you haven't shown. – morganney Commented Sep 17, 2021 at 17:08
2 Answers
Reset to default 3I have e up with a fairly elegant solution, I believe, which allows the user to scroll if their scroll position begins on the track but not on the thumbs. This replicates the native HTML range input so I feel that this is the best solution.
There's two parts to this
Step 1, allow touch-action
on the slider root element as it is disabled by default and prevents the user from starting a scroll on the slider
const useStyles = makeStyles({
sliderRoot: {
touchAction: "auto"
}
});
return (
<Slider
classes={{
root: classes.sliderRoot
}}
...
/>
Step 2, stop propagation on the root element with a ref
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
ref.current.addEventListener(
"touchstart",
(e) => {
const isThumb = e.target?.dataset.index;
if (!isThumb) {
e.stopPropagation();
}
},
{ capture: true }
);
}
});
return (
<Slider
ref={ref}
...
/>
And here is a fully working demo on Codesandbox.
There is not going to be a pletely clean solution to this other than telling your users to watch their finger placement when scrolling on mobile devices.
Using a controlled Slider, one approach would be to listen to the touchstart
event and record the current pageY
on the first changedTouches
object. Then pare that coordinate to the pageY
on the onChangeCommited
event handler for the corresponding touchmove
event. If the difference between the two coordinates is larger than some predefined range, then do not update the Slider value
.
Inside your ponent using the Slider
:
const delta = 50
const sliderRef = useRef(null)
const [value, setValue] = useState(0) // Or from some prop
const [touchStart, setTouchStart] = useState(0)
const debouncedHandler = useMemo(() => {
// Using lodash.debounce
return debounce((evt, value) => {
// If it is a mouse event then just update value as usual
if (evt instanceof MouseEvent) {
setValue(value)
}
}, 25)
}, [])
useLayoutEffect(() => {
if (sliderRef.current) {
sliderRef.current.addEventListener('touchstart', evt => {
setTouchStart(evt.changedTouches[0].pageY)
})
}
}, [])
return (
<Slider
value={value}
ref={sliderRef}
onChange={debouncedHandler}
onChangeCommitted={(evt, value) => {
if (evt instanceof TouchEvent) {
if (Math.abs(touchStart - evt.changedTouches[0].pageY) < delta) {
setValue(value)
}
} else {
setValue(value)
}
}}
/>
)
This will prevent the Slider from changing value
on TouchEvent
when the difference between the starting y-coordinate and the ending y-coordinate is larger than delta
. Adjust delta
to whatever value you like. The tradeoff is that you will not get as smooth of a transition when adjusting the Slider with a normal MouseEvent
(or TouchEvent
within the predefined range).
See the jsFiddle.
Or npm i mui-scrollable-slider-hook
and use it like
import Slider from '@mui/material/Slider'
import { useMuiScrollableSlider } from 'mui-scrollable-slider-hook'
const { ref, value, onChange, onChangeCommitted } = useMuiScrollableSlider()
return <Slider ref={ref} value={value} onChange={onChange} onChangeCommitted={onChangeCommitted} />
An example of using mui-scrollable-slider-hook
on codesandbox.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745171873a4614975.html
评论列表(0条)