Skip to content

Commit

Permalink
perf(module:resizable): listen document events when resizing start (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
hsuanxyz authored Sep 9, 2019
1 parent ef39aa1 commit 66afcf0
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 71 deletions.
3 changes: 1 addition & 2 deletions components/core/testing/event-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ export function createTouchEvent(type: string, pageX: number = 0, pageY: number
// In favor of creating events that work for most of the browsers, the event is created
// as a basic UI Event. The necessary details for the event will be set manually.
const event = document.createEvent('UIEvent');
const touchDetails = { pageX, pageY };

const touchDetails = { pageX, pageY, clientX: pageX, clientY: pageY };
event.initUIEvent(type, true, true, window, 0);

// Most of the browsers don't have a "initTouchEvent" method that can be used to define
Expand Down
12 changes: 12 additions & 0 deletions components/resizable/nz-resizable-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @license
* Copyright Alibaba.com All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
import { isTouchEvent } from 'ng-zorro-antd/core';

export function getEventWithPoint(event: MouseEvent | TouchEvent): MouseEvent | Touch {
return isTouchEvent(event) ? event.touches[0] || event.changedTouches[0] : (event as MouseEvent);
}
82 changes: 42 additions & 40 deletions components/resizable/nz-resizable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
Directive,
ElementRef,
EventEmitter,
HostListener,
Input,
NgZone,
OnDestroy,
Expand All @@ -24,14 +23,15 @@ import { ensureInBounds, InputBoolean } from 'ng-zorro-antd/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { getEventWithPoint } from './nz-resizable-utils';
import { NzResizableService } from './nz-resizable.service';
import { NzResizeHandleMouseDownEvent } from './nz-resize-handle.component';

export interface NzResizeEvent {
width?: number;
height?: number;
col?: number;
mouseEvent?: MouseEvent;
mouseEvent?: MouseEvent | TouchEvent;
}

@Directive({
Expand Down Expand Up @@ -62,7 +62,7 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {

resizing = false;
private elRect: ClientRect | DOMRect;
private currentHandleEvent: NzResizeHandleMouseDownEvent;
private currentHandleEvent: NzResizeHandleMouseDownEvent | null;
private ghostElement: HTMLDivElement | null;
private el: HTMLElement;
private sizeCache: NzResizeEvent | null;
Expand All @@ -71,37 +71,32 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
constructor(
private elementRef: ElementRef<HTMLElement>,
private renderer: Renderer2,
private ngZone: NgZone,
private nzResizableService: NzResizableService,
private platform: Platform
private platform: Platform,
private ngZone: NgZone
) {
this.nzResizableService.handleMouseDown$.pipe(takeUntil(this.destroy$)).subscribe(event => {
this.resizing = true;
this.nzResizableService.startResizing(event.mouseEvent);
this.currentHandleEvent = event;
this.setCursor();
this.nzResizeStart.emit({
mouseEvent: event.mouseEvent
});
this.elRect = this.el.getBoundingClientRect();
});
}

@HostListener('document:mouseup', ['$event'])
onMouseup($event: MouseEvent): void {
this.ngZone.runOutsideAngular(() => {
this.nzResizableService.documentMouseUp$.pipe(takeUntil(this.destroy$)).subscribe(event => {
if (this.resizing) {
this.resizing = false;
this.nzResizableService.documentMouseUp$.next();
this.endResize($event);
this.endResize(event);
}
});
}

@HostListener('document:mousemove', ['$event'])
onMousemove($event: MouseEvent): void {
this.ngZone.runOutsideAngular(() => {
this.nzResizableService.documentMouseMove$.pipe(takeUntil(this.destroy$)).subscribe(event => {
if (this.resizing) {
this.resize($event);
this.resize(event);
}
});
}
Expand All @@ -124,11 +119,11 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
calcSize(width: number, height: number, ratio: number): NzResizeEvent {
let newWidth: number;
let newHeight: number;
let maxWidth: number;
let maxHeight: number;
let col = 0;
let spanWidth = 0;
let maxWidth = Infinity;
let minWidth = this.nzMinWidth;
let maxHeight = Infinity;
let boundWidth = Infinity;
let boundHeight = Infinity;
if (this.nzBounds === 'parent') {
Expand Down Expand Up @@ -159,7 +154,7 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
}

if (ratio !== -1) {
if (/(left|right)/i.test(this.currentHandleEvent.direction)) {
if (/(left|right)/i.test(this.currentHandleEvent!.direction)) {
newWidth = Math.min(Math.max(width, minWidth), maxWidth);
newHeight = Math.min(Math.max(newWidth / ratio, this.nzMinHeight), maxHeight);
if (newHeight >= maxHeight || newHeight <= this.nzMinHeight) {
Expand Down Expand Up @@ -190,7 +185,7 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
}

setCursor(): void {
switch (this.currentHandleEvent.direction) {
switch (this.currentHandleEvent!.direction) {
case 'left':
case 'right':
this.renderer.setStyle(document.body, 'cursor', 'col-resize');
Expand All @@ -211,52 +206,56 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
this.renderer.setStyle(document.body, 'user-select', 'none');
}

resize($event: MouseEvent): void {
resize(event: MouseEvent | TouchEvent): void {
const elRect = this.elRect;
const resizeEvent = getEventWithPoint(event);
const handleEvent = getEventWithPoint(this.currentHandleEvent!.mouseEvent);
let width = elRect.width;
let height = elRect.height;
const ratio = this.nzLockAspectRatio ? width / height : -1;
switch (this.currentHandleEvent.direction) {
switch (this.currentHandleEvent!.direction) {
case 'bottomRight':
width = $event.clientX - elRect.left;
height = $event.clientY - elRect.top;
width = resizeEvent.clientX - elRect.left;
height = resizeEvent.clientY - elRect.top;
break;
case 'bottomLeft':
width = elRect.width + this.currentHandleEvent.mouseEvent.clientX - $event.clientX;
height = $event.clientY - elRect.top;
width = elRect.width + handleEvent.clientX - resizeEvent.clientX;
height = resizeEvent.clientY - elRect.top;
break;
case 'topRight':
width = $event.clientX - elRect.left;
height = elRect.height + this.currentHandleEvent.mouseEvent.clientY - $event.clientY;
width = resizeEvent.clientX - elRect.left;
height = elRect.height + handleEvent.clientY - resizeEvent.clientY;
break;
case 'topLeft':
width = elRect.width + this.currentHandleEvent.mouseEvent.clientX - $event.clientX;
height = elRect.height + this.currentHandleEvent.mouseEvent.clientY - $event.clientY;
width = elRect.width + handleEvent.clientX - resizeEvent.clientX;
height = elRect.height + handleEvent.clientY - resizeEvent.clientY;
break;
case 'top':
height = elRect.height + this.currentHandleEvent.mouseEvent.clientY - $event.clientY;
height = elRect.height + handleEvent.clientY - resizeEvent.clientY;
break;
case 'right':
width = $event.clientX - elRect.left;
width = resizeEvent.clientX - elRect.left;
break;
case 'bottom':
height = $event.clientY - elRect.top;
height = resizeEvent.clientY - elRect.top;
break;
case 'left':
width = elRect.width + this.currentHandleEvent.mouseEvent.clientX - $event.clientX;
width = elRect.width + handleEvent.clientX - resizeEvent.clientX;
}
const size = this.calcSize(width, height, ratio);
this.sizeCache = { ...size };
this.nzResize.emit({
...size,
mouseEvent: $event
this.ngZone.run(() => {
this.nzResize.emit({
...size,
mouseEvent: event
});
});
if (this.nzPreview) {
this.previewResize(size);
}
}

endResize($event: MouseEvent): void {
endResize(event: MouseEvent | TouchEvent): void {
this.renderer.setStyle(document.body, 'cursor', '');
this.renderer.setStyle(document.body, 'user-select', '');
this.removeGhostElement();
Expand All @@ -266,11 +265,14 @@ export class NzResizableDirective implements AfterViewInit, OnDestroy {
width: this.elRect.width,
height: this.elRect.height
};
this.nzResizeEnd.emit({
...size,
mouseEvent: $event
this.ngZone.run(() => {
this.nzResizeEnd.emit({
...size,
mouseEvent: event
});
});
this.sizeCache = null;
this.currentHandleEvent = null;
}

previewResize({ width, height }: NzResizeEvent): void {
Expand Down
47 changes: 44 additions & 3 deletions components/resizable/nz-resizable.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,64 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { Injectable, OnDestroy } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, NgZone, OnDestroy } from '@angular/core';

import { Subject } from 'rxjs';

import { isTouchEvent } from 'ng-zorro-antd/core';
import { NzResizeHandleMouseDownEvent } from './nz-resize-handle.component';

@Injectable()
export class NzResizableService implements OnDestroy {
private document: Document;
private listeners = new Map<string, (event: MouseEvent | TouchEvent) => void>();

handleMouseDown$ = new Subject<NzResizeHandleMouseDownEvent>();
documentMouseUp$ = new Subject<void>();
documentMouseUp$ = new Subject<MouseEvent | TouchEvent>();
documentMouseMove$ = new Subject<MouseEvent | TouchEvent>();
mouseEntered$ = new Subject<boolean>();

constructor() {}
// tslint:disable-next-line:no-any
constructor(private ngZone: NgZone, @Inject(DOCUMENT) document: any) {
this.document = document;
}

startResizing(event: MouseEvent | TouchEvent): void {
const _isTouchEvent = isTouchEvent(event);
this.clearListeners();
const moveEvent = _isTouchEvent ? 'touchmove' : 'mousemove';
const upEvent = _isTouchEvent ? 'touchend' : 'mouseup';
const moveEventHandler = (e: MouseEvent | TouchEvent) => {
this.documentMouseMove$.next(e);
};
const upEventHandler = (e: MouseEvent | TouchEvent) => {
this.documentMouseUp$.next(e);
this.clearListeners();
};

this.listeners.set(moveEvent, moveEventHandler);
this.listeners.set(upEvent, upEventHandler);

this.ngZone.runOutsideAngular(() => {
this.listeners.forEach((handler, name) => {
this.document.addEventListener(name, handler as EventListener);
});
});
}

private clearListeners(): void {
this.listeners.forEach((handler, name) => {
this.document.removeEventListener(name, handler as EventListener);
});
this.listeners.clear();
}

ngOnDestroy(): void {
this.handleMouseDown$.complete();
this.documentMouseUp$.complete();
this.documentMouseMove$.complete();
this.mouseEntered$.complete();
this.clearListeners();
}
}
Loading

0 comments on commit 66afcf0

Please sign in to comment.