可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a list, with the overflow-x
and overflow-y
set to auto
. In addition, I've set up momentum scroll, so the touch scrolling works nice in mobile, using webkit-overflow-scrolling: true
.
The issue, however, is that I cannot figure out how to disable the horizontal scroll when scrolling vertically. It leads to really bad user experience, as the swiping towards the top left or top right will cause the table to scroll diagonally. When the user is scrolling vertically, I absolutely do NOT want any scrolling horizontally until the user has stopped scrolling vertically.
I've tried the following:
JS:
offsetX: number;
offsetY: number;
isScrollingHorizontally = false;
isScrollingVertically = false;
//Detect the scrolling events
ngOnInit() {
this.scrollListener = this.renderer.listen(
this.taskRows.nativeElement,
'scroll',
evt => {
this.didScroll();
}
);
fromEvent(this.taskRows.nativeElement, 'scroll')
.pipe(
debounceTime(100),
takeUntil(this.destroy$)
)
.subscribe(() => {
this.endScroll();
});
}
didScroll() {
if ((this.taskRows.nativeElement.scrollLeft != this.offsetX) && (!this.isScrollingHorizontally)){
console.log("Scrolling horizontally")
this.isScrollingHorizontally = true;
this.isScrollingVertically = false;
this.changeDetectorRef.markForCheck();
}else if ((this.taskRows.nativeElement.scrollTop != this.offsetY) && (!this.isScrollingVertically)) {
console.log("Scrolling Vertically")
this.isScrollingHorizontally = false;
this.isScrollingVertically = true;
this.changeDetectorRef.markForCheck();
}
}
endScroll() {
console.log("Ended scroll")
this.isScrollingVertically = false;
this.isScrollingHorizontally = false;
this.changeDetectorRef.markForCheck();
}
HTML:
<div
class="cu-dashboard-table__scroll"
[class.cu-dashboard-table__scroll_disable-x]="isScrollingVertically"
[class.cu-dashboard-table__scroll_disable-y]="isScrollingHorizontally"
>
CSS:
&__scroll {
display: flex;
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: auto;
will-change: transform;
-webkit-overflow-scrolling: touch;
&_disable-x {
overflow-x: hidden;
}
&_disable-y {
overflow-y: hidden;
}
}
But the everytime I set overflow-x
or overflow-y
to hidden
when its been scrolled, scrolling will glitch and jump back to the top. I've also noticed that webkit-overflow-scrolling: true
is the reason why this occurs, when I remove it, the behavior seems to stop, but I absolutely need this for momentum scrolling in mobile devices.
How do I disable horizontal scroll when scrolling vertically?
回答1:
Try this
HTML
<div
class="cu-dashboard-table__scroll-vertical"
>
<div
class="cu-dashboard-table__scroll-horizontal"
>
<!-- Scroll content here -->
</div>
</div>
CSS
&__scroll {
&-horizontal {
overflow-x: auto;
width: 100%;
-webkit-overflow-scrolling: touch;
}
&-vertical {
overflow-y: auto;
height: 100%;
-webkit-overflow-scrolling: touch;
}
}
Instead of using one div for scrolling, why dont you use two? Have one for X and one for Y
回答2:
It's generally a bad practice for your design to need multiaxis scrolling on mobile, unless maybe you're showing big tables of data. That being said, why do you want to prevent it? If a user wants to scroll diagonally, that doesn't seem like the end of the world to me. Some browsers, like Chrome on OSX, already do what you're describing by default.
If you must have single-axis scrolling, a possible solution might be to keep track of the scroll position yourself via touchstart
and touchmove
events. If you set your drag threshold lower than the browser's, you may be able to do your css stuff before it starts scrolling, avoiding the perceived glitch. Also, even if it does still glitch, you have the touch start and the touch's current location. From these, if you record your div's starting scroll position, you can manually scroll the div to the correct place to counteract it jumping to the top if you have to. A possible algorithm might look like this:
// Touchstart handler
if (scrollState === ScrollState.Default) {
// Record position and id of touch
touchStart = touch.location
touchStartId = touch.id.
scrollState = ScrollState.Touching
// If you have to manually scroll the div, first store its starting scroll position:
containerTop = $('.myContainer').scrollTop();
containerLeft = $('.myContainer').scrollLeft();
}
// Touchmove handler - If the user has dragged beyond a distance threshold,
// do the css classes swap.
if (touch.id === touchStartId && distance(touchStart, touch.location > threshold) {
scrollState = ScrollState.Scrolling;
swapCssClasses();
// If you have to manually scroll the div to prevent jump:
$('.myContainer').scrollTop(containerTop + (touch.location.y - touchStart.y));
// Do the same for horizontal scroll.
}
// Then, listen for debounced scroll events, like you're already doing,
// to reset your state back to default.
Second idea: in your scroll handler, instead of changing the css classes, set the scroll position of the div directly for the axis you want locked. IE, if you're scrolling horizontally, always set the scrollTop to its starting value. This might also cancel scrolling, not sure. You'd have to try it to see if it works.
回答3:
There are numerous ways to try to solve this. Some good ideas are offered here.
However, as others have alluded, relying on (or trying to avoid) multi-dimensional scrolling is a bad UX smell - indicative of the UX being a problem. I don't think this is a legitimate dev issue. It would be better to take a step back and reevaluate what you're trying to accomplish. One of the issues might be that in an effort to make the UI more usable, you'll be confusing users. The usability described here would likely cause confusion.
Why can't I scroll horizontally?
Why when I stop scrolling vertically, only then can I scroll horizontally?
These are some questions that may be asked (inaudible).
If you're trying to allow for additional information of the vertical data list to be browsable only when the user has selected the row, it would likely be much better to simply have a flat list by default only scrollable vertically, and only toggle the vertical information when the "row" has been selected/activated. Collapsing the details would get you back to the flat vertical list.
If you're having to jump through hoops to solve such a fringe technical challenge, it's a good indication that the user experience has not been designed well to begin with.
回答4:
Need to use three containers.
In the first container I enable vertical scrolling and disallow horizontal.
In the second, vice versa, I enable horizontal scrolling and disallow vertical.
Be sure to use overflow-x: hidden;
and overflow-y: hidden;
, otherwise child containers may go beyond the current container.
Also need to use min-width: 100%; min-height: 100%;
.
For the third container we need to use display: inline-block;
and then the internal content will stretch this container and the corresponding scroll bars will appear on the two parent blocks.
HTML
<div class="scroll-y">
<div class="scroll-x">
<div class="content-container">
<!-- scrollable content here -->
</div>
</div>
</div>
CSS
.scroll-y {
position: absolute;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
}
.scroll-x {
overflow-y: hidden;
overflow-x: auto;
width: auto;
min-width: 100%;
min-height: 100%;
}
.content-container {
min-width: 100%;
min-height: 100%;
display: inline-block;
}
You can test it here in Safari on iPhone.
Good luck!!