Skip to content

Commit

Permalink
perf: throttle mousemove listener events
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Lewis committed Mar 23, 2017
1 parent 084c023 commit 05f7f7e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 30 deletions.
19 changes: 14 additions & 5 deletions src/resizable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import {
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import {merge} from 'rxjs/observable/merge';
import {interval} from 'rxjs/observable/interval';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/pairwise';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/throttle';
import 'rxjs/add/operator/share';
import {ResizeHandle} from './resizeHandle.directive';
import {Edges} from './interfaces/edges.interface';
import {BoundingRectangle} from './interfaces/boundingRectangle.interface';
Expand Down Expand Up @@ -185,6 +188,8 @@ const RESIZE_RIGHT_HOVER_CLASS: string = 'resize-right-hover';
const RESIZE_TOP_HOVER_CLASS: string = 'resize-top-hover';
const RESIZE_BOTTOM_HOVER_CLASS: string = 'resize-bottom-hover';

export const MOUSE_MOVE_THROTTLE_MS: number = 50;

/**
* Place this on an element to make it resizable
*
Expand Down Expand Up @@ -295,11 +300,15 @@ export class Resizable implements OnInit, OnDestroy, AfterViewInit {
}
};

this.mousemove.subscribe(({mouseX, mouseY, event}) => {
const mouseMove: Observable<any> = this.mousemove.share();

if (currentResize) {
mouseMove
.filter(() => !!currentResize)
.subscribe(({event}) => {
event.preventDefault();
}
});

mouseMove.throttle(val => interval(MOUSE_MOVE_THROTTLE_MS)).subscribe(({mouseX, mouseY}) => {

const resizeEdges: Edges = getResizeEdges({
mouseX, mouseY,
Expand Down Expand Up @@ -356,8 +365,8 @@ export class Resizable implements OnInit, OnDestroy, AfterViewInit {
};

return merge(
this.mousemove.take(1).map(coords => [, coords]),
this.mousemove.pairwise()
mouseMove.take(1).map(coords => [, coords]),
mouseMove.pairwise()
).map(([previousCoords, newCoords]) => {
return [previousCoords ? getDiff(previousCoords) : previousCoords, getDiff(newCoords)];
}).filter(([previousCoords, newCoords]) => {
Expand Down
69 changes: 44 additions & 25 deletions test/resizable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Component, ViewChild} from '@angular/core';
import {Resizable} from '../src/resizable.directive';
import {Edges} from '../src/interfaces/edges.interface';
import {ResizeEvent, ResizableModule} from './../src';
import {MOUSE_MOVE_THROTTLE_MS} from '../src/resizable.directive';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {expect} from 'chai';
import * as sinon from 'sinon';
Expand Down Expand Up @@ -90,7 +91,7 @@ describe('resizable directive', () => {

describe('cursor changes', () => {

let assertions: Array<Object>;
let assertions: Array<any>;

it('should change the cursor to the ns-resize when mousing over the top edge', () => {
assertions = [{
Expand Down Expand Up @@ -188,13 +189,25 @@ describe('resizable directive', () => {
}];
});

afterEach(() => {
afterEach(done => {
let count: number = 0;
const fixture: ComponentFixture<TestCmp> = createComponent();
const elm: HTMLElement = fixture.componentInstance.resizable.elm.nativeElement;
assertions.forEach(({coords, cursor}: {coords: Object, cursor: string}) => {
triggerDomEvent('mousemove', elm, coords);
expect(elm.style.cursor).to.equal(cursor);
});

function runAssertion(): void {
if (count === assertions.length) {
done();
} else {
const {coords, cursor} = assertions[count];
triggerDomEvent('mousemove', elm, coords);
expect(elm.style.cursor).to.equal(cursor);
count++;
setTimeout(runAssertion, MOUSE_MOVE_THROTTLE_MS);
}
}

runAssertion();

});

});
Expand Down Expand Up @@ -1038,7 +1051,7 @@ describe('resizable directive', () => {

});

it('should set the resize active class', () => {
it('should set the resize active class', done => {

const fixture: ComponentFixture<TestCmp> = createComponent();
fixture.detectChanges();
Expand All @@ -1048,24 +1061,27 @@ describe('resizable directive', () => {
clientY: 210
});
expect(elm.classList.contains('resize-active')).to.be.false;
triggerDomEvent('mousedown', elm, {
clientX: 100,
clientY: 210
});
triggerDomEvent('mousemove', elm, {
clientX: 101,
clientY: 210
});
expect(elm.classList.contains('resize-active')).to.be.true;
triggerDomEvent('mouseup', elm, {
clientX: 101,
clientY: 210
});
expect(elm.classList.contains('resize-active')).to.be.false;
setTimeout(() => {
triggerDomEvent('mousedown', elm, {
clientX: 100,
clientY: 210
});
triggerDomEvent('mousemove', elm, {
clientX: 101,
clientY: 210
});
expect(elm.classList.contains('resize-active')).to.be.true;
triggerDomEvent('mouseup', elm, {
clientX: 101,
clientY: 210
});
expect(elm.classList.contains('resize-active')).to.be.false;
done();
}, MOUSE_MOVE_THROTTLE_MS);

});

it('should set the resize edge classes', () => {
it('should set the resize edge classes', done => {

const fixture: ComponentFixture<TestCmp> = createComponent();
fixture.detectChanges();
Expand All @@ -1075,9 +1091,12 @@ describe('resizable directive', () => {
expect(elm.classList.contains('resize-top-hover')).to.be.false;
expect(elm.classList.contains('resize-right-hover')).to.be.false;
expect(elm.classList.contains('resize-bottom-hover')).to.be.false;
triggerDomEvent('mousemove', elm, {clientX: 50, clientY: 300});
expect(elm.classList.contains('resize-left-hover')).to.be.false;
fixture.destroy();
setTimeout(() => {
triggerDomEvent('mousemove', elm, {clientX: 50, clientY: 300});
expect(elm.classList.contains('resize-left-hover')).to.be.false;
fixture.destroy();
done();
}, MOUSE_MOVE_THROTTLE_MS);

});

Expand Down

0 comments on commit 05f7f7e

Please sign in to comment.