Después de que Edge se unió a las valientes filas de los navegadores Chromium, la personalización de las barras de desplazamiento a través de CSS solo está ausente en Firefox. Esto es genial, pero aparte de Firefox, la solución CSS tiene muchas limitaciones. Mira qué magia negra tienes que usar para desvanecerte. Para obtener un control total sobre la apariencia, aún debe recurrir a JavaScript. Veamos cómo hacerlo a través del componente angular de una buena manera.

Magia de CSS
, , . -, . . scrollbar-width
, Firefox. Edge IE -ms-overflow-style
. IE , position: sticky
. Chrome Safari ::-webkit-scrollbar
.
-, , . , - position: fixed
. : .
position: sticky
display: flex
. :
<div class="bars">...</div>
<div class="content"><ng-content></ng-content></div>
. , - z-index: 1000, 1001, 9999
. , position: relative, z-index: 0
. - .
z-index: 1
, , 100%. :

, . . margin-right: -100%
, «» .
, , float
, 100%, (max-height, flex: 1
).
Angular
Angular, , . . . , . :
<div class="bars">
<div *ngIf="hasVerticalBar" class="bar">
<div
class="thumb"
[class.thumb_active]="verticalThumbActive"
[style.height.%]="verticalView"
[style.top.%]="verticalThumb"
></div>
</div>
</div>
:
//
get verticalScrolled(): number {
const {
scrollTop,
scrollHeight,
clientHeight
} = this.elementRef.nativeElement;
return scrollTop / (scrollHeight - clientHeight);
}
//
get verticalSize(): number {
const { clientHeight, scrollHeight } = this.elementRef.nativeElement;
return Math.ceil(clientHeight / scrollHeight * 100);
}
//
get verticalPosition(): number {
return this.verticalScrolled * (100 - this.verticalSize);
}
// ,
get hasVerticalBar(): boolean {
return this.verticalSize < 100;
}
, , — . . , .
mousedown
, mousemove
mouseup
.
:
<div
#vertical
class="thumb"
[class.thumb_active]="verticalThumbActive"
[style.height.%]="verticalSize"
[style.top.%]="verticalPosition"
(mousedown)="onVerticalStart($event)"
(document:mousemove)="onVerticalMove($event, vertical)"
></div>
mouseup
:
@HostListener('document:mouseup)
onDragEnd() {
this.verticalThumbActive = false;
}
onVerticalStart(event: MouseEvent) {
event.preventDefault();
const { target, clientY } = event;
const { top, height } = target.getBoundingClientRect();
this.verticalThumbDragOffset = (clientY - top) / height;
this.verticalThumbActive = true;
}
onVerticalMove(
{ clientY }: MouseEvent,
{ offsetHeight }: HTMLElement
) {
if (!this.verticalThumbActive) {
return;
}
const { nativeElement } = this.elementRef;
const { top, height } = nativeElement.getBoundingClientRect();
const maxScrollTop = nativeElement.scrollHeight - height;
const scrolled =
(clientY - top - offsetHeight * this.verticalThumbDragOffset) /
(height - offsetHeight);
nativeElement.scrollTop = maxScrollTop * scrolled;
}
.
Angular
, . mousemove
. . , Angular , . @tinkoff/ng-event-plugins, . .silent
@shouldCall
:
(document:mousemove.silent)="onVerticalMove($event, vertical)"
@shouldCall(isActive)
@HostListener('init.end', ['$event'])
@HostListener('document:mouseup.silent')
onDragEnd() {
this.verticalThumbActive = false;
}
@shouldCall(isActive)
@HostListener('init.move', ['$event'])
onVerticalMove(
{ clientY }: MouseEvent,
{ offsetHeight }: HTMLElement
) {
const { nativeElement } = this.elementRef;
const { top, height } = nativeElement.getBoundingClientRect();
const maxScrollTop = nativeElement.scrollHeight - height;
const scrolled =
(clientY - top - offsetHeight * this.verticalThumbDragOffset) /
(height - offsetHeight);
nativeElement.scrollTop = maxScrollTop * scrolled;
}
: Angular 10 markDirty(this)
@shouldCall
@HostListener(‘init.xxx’, [‘$event’])
, , — .
, . . , . ResizeObserver
@ng-web-apis/resize-observer, . Stackblitz.
Edit:
, , . . Output fromEvent, :
@Output()
dragged = fromEvent(
this.elementRef.nativeElement,
'mousedown'
).pipe(
switchMap(event => {
event.preventDefault();
const clientRect = event.target.getBoundingClientRect();
const offsetVertical = getOffsetVertical(event, clientRect);
const offsetHorizontal = getOffsetHorizontal(event, clientRect);
return fromEvent(this.documentRef, 'mousemove').pipe(
map(event => this.getScrolled(event, offsetVertical, offsetHorizontal)),
takeUntil(fromEvent(this.documentRef, 'mouseup'))
);
})
);
. , .