diff --git a/packages/board/examples/features/index.html b/packages/board/examples/features/index.html index 8a14754..8b4a93b 100644 --- a/packages/board/examples/features/index.html +++ b/packages/board/examples/features/index.html @@ -11,7 +11,11 @@ - + + + \ No newline at end of file diff --git a/packages/board/examples/features/main.html b/packages/board/examples/features/main.html index fc8c377..9f1b15a 100644 --- a/packages/board/examples/features/main.html +++ b/packages/board/examples/features/main.html @@ -3,6 +3,14 @@ @@ -52,5 +61,11 @@ + \ No newline at end of file diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 6d21a0a..526cf21 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -2,7 +2,8 @@ import { TypeScreenPosition, TypeScreenSize, TypeScreenContext, TypePoint, TypePointCursor, TypeBoardOptions, TypeBoardSizeOptions, } from '@idraw/types'; import util from '@idraw/util'; -import { Watcher } from './lib/watcher'; +// import { Watcher } from './lib/watcher'; +import { ScreenWatcher } from './lib/screen-watcher'; import { setStyle } from './lib/style'; import Context from './lib/context'; import { TypeBoardEventArgMap } from './lib/event'; @@ -31,7 +32,8 @@ class Board { private [_ctx]: Context; private [_displayCtx]: CanvasRenderingContext2D; private [_originCtx]: CanvasRenderingContext2D; - private [_watcher]: Watcher; + // private [_watcher]: Watcher; + private [_watcher]: ScreenWatcher; private [_scroller]: Scroller; private [_screen]: Screen; @@ -45,7 +47,8 @@ class Board { this[_displayCtx] = this[_displayCanvas].getContext('2d') as CanvasRenderingContext2D; this[_ctx] = new Context(this[_originCtx], this[_opts]); this[_screen] = new Screen(this[_ctx], this[_opts]); - this[_watcher] = new Watcher(this[_displayCanvas]); + // this[_watcher] = new Watcher(this[_displayCanvas]); + this[_watcher] = new ScreenWatcher(this[_displayCanvas]); this[_scroller] = new Scroller( this[_displayCtx], { width: opts.width, @@ -109,7 +112,8 @@ class Board { } clear() { - this[_displayCtx].clearRect(0, 0, this[_displayCanvas].width, this[_displayCanvas].height); + this[_displayCtx].clearRect(0, 0, this[_canvas].width, this[_canvas].height) + // this[_displayCtx].clearRect(0, 0, this[_displayCanvas].width, this[_displayCanvas].height); } on(name: T, callback: (p: TypeBoardEventArgMap[T]) => void) { diff --git a/packages/board/src/lib/screen-watcher.ts b/packages/board/src/lib/screen-watcher.ts new file mode 100644 index 0000000..cb46eef --- /dev/null +++ b/packages/board/src/lib/screen-watcher.ts @@ -0,0 +1,168 @@ +import { TypePoint } from '@idraw/types'; +import { BoardEvent, TypeBoardEventArgMap } from './event'; +import { TempData } from './watcher-temp'; + +// const isInIframe = window.self === window.top; + +export class ScreenWatcher { + + private _canvas: HTMLCanvasElement; + private _isMoving = false; + // private _onMove?: TypeWatchCallback; + // private _onMoveStart?: TypeWatchCallback; + // private _onMoveEnd?: TypeWatchCallback; + private _event: BoardEvent; + private _temp: TempData = new TempData; + private _container: HTMLElement | Window = window; + + constructor(canvas: HTMLCanvasElement) { + this._canvas = canvas; + this._isMoving = false; + this._initEvent(); + this._event = new BoardEvent; + } + + on(name: T, callback: (p: TypeBoardEventArgMap[T]) => void): void { + this._event.on(name, callback); + } + + off(name: T, callback: (p: TypeBoardEventArgMap[T]) => void): void { + this._event.off(name, callback); + } + + _initEvent(): void { + const canvas = this._canvas; + const container = this._container; + container.addEventListener('mousemove', this._listenHover.bind(this), true); + container.addEventListener('mousedown', this._listenMoveStart.bind(this), true); + container.addEventListener('mousemove', this._listenMove.bind(this), true); + container.addEventListener('mouseup', this._listenMoveEnd.bind(this), true); + container.addEventListener('mouseleave', this._listenMoveEnd.bind(this), true); + container.addEventListener('mouseleave', this._listenLeave.bind(this), true); + container.addEventListener('click', this._listenClick.bind(this), true); + + canvas.addEventListener('wheel', this._listenWheel.bind(this), true); + + container.addEventListener('touchstart', this._listenMoveStart.bind(this), true); + container.addEventListener('touchmove', this._listenMove.bind(this), true); + container.addEventListener('touchend', this._listenMoveEnd.bind(this), true); + } + + _listenHover(e: MouseEvent|TouchEvent|Event): void { + e.preventDefault(); + const p = this._getPosition(e as MouseEvent|TouchEvent); + if (this._isVaildPoint(p)) { + if (this._event.has('hover')) { + this._event.trigger('hover', p); + } + } + this._isMoving = true; + } + + _listenLeave(e: MouseEvent|TouchEvent|Event): void { + e.preventDefault(); + if (this._event.has('leave')) { + this._event.trigger('leave', undefined); + } + } + + _listenMoveStart(e: MouseEvent|TouchEvent|Event): void { + e.preventDefault(); + const p = this._getPosition(e as MouseEvent|TouchEvent); + if (this._isVaildPoint(p)) { + if (this._event.has('point')) { + this._event.trigger('point', p); + } + if (this._event.has('moveStart')) { + this._event.trigger('moveStart', p); + } + } + this._isMoving = true; + } + + _listenMove(e: MouseEvent|TouchEvent|Event): void { + e.preventDefault(); + e.stopPropagation(); + if (this._event.has('move') && this._isMoving === true) { + const p = this._getPosition(e as MouseEvent|TouchEvent); + if (this._isVaildPoint(p)) { + this._event.trigger('move', p); + } + } + } + + _listenMoveEnd(e: MouseEvent|TouchEvent|Event): void { + e.preventDefault(); + if (this._event.has('moveEnd')) { + const p = this._getPosition(e as MouseEvent|TouchEvent); + if (this._isVaildPoint(p)) { + this._event.trigger('moveEnd', p); + } + } + this._isMoving = false; + } + + _listenWheel(e: WheelEvent) { + e.preventDefault(); + if (this._event.has('wheelX') && (e.deltaX > 0 || e.deltaX < 0)) { + this._event.trigger('wheelX', e.deltaX); + } + if (this._event.has('wheelY') && (e.deltaY > 0 || e.deltaY < 0)) { + this._event.trigger('wheelY', e.deltaY); + } + } + + _listenClick(e: MouseEvent|TouchEvent|Event) { + e.preventDefault(); + const maxLimitTime = 500; + const p = this._getPosition(e as MouseEvent|TouchEvent); + const t = Date.now(); + if (this._isVaildPoint(p)) { + const preClickPoint = this._temp.get('prevClickPoint'); + if ( + preClickPoint && (t - preClickPoint.t <= maxLimitTime) + && Math.abs(preClickPoint.x - p.x) <= 5 + && Math.abs(preClickPoint.y - p.y) <= 5 + ) { + if (this._event.has('doubleClick')) { + this._event.trigger('doubleClick', { x: p.x, y: p.y }); + } + } else { + this._temp.set('prevClickPoint', {x: p.x, y: p.y, t, }) + } + } + } + + _getPosition(e: MouseEvent|TouchEvent): TypePoint { + const canvas = this._canvas; + let x = 0; + let y = 0; + + // @ts-ignore + if (e && e.touches && e.touches.length > 0) { + // @ts-ignore + const touch: Touch = e.touches[0]; + if (touch) { + x = touch.clientX; + y = touch.clientY; + } + } else { + // @ts-ignore + x = e.clientX; + // @ts-ignore + y = e.clientY; + } + + const p = { + x: x - canvas.getBoundingClientRect().left, + y: y - canvas.getBoundingClientRect().top, + t: Date.now(), + }; + return p; + } + + private _isVaildPoint(p: TypePoint): boolean { + return ( p.x > 0 && p.y > 0); + } + +}