我正在使用以下应用程序:即时消息使用来自角材料cdk和perfect-scroll-bar.js的拖放(以更改容器中的滚动条),该应用程序需要支持触摸设备,而我在这里拖动事件和滚动事件的问题,我有一个包含2个项目列表的容器,一个容器有一个max-height
和overflow: hidden
以在内容溢出的情况下显示滚动条,这些项目具有拖放功能来自cdk的指令,现在的问题是,在触摸设备中,当我尝试拖动元素时,容器会尝试同时滚动,同时拖动元素,移动列表并忽略元素i的位置拖着脚,有什么办法可以避免这种行为。
Todo-template.html
<div class="detail-content-wrapper" perfecScrollbar>
<div class="todo-list" cdkDropList #openList="cdkDropList" (cdkDropListDropped)="drop($event)">
<div class="list-item" *ngFor="let todo of openTaskList; let i = index" [attr.position]="todo.position"
cdkDrag>
<div class="dd-placeholder" *cdkDragPlaceholder></div>
<div class="state"></div>
<div class="todo-text">
<span class="task-text"> {{ todo.name }} </span>
<input name="editToDoInput" class="form-control" value="{{todo.name}}" #editToDoInput />
</div>
<i class="fa fa-ellipsis-v drag-indicator"></i>
<span class="icon-hype-close remove-icon" (click)="deleteTaskItem($event, todo, openTaskList, i)"></span>
</div>
</div>
</div>
</div>
Perfec滚动指令
import { HypeConfigService } from './../services/config.service';
import { AfterViewInit, Directive, ElementRef, HostListener, OnDestroy, Input, Renderer2 } from '@angular/core';
import PerfectScrollbar from 'perfect-scrollbar';
@Directive({
selector: '[perfectScrollbar]'
})
export class PerfectScrollbarDirective implements AfterViewInit, OnDestroy {
isInitialized = true;
pscrollbar: PerfectScrollbar;
@Input() column: boolean = false;
constructor(public element: ElementRef, private _render: Renderer2, private configService: HypeConfigService) {}
ngAfterViewInit() {
// Initialize the perfect-scrollbar
this.pscrollbar = new PerfectScrollbar(this.element.nativeElement, {
wheelPropagation: true,
wheelSpeed: 2,
suppressScrollX: this.column ? true : false
});
if (this.column) {
if (this.configService.appConfig.IsUsingInternetExplorer || this.configService.appConfig.IsUsingMsEdge) {
this.element.nativeElement.style.paddingRight = '0px';
this.element.nativeElement.style.marginRight = '0px';
} else {
this.element.nativeElement.style.paddingRight = '18px';
this.element.nativeElement.style.marginRight = this.configService.appConfig.IsUsingChrome ? '-18px' : '-24px';
}
}
}
ngOnDestroy() {
if (!this.isInitialized || !this.pscrollbar) {
return;
}
// Destroy the perfect-scrollbar
this.pscrollbar.destroy();
}
@HostListener('document:click', ['$event'])
documentClick(event: Event): void {
if (!this.isInitialized || !this.pscrollbar) {
return;
}
// Update the scrollbar on document click..
// This isn't the most elegant solution but there is no other way
// of knowing when the contents of the scrollable container changes.
// Therefore, we update scrollbars on every document click.
this.pscrollbar.update();
}
@HostListener('scroll', ['$event'])
scrolling(event) {
let element = this.element.nativeElement as HTMLElement;
// A kanban column is the scrollable element
if (element.attributes && element.attributes.getNamedItem('data-columnId')) {
let parentElement = element.parentElement.parentElement;
if (element.scrollTop > 0) {
// If the user scrolls in a kanban column the element with css-class 'kanban-column' gets css-class 'scrolled'
this._render.addClass(parentElement, 'scrolled');
} else {
// If scroll position is 0 (Zero) then remove the css-class on kanban column element
if (parentElement && parentElement.className.indexOf('scrolled') > -1) {
this._render.removeClass(parentElement, 'scrolled');
}
}
} else {
let parentElement = element.parentElement;
if (element.scrollTop > 0) {
// If the user scrolls in a kanban column the element with css-class 'kanban-column' gets css-class 'scrolled'
this._render.addClass(parentElement, 'scrolled');
} else {
// If scroll position is 0 (Zero) then remove the css-class on kanban column element
if (parentElement && parentElement.className.indexOf('scrolled') > -1) {
this._render.removeClass(parentElement, 'scrolled');
}
}
}
}
update() {
if (!this.isInitialized) {
return;
}
// Update the perfect-scrollbar
this.pscrollbar.update();
}
destroy() {
this.ngOnDestroy();
}
scrollToX(x: number, speed?: number) {
this.animateScrolling('scrollLeft', x, speed);
}
scrollToY(y: number, speed?: number) {
this.animateScrolling('scrollTop', y, speed);
}
scrollToTop(offset?: number, speed?: number) {
this.animateScrolling('scrollTop', offset || 0, speed);
}
scrollToLeft(offset?: number, speed?: number) {
this.animateScrolling('scrollLeft', offset || 0, speed);
}
scrollToRight(offset?: number, speed?: number) {
const width = this.element.nativeElement.scrollWidth;
this.animateScrolling('scrollLeft', width - (offset || 0), speed);
}
scrollToBottom(offset?: number, speed?: number) {
const height = this.element.nativeElement.scrollHeight;
this.animateScrolling('scrollTop', height - (offset || 0), speed);
}
animateScrolling(target: string, value: number, speed?: number) {
if (!speed) {
this.element.nativeElement[target] = value;
// PS has weird event sending order, this is a workaround for that
this.update();
this.update();
} else if (value !== this.element.nativeElement[target]) {
let newValue = 0;
let scrollCount = 0;
let oldTimestamp = performance.now();
let oldValue = this.element.nativeElement[target];
const cosParameter = (oldValue - value) / 2;
const step = (newTimestamp) => {
scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));
newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));
// Only continue animation if scroll position has not changed
if (this.element.nativeElement[target] === oldValue) {
if (scrollCount >= Math.PI) {
this.element.nativeElement[target] = value;
// PS has weird event sending order, this is a workaround for that
this.update();
this.update();
} else {
this.element.nativeElement[target] = oldValue = newValue;
oldTimestamp = newTimestamp;
window.requestAnimationFrame(step);
}
}
};
window.requestAnimationFrame(step);
}
}
}