UNPKG

@leafer-ui/core

Version:
1,362 lines (1,328 loc) 49.3 kB
'use strict'; var draw = require('@leafer-ui/draw'); var core = require('@leafer/core'); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; exports.App = class App extends draw.Leafer { get __tag() { return 'App'; } get isApp() { return true; } constructor(userConfig, data) { super(userConfig, data); } init(userConfig, parentApp) { super.init(userConfig, parentApp); if (userConfig) { const { ground, tree, sky, editor } = userConfig; if (ground) this.ground = this.addLeafer(ground); if (tree || editor) this.tree = this.addLeafer(tree || { type: userConfig.type || 'design' }); if (sky || editor) this.sky = this.addLeafer(sky); if (editor) core.Creator.editor(editor, this); } } __setApp() { const { canvas } = this; const { realCanvas, view } = this.config; if (realCanvas || view === this.canvas.view || !canvas.parentView) this.realCanvas = true; else canvas.unrealCanvas(); this.leafer = this; this.watcher.disable(); this.layouter.disable(); } __updateLocalBounds() { this.forEach(leafer => leafer.updateLayout()); super.__updateLocalBounds(); } start() { super.start(); this.forEach(leafer => leafer.start()); } stop() { this.forEach(leafer => leafer.stop()); super.stop(); } unlockLayout() { super.unlockLayout(); this.forEach(leafer => leafer.unlockLayout()); } lockLayout() { super.lockLayout(); this.forEach(leafer => leafer.lockLayout()); } forceRender(bounds, sync) { this.forEach(leafer => leafer.forceRender(bounds, sync)); } addLeafer(merge) { const leafer = new draw.Leafer(merge); this.add(leafer); return leafer; } add(leafer, index) { if (!leafer.view) { if (this.realCanvas && !this.canvas.bounds) { setTimeout(() => this.add(leafer, index), 10); return; } leafer.init(this.__getChildConfig(leafer.userConfig), this); } super.add(leafer, index); if (index !== undefined) leafer.canvas.childIndex = index; this.__listenChildEvents(leafer); } forEach(fn) { this.children.forEach(fn); } __onCreated() { this.created = this.children.every(child => child.created); } __onReady() { if (this.children.every(child => child.ready)) super.__onReady(); } __onViewReady() { if (this.children.every(child => child.viewReady)) super.__onViewReady(); } __onChildRenderEnd(e) { this.renderer.addBlock(e.renderBounds); if (this.viewReady) this.renderer.update(); } __render(canvas, options) { if (canvas.context) this.forEach(leafer => options.matrix ? leafer.__render(canvas, options) : canvas.copyWorld(leafer.canvas, options && options.bounds)); } __onResize(event) { this.forEach(leafer => leafer.resize(event)); super.__onResize(event); } updateLayout() { this.forEach(leafer => leafer.updateLayout()); } __getChildConfig(userConfig) { const config = Object.assign({}, this.config); config.hittable = config.realCanvas = undefined; if (userConfig) core.DataHelper.assign(config, userConfig); if (this.autoLayout) core.DataHelper.copyAttrs(config, this, core.canvasSizeAttrs); config.view = this.realCanvas ? undefined : this.view; config.fill = undefined; return config; } __listenChildEvents(leafer) { leafer.once([ [core.LayoutEvent.END, this.__onReady, this], [core.RenderEvent.START, this.__onCreated, this], [core.RenderEvent.END, this.__onViewReady, this] ]); if (this.realCanvas) this.__eventIds.push(leafer.on_(core.RenderEvent.END, this.__onChildRenderEnd, this)); } }; exports.App = __decorate([ core.registerUI() ], exports.App); const downKeyMap = {}; const Keyboard = { isHoldSpaceKey() { return Keyboard.isHold('Space'); }, isHold(code) { return downKeyMap[code]; }, setDownCode(code) { if (!downKeyMap[code]) downKeyMap[code] = true; }, setUpCode(code) { downKeyMap[code] = false; } }; const PointerButton = { LEFT: 1, RIGHT: 2, MIDDLE: 4, defaultLeft(event) { if (!event.buttons) event.buttons = 1; }, left(event) { return event.buttons === 1; }, right(event) { return event.buttons === 2; }, middle(event) { return event.buttons === 4; } }; class UIEvent extends core.Event { get spaceKey() { return Keyboard.isHoldSpaceKey(); } get left() { return PointerButton.left(this); } get right() { return PointerButton.right(this); } get middle() { return PointerButton.middle(this); } constructor(params) { super(params.type); this.bubbles = true; Object.assign(this, params); } getBoxPoint(relative) { return (relative || this.current).getBoxPoint(this); } getInnerPoint(relative) { return (relative || this.current).getInnerPoint(this); } getLocalPoint(relative) { return (relative || this.current).getLocalPoint(this); } getPagePoint() { return this.current.getPagePoint(this); } getInner(relative) { return this.getInnerPoint(relative); } getLocal(relative) { return this.getLocalPoint(relative); } getPage() { return this.getPagePoint(); } static changeName(oldName, newName) { core.EventCreator.changeName(oldName, newName); } } exports.PointerEvent = class PointerEvent extends UIEvent { }; exports.PointerEvent.POINTER = 'pointer'; exports.PointerEvent.BEFORE_DOWN = 'pointer.before_down'; exports.PointerEvent.BEFORE_MOVE = 'pointer.before_move'; exports.PointerEvent.BEFORE_UP = 'pointer.before_up'; exports.PointerEvent.DOWN = 'pointer.down'; exports.PointerEvent.MOVE = 'pointer.move'; exports.PointerEvent.UP = 'pointer.up'; exports.PointerEvent.OVER = 'pointer.over'; exports.PointerEvent.OUT = 'pointer.out'; exports.PointerEvent.ENTER = 'pointer.enter'; exports.PointerEvent.LEAVE = 'pointer.leave'; exports.PointerEvent.TAP = 'tap'; exports.PointerEvent.DOUBLE_TAP = 'double_tap'; exports.PointerEvent.CLICK = 'click'; exports.PointerEvent.DOUBLE_CLICK = 'double_click'; exports.PointerEvent.LONG_PRESS = 'long_press'; exports.PointerEvent.LONG_TAP = 'long_tap'; exports.PointerEvent.MENU = 'pointer.menu'; exports.PointerEvent.MENU_TAP = 'pointer.menu_tap'; exports.PointerEvent = __decorate([ core.registerUIEvent() ], exports.PointerEvent); const MyPointerEvent = exports.PointerEvent; const tempMove = {}; exports.DragEvent = class DragEvent extends exports.PointerEvent { static setList(data) { this.list = data instanceof core.LeafList ? data : new core.LeafList(data); } static setData(data) { this.data = data; } static getValidMove(leaf, start, total) { const { draggable, dragBounds } = leaf, move = leaf.getLocalPoint(total, null, true); core.PointHelper.move(move, start.x - leaf.x, start.y - leaf.y); if (dragBounds) this.getMoveInDragBounds(leaf.__localBoxBounds, dragBounds === 'parent' ? leaf.parent.boxBounds : dragBounds, move, true); if (draggable === 'x') move.y = 0; if (draggable === 'y') move.x = 0; return move; } static getMoveInDragBounds(childBox, dragBounds, move, change) { const x = childBox.x + move.x, y = childBox.y + move.y, right = x + childBox.width, bottom = y + childBox.height; const boundsRight = dragBounds.x + dragBounds.width, boundsBottom = dragBounds.y + dragBounds.height; if (!change) move = Object.assign({}, move); if (core.BoundsHelper.includes(childBox, dragBounds)) { if (x > dragBounds.x) move.x += dragBounds.x - x; else if (right < boundsRight) move.x += boundsRight - right; if (y > dragBounds.y) move.y += dragBounds.y - y; else if (bottom < boundsBottom) move.y += boundsBottom - bottom; } else { if (x < dragBounds.x) move.x += dragBounds.x - x; else if (right > boundsRight) move.x += boundsRight - right; if (y < dragBounds.y) move.y += dragBounds.y - y; else if (bottom > boundsBottom) move.y += boundsBottom - bottom; } return move; } getPageMove(total) { this.assignMove(total); return this.current.getPagePoint(tempMove, null, true); } getInnerMove(relative, total) { if (!relative) relative = this.current; this.assignMove(total); return relative.getInnerPoint(tempMove, null, true); } getLocalMove(relative, total) { if (!relative) relative = this.current; this.assignMove(total); return relative.getLocalPoint(tempMove, null, true); } getPageTotal() { return this.getPageMove(true); } getInnerTotal(relative) { return this.getInnerMove(relative, true); } getLocalTotal(relative) { return this.getLocalMove(relative, true); } getPageBounds() { const total = this.getPageTotal(), start = this.getPagePoint(), bounds = {}; core.BoundsHelper.set(bounds, start.x - total.x, start.y - total.y, total.x, total.y); core.BoundsHelper.unsign(bounds); return bounds; } assignMove(total) { tempMove.x = total ? this.totalX : this.moveX; tempMove.y = total ? this.totalY : this.moveY; } }; exports.DragEvent.BEFORE_DRAG = 'drag.before_drag'; exports.DragEvent.START = 'drag.start'; exports.DragEvent.DRAG = 'drag'; exports.DragEvent.END = 'drag.end'; exports.DragEvent.OVER = 'drag.over'; exports.DragEvent.OUT = 'drag.out'; exports.DragEvent.ENTER = 'drag.enter'; exports.DragEvent.LEAVE = 'drag.leave'; exports.DragEvent = __decorate([ core.registerUIEvent() ], exports.DragEvent); const MyDragEvent = exports.DragEvent; exports.DropEvent = class DropEvent extends exports.PointerEvent { static setList(data) { exports.DragEvent.setList(data); } static setData(data) { exports.DragEvent.setData(data); } }; exports.DropEvent.DROP = 'drop'; exports.DropEvent = __decorate([ core.registerUIEvent() ], exports.DropEvent); exports.MoveEvent = class MoveEvent extends exports.DragEvent { }; exports.MoveEvent.BEFORE_MOVE = 'move.before_move'; exports.MoveEvent.START = 'move.start'; exports.MoveEvent.MOVE = 'move'; exports.MoveEvent.END = 'move.end'; exports.MoveEvent = __decorate([ core.registerUIEvent() ], exports.MoveEvent); exports.RotateEvent = class RotateEvent extends exports.PointerEvent { }; exports.RotateEvent.BEFORE_ROTATE = 'rotate.before_rotate'; exports.RotateEvent.START = 'rotate.start'; exports.RotateEvent.ROTATE = 'rotate'; exports.RotateEvent.END = 'rotate.end'; exports.RotateEvent = __decorate([ core.registerUIEvent() ], exports.RotateEvent); exports.SwipeEvent = class SwipeEvent extends exports.DragEvent { }; exports.SwipeEvent.SWIPE = 'swipe'; exports.SwipeEvent.LEFT = 'swipe.left'; exports.SwipeEvent.RIGHT = 'swipe.right'; exports.SwipeEvent.UP = 'swipe.up'; exports.SwipeEvent.DOWN = 'swipe.down'; exports.SwipeEvent = __decorate([ core.registerUIEvent() ], exports.SwipeEvent); exports.ZoomEvent = class ZoomEvent extends exports.PointerEvent { }; exports.ZoomEvent.BEFORE_ZOOM = 'zoom.before_zoom'; exports.ZoomEvent.START = 'zoom.start'; exports.ZoomEvent.ZOOM = 'zoom'; exports.ZoomEvent.END = 'zoom.end'; exports.ZoomEvent = __decorate([ core.registerUIEvent() ], exports.ZoomEvent); exports.KeyEvent = class KeyEvent extends UIEvent { }; exports.KeyEvent.DOWN = 'key.down'; exports.KeyEvent.HOLD = 'key.hold'; exports.KeyEvent.UP = 'key.up'; exports.KeyEvent = __decorate([ core.registerUIEvent() ], exports.KeyEvent); const InteractionHelper = { getDragEventData(startPoint, lastPoint, event) { return Object.assign(Object.assign({}, event), { x: event.x, y: event.y, moveX: event.x - lastPoint.x, moveY: event.y - lastPoint.y, totalX: event.x - startPoint.x, totalY: event.y - startPoint.y }); }, getDropEventData(event, list, data) { return Object.assign(Object.assign({}, event), { list, data }); }, getSwipeDirection(angle) { if (angle < -45 && angle > -135) return exports.SwipeEvent.UP; else if (angle > 45 && angle < 135) return exports.SwipeEvent.DOWN; else if (angle <= 45 && angle >= -45) return exports.SwipeEvent.RIGHT; else return exports.SwipeEvent.LEFT; }, getSwipeEventData(startPoint, lastDragData, event) { return Object.assign(Object.assign({}, event), { moveX: lastDragData.moveX, moveY: lastDragData.moveY, totalX: event.x - startPoint.x, totalY: event.y - startPoint.y, type: I.getSwipeDirection(core.PointHelper.getAngle(startPoint, event)) }); }, getBase(e) { const pointerUpButtons = e.button === 1 ? 4 : e.button; return { altKey: e.altKey, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, metaKey: e.metaKey, buttons: e.buttons === undefined ? 1 : (e.buttons === 0 ? pointerUpButtons : e.buttons), origin: e }; }, pathHasEventType(path, type) { const { list } = path; for (let i = 0, len = list.length; i < len; i++) { if (list[i].hasEvent(type)) return true; } return false; }, filterPathByEventType(path, type) { const find = new core.LeafList(); const { list } = path; for (let i = 0, len = list.length; i < len; i++) { if (list[i].hasEvent(type)) find.add(list[i]); } return find; }, pathCanDrag(path) { return path && path.list.some(item => item.draggable || item.editable || (!item.isLeafer && item.hasEvent(exports.DragEvent.DRAG))); }, pathHasOutside(path) { return path && path.list.some(item => item.isOutside); }, }; const I = InteractionHelper; const emptyList = new core.LeafList(); const { getDragEventData, getDropEventData, getSwipeEventData } = InteractionHelper; class Dragger { constructor(interaction) { this.interaction = interaction; } setDragData(data) { if (this.animateWait) this.dragEndReal(); this.downData = this.interaction.downData; this.dragData = getDragEventData(data, data, data); this.canAnimate = this.canDragOut = true; } getList(realDraggable, hover) { const { proxy } = this.interaction.selector; const hasProxyList = proxy && proxy.list.length, dragList = exports.DragEvent.list || this.draggableList || emptyList; return this.dragging && (hasProxyList ? (realDraggable ? emptyList : new core.LeafList(hover ? [...proxy.list, ...proxy.dragHoverExclude] : proxy.list)) : dragList); } checkDrag(data, canDrag) { const { interaction } = this; if (this.moving && data.buttons < 1) { this.canAnimate = false; interaction.pointerCancel(); return; } if (!this.moving && canDrag) { if (this.moving = interaction.canMove(this.downData) || interaction.isHoldRightKey || interaction.isMobileDragEmpty) { this.dragData.moveType = 'drag'; interaction.emit(exports.MoveEvent.START, this.dragData); } } if (!this.moving) this.dragStart(data, canDrag); this.drag(data); } dragStart(data, canDrag) { if (!this.dragging) { this.dragging = canDrag && PointerButton.left(data); if (this.dragging) { this.interaction.emit(exports.DragEvent.START, this.dragData); this.getDraggableList(this.dragData.path); this.setDragStartPoints(this.realDraggableList = this.getList(true)); } } } setDragStartPoints(list) { this.dragStartPoints = {}; list.forEach(leaf => this.dragStartPoints[leaf.innerId] = { x: leaf.x, y: leaf.y }); } getDraggableList(path) { let leaf; for (let i = 0, len = path.length; i < len; i++) { leaf = path.list[i]; if ((leaf.draggable || leaf.editable) && leaf.hitSelf && !leaf.locked) { this.draggableList = new core.LeafList(leaf); break; } } } drag(data) { const { interaction, dragData, downData } = this; const { path, throughPath } = downData; this.dragData = getDragEventData(downData, dragData, data); if (throughPath) this.dragData.throughPath = throughPath; this.dragData.path = path; if (this.moving) { this.dragData.moveType = 'drag'; interaction.emit(exports.MoveEvent.BEFORE_MOVE, this.dragData); interaction.emit(exports.MoveEvent.MOVE, this.dragData); } else if (this.dragging) { this.dragReal(); interaction.emit(exports.DragEvent.BEFORE_DRAG, this.dragData); interaction.emit(exports.DragEvent.DRAG, this.dragData); } } dragReal() { const { running } = this.interaction; const list = this.realDraggableList; if (list.length && running) { const { totalX, totalY } = this.dragData; list.forEach(leaf => leaf.draggable && leaf.move(exports.DragEvent.getValidMove(leaf, this.dragStartPoints[leaf.innerId], { x: totalX, y: totalY }))); } } dragOverOrOut(data) { const { interaction } = this; const { dragOverPath } = this; const { path } = data; this.dragOverPath = path; if (dragOverPath) { if (path.indexAt(0) !== dragOverPath.indexAt(0)) { interaction.emit(exports.DragEvent.OUT, data, dragOverPath); interaction.emit(exports.DragEvent.OVER, data, path); } } else interaction.emit(exports.DragEvent.OVER, data, path); } dragEnterOrLeave(data) { const { interaction } = this; const { dragEnterPath } = this; const { path } = data; interaction.emit(exports.DragEvent.LEAVE, data, dragEnterPath, path); interaction.emit(exports.DragEvent.ENTER, data, path, dragEnterPath); this.dragEnterPath = path; } dragEnd(data, speed) { if (!this.dragging && !this.moving) return; if (this.checkDragEndAnimate(data, speed)) return; this.dragEndReal(data); } dragEndReal(data) { const { interaction, downData, dragData } = this; if (!data) data = dragData; const { path, throughPath } = downData; const endDragData = getDragEventData(downData, data, data); if (throughPath) endDragData.throughPath = throughPath; endDragData.path = path; if (this.moving) { this.moving = false; endDragData.moveType = 'drag'; interaction.emit(exports.MoveEvent.END, endDragData); } if (this.dragging) { const dropList = this.getList(); this.dragging = false; interaction.emit(exports.DragEvent.END, endDragData); this.swipe(data, downData, dragData, endDragData); this.drop(data, dropList, this.dragEnterPath); } this.autoMoveCancel(); this.dragReset(); this.animate(null, 'off'); } swipe(data, downData, dragData, endDragData) { const { interaction } = this; if (core.PointHelper.getDistance(downData, data) > interaction.config.pointer.swipeDistance) { const swipeData = getSwipeEventData(downData, dragData, endDragData); this.interaction.emit(swipeData.type, swipeData); } } drop(data, dropList, dragEnterPath) { const dropData = getDropEventData(data, dropList, exports.DragEvent.data); dropData.path = dragEnterPath; this.interaction.emit(exports.DropEvent.DROP, dropData); this.interaction.emit(exports.DragEvent.LEAVE, data, dragEnterPath); } dragReset() { exports.DragEvent.list = exports.DragEvent.data = this.draggableList = this.dragData = this.downData = this.dragOverPath = this.dragEnterPath = null; } checkDragEndAnimate(_data, _speed) { return false; } animate(_func, _off) { } checkDragOut(_data) { } autoMoveOnDragOut(_data) { } autoMoveCancel() { } destroy() { this.dragReset(); } } const debug = core.Debug.get('emit'); function emit(type, data, path, excludePath) { if (!path && !data.path) return; let leaf; data.type = type; if (path) { data = Object.assign(Object.assign({}, data), { path }); } else { path = data.path; } data.target = path.indexAt(0); try { for (let i = path.length - 1; i > -1; i--) { leaf = path.list[i]; if (emitEvent(leaf, type, data, true, excludePath)) return; if (leaf.isApp) emitAppChildren(leaf, type, data, true, excludePath); } for (let i = 0, len = path.length; i < len; i++) { leaf = path.list[i]; if (leaf.isApp) emitAppChildren(leaf, type, data, false, excludePath); if (emitEvent(leaf, type, data, false, excludePath)) return; } } catch (e) { debug.error(e); } } const allowTypes = ['move', 'zoom', 'rotate', 'key']; function emitAppChildren(leaf, type, data, capture, excludePath) { if (allowTypes.some(name => type.startsWith(name)) && leaf.__.hitChildren && !exclude(leaf, excludePath)) { let child; for (let i = 0, len = leaf.children.length; i < len; i++) { child = leaf.children[i]; if (!data.path.has(child) && child.__.hittable) emitEvent(child, type, data, capture, excludePath); } } } function emitEvent(leaf, type, data, capture, excludePath) { if (leaf.destroyed) return false; if (leaf.__.hitSelf && !exclude(leaf, excludePath)) { if (draw.State.updateEventStyle && !capture) draw.State.updateEventStyle(leaf, type); if (leaf.hasEvent(type, capture)) { data.phase = capture ? 1 : ((leaf === data.target) ? 2 : 3); const event = core.EventCreator.get(type, data); leaf.emitEvent(event, capture); if (event.isStop) return true; } } return false; } function exclude(leaf, excludePath) { return excludePath && excludePath.has(leaf); } const config = { wheel: { zoomSpeed: 0.5, moveSpeed: 0.5, rotateSpeed: 0.5, delta: { x: 80 / 4, y: 8.0 }, }, pointer: { type: 'pointer', snap: true, hitRadius: 5, tapTime: 120, longPressTime: 800, transformTime: 500, hover: true, dragHover: true, dragDistance: 2, swipeDistance: 20, }, touch: { preventDefault: 'auto' }, multiTouch: {}, move: { autoDistance: 2 }, zoom: {}, cursor: true, keyEvent: true }; const { pathHasEventType, pathCanDrag, pathHasOutside } = InteractionHelper; class InteractionBase { get dragging() { return this.dragger.dragging; } get transforming() { return this.transformer.transforming; } get moveMode() { return this.m.drag === true || this.isHoldSpaceKey || this.isHoldMiddleKey || (this.isHoldRightKey && this.dragger.moving) || this.isDragEmpty; } get canHover() { return this.p.hover && !this.config.mobile; } get isDragEmpty() { return this.m.dragEmpty && this.isRootPath(this.hoverData) && (!this.downData || this.isRootPath(this.downData)); } get isMobileDragEmpty() { return this.m.dragEmpty && !this.canHover && this.downData && this.isTreePath(this.downData); } get isHoldMiddleKey() { return this.m.holdMiddleKey && this.downData && PointerButton.middle(this.downData); } get isHoldRightKey() { return this.m.holdRightKey && this.downData && PointerButton.right(this.downData); } get isHoldSpaceKey() { return this.m.holdSpaceKey && Keyboard.isHoldSpaceKey(); } get m() { return this.config.move; } get p() { return this.config.pointer; } get hitRadius() { return this.p.hitRadius; } constructor(target, canvas, selector, userConfig) { this.config = core.DataHelper.clone(config); this.tapCount = 0; this.downKeyMap = {}; this.target = target; this.canvas = canvas; this.selector = selector; this.defaultPath = new core.LeafList(target); this.createTransformer(); this.dragger = new Dragger(this); if (userConfig) this.config = core.DataHelper.default(userConfig, this.config); this.__listenEvents(); } start() { this.running = true; } stop() { this.running = false; } receive(_event) { } pointerDown(data, useDefaultPath) { if (!data) data = this.hoverData; if (!data) return; PointerButton.defaultLeft(data); this.updateDownData(data); this.checkPath(data, useDefaultPath); this.downTime = Date.now(); this.emit(exports.PointerEvent.BEFORE_DOWN, data); this.emit(exports.PointerEvent.DOWN, data); if (PointerButton.left(data)) { this.tapWait(); this.longPressWait(data); } this.waitRightTap = PointerButton.right(data); this.dragger.setDragData(data); if (!this.isHoldRightKey) this.updateCursor(data); } pointerMove(data) { if (!data) data = this.hoverData; if (!data) return; const { downData } = this; if (downData) PointerButton.defaultLeft(data); const hit = this.canvas.bounds.hitPoint(data); if (hit || downData) { this.pointerMoveReal(data); if (downData) this.dragger.checkDragOut(data); } } pointerMoveReal(data) { this.emit(exports.PointerEvent.BEFORE_MOVE, data, this.defaultPath); if (this.downData) { const canDrag = core.PointHelper.getDistance(this.downData, data) > this.p.dragDistance; if (canDrag) { this.pointerWaitCancel(); this.waitRightTap = false; } this.dragger.checkDrag(data, canDrag); } if (!this.dragger.moving) { this.updateHoverData(data); this.checkPath(data); this.emit(exports.PointerEvent.MOVE, data); this.pointerHover(data); if (this.dragging) { this.dragger.dragOverOrOut(data); this.dragger.dragEnterOrLeave(data); } } this.updateCursor(this.downData || data); } pointerUp(data) { const { downData } = this; if (!data) data = downData; if (!downData) return; PointerButton.defaultLeft(data); data.multiTouch = downData.multiTouch; this.findPath(data); const upData = Object.assign(Object.assign({}, data), { path: data.path.clone() }); data.path.addList(downData.path.list); this.checkPath(data); this.downData = null; this.emit(exports.PointerEvent.BEFORE_UP, data); this.emit(exports.PointerEvent.UP, data); this.touchLeave(data); if (!data.isCancel) { this.tap(data); this.menuTap(data); } this.dragger.dragEnd(data); this.updateCursor(upData); } pointerCancel() { const data = Object.assign({}, this.dragger.dragData); data.isCancel = true; this.pointerUp(data); } menu(data) { this.findPath(data); this.emit(exports.PointerEvent.MENU, data); this.waitMenuTap = true; if (!this.downData && this.waitRightTap) this.menuTap(data); } menuTap(data) { if (this.waitRightTap && this.waitMenuTap) { this.emit(exports.PointerEvent.MENU_TAP, data); this.waitRightTap = this.waitMenuTap = false; } } createTransformer() { } move(_data) { } zoom(_data) { } rotate(_data) { } transformEnd() { } wheel(_data) { } multiTouch(_data, _list) { } keyDown(data) { if (!this.config.keyEvent) return; const { code } = data; if (!this.downKeyMap[code]) { this.downKeyMap[code] = true; Keyboard.setDownCode(code); this.emit(exports.KeyEvent.HOLD, data, this.defaultPath); if (this.moveMode) { this.cancelHover(); this.updateCursor(); } } this.emit(exports.KeyEvent.DOWN, data, this.defaultPath); } keyUp(data) { if (!this.config.keyEvent) return; const { code } = data; this.downKeyMap[code] = false; Keyboard.setUpCode(code); this.emit(exports.KeyEvent.UP, data, this.defaultPath); if (this.cursor === 'grab') this.updateCursor(); } pointerHover(data) { if (this.canHover && !(this.dragging && !this.p.dragHover)) { data.path || (data.path = new core.LeafList()); this.pointerOverOrOut(data); this.pointerEnterOrLeave(data); } } pointerOverOrOut(data) { const { path } = data; const { overPath } = this; this.overPath = path; if (overPath) { if (path.indexAt(0) !== overPath.indexAt(0)) { this.emit(exports.PointerEvent.OUT, data, overPath); this.emit(exports.PointerEvent.OVER, data, path); } } else { this.emit(exports.PointerEvent.OVER, data, path); } } pointerEnterOrLeave(data) { let { path } = data; if (this.downData && !this.moveMode) { path = path.clone(); this.downData.path.forEach(leaf => path.add(leaf)); } const { enterPath } = this; this.enterPath = path; this.emit(exports.PointerEvent.LEAVE, data, enterPath, path); this.emit(exports.PointerEvent.ENTER, data, path, enterPath); } touchLeave(data) { if (data.pointerType === 'touch') { if (this.enterPath) { this.emit(exports.PointerEvent.LEAVE, data); if (this.dragger.dragging) this.emit(exports.DropEvent.LEAVE, data); } } } tap(data) { const { pointer } = this.config; const hasLong = this.longTap(data); if (!pointer.tapMore && hasLong) return; if (!this.waitTap) return; if (pointer.tapMore) this.emitTap(data); const useTime = Date.now() - this.downTime; const hasDouble = [exports.PointerEvent.DOUBLE_TAP, exports.PointerEvent.DOUBLE_CLICK].some(type => pathHasEventType(data.path, type)); if (useTime < pointer.tapTime + 50 && hasDouble) { this.tapCount++; if (this.tapCount === 2) { this.tapWaitCancel(); this.emitDoubleTap(data); } else { clearTimeout(this.tapTimer); this.tapTimer = setTimeout(() => { if (!pointer.tapMore) { this.tapWaitCancel(); this.emitTap(data); } }, pointer.tapTime); } } else { if (!pointer.tapMore) { this.tapWaitCancel(); this.emitTap(data); } } } findPath(data, options) { const { hitRadius, through } = this.p; const { bottomList, target } = this; if (!core.Platform.backgrounder && !data.origin) target && target.updateLayout(); const find = this.selector.getByPoint(data, hitRadius, Object.assign({ bottomList, name: data.type }, (options || { through }))); if (find.throughPath) data.throughPath = find.throughPath; data.path = find.path; return find.path; } isRootPath(data) { return data && data.path.list[0].isLeafer; } isTreePath(data) { const app = this.target.app; if (!app || !app.isApp) return false; return app.editor && (!data.path.has(app.editor) && data.path.has(app.tree) && !data.target.syncEventer); } checkPath(data, useDefaultPath) { if (useDefaultPath || (this.moveMode && !pathHasOutside(data.path))) data.path = this.defaultPath; } canMove(data) { return data && (this.moveMode || (this.m.drag === 'auto' && !pathCanDrag(data.path))) && !pathHasOutside(data.path); } isDrag(leaf) { return this.dragger.getList().has(leaf); } isPress(leaf) { return this.downData && this.downData.path.has(leaf); } isHover(leaf) { return this.enterPath && this.enterPath.has(leaf); } isFocus(leaf) { return this.focusData === leaf; } cancelHover() { const { hoverData } = this; if (hoverData) { hoverData.path = this.defaultPath; this.pointerHover(hoverData); } } updateDownData(data, options, merge) { const { downData } = this; if (!data && downData) data = downData; if (!data) return; this.findPath(data, options); if (merge && downData) data.path.addList(downData.path.list); this.downData = data; } updateHoverData(data) { if (!data) data = this.hoverData; if (!data) return; this.findPath(data, { exclude: this.dragger.getList(false, true), name: exports.PointerEvent.MOVE }); this.hoverData = data; } updateCursor(data) { if (!this.config.cursor || !this.canHover) return; if (!data) { this.updateHoverData(); data = this.downData || this.hoverData; } if (this.dragger.moving) { return this.setCursor('grabbing'); } else if (this.canMove(data)) { return this.setCursor(this.downData ? 'grabbing' : 'grab'); } else if (!data) return; let leaf, cursor; const { path } = data; for (let i = 0, len = path.length; i < len; i++) { leaf = path.list[i]; cursor = (leaf.syncEventer && leaf.syncEventer.cursor) || leaf.cursor; if (cursor) break; } this.setCursor(cursor); } setCursor(cursor) { this.cursor = cursor; } getLocal(clientPoint, updateClient) { const clientBounds = this.canvas.getClientBounds(updateClient); const point = { x: clientPoint.clientX - clientBounds.x, y: clientPoint.clientY - clientBounds.y }; if (this.p.snap) core.PointHelper.round(point); return point; } emitTap(data) { this.emit(exports.PointerEvent.TAP, data); this.emit(exports.PointerEvent.CLICK, data); } emitDoubleTap(data) { this.emit(exports.PointerEvent.DOUBLE_TAP, data); this.emit(exports.PointerEvent.DOUBLE_CLICK, data); } pointerWaitCancel() { this.tapWaitCancel(); this.longPressWaitCancel(); } tapWait() { clearTimeout(this.tapTimer); this.waitTap = true; } tapWaitCancel() { if (this.waitTap) { clearTimeout(this.tapTimer); this.waitTap = false; this.tapCount = 0; } } longPressWait(data) { clearTimeout(this.longPressTimer); this.longPressTimer = setTimeout(() => { this.longPressed = true; this.emit(exports.PointerEvent.LONG_PRESS, data); }, this.p.longPressTime); } longTap(data) { let hasLong; if (this.longPressed) { this.emit(exports.PointerEvent.LONG_TAP, data); if (pathHasEventType(data.path, exports.PointerEvent.LONG_TAP) || pathHasEventType(data.path, exports.PointerEvent.LONG_PRESS)) hasLong = true; } this.longPressWaitCancel(); return hasLong; } longPressWaitCancel() { if (this.longPressTimer) { clearTimeout(this.longPressTimer); this.longPressed = false; } } __onResize() { const { dragOut } = this.m; this.shrinkCanvasBounds = new core.Bounds(this.canvas.bounds); this.shrinkCanvasBounds.spread(-(typeof dragOut === 'number' ? dragOut : 2)); } __listenEvents() { const { target } = this; this.__eventIds = [target.on_(core.ResizeEvent.RESIZE, this.__onResize, this)]; target.once(core.LeaferEvent.READY, () => this.__onResize()); } __removeListenEvents() { this.target.off_(this.__eventIds); this.__eventIds.length = 0; } emit(type, data, path, excludePath) { if (this.running) emit(type, data, path, excludePath); } destroy() { if (this.__eventIds.length) { this.stop(); this.__removeListenEvents(); this.dragger.destroy(); if (this.transformer) this.transformer.destroy(); this.downData = this.overPath = this.enterPath = null; } } } class Cursor { static set(name, value) { this.custom[name] = value; } static get(name) { return this.custom[name]; } } Cursor.custom = {}; class HitCanvasManager extends core.CanvasManager { constructor() { super(...arguments); this.maxTotal = 1000; this.pathList = new core.LeafList(); this.pixelList = new core.LeafList(); } getPixelType(leaf, config) { this.__autoClear(); this.pixelList.add(leaf); return core.Creator.hitCanvas(config); } getPathType(leaf) { this.__autoClear(); this.pathList.add(leaf); return core.Creator.hitCanvas(); } clearImageType() { this.__clearLeafList(this.pixelList); } clearPathType() { this.__clearLeafList(this.pathList); } __clearLeafList(leafList) { if (leafList.length) { leafList.forEach(leaf => { if (leaf.__hitCanvas) { leaf.__hitCanvas.destroy(); leaf.__hitCanvas = null; } }); leafList.reset(); } } __autoClear() { if (this.pathList.length + this.pixelList.length > this.maxTotal) this.clear(); } clear() { this.clearPathType(); this.clearImageType(); } } const { toInnerRadiusPointOf, copy, setRadius } = core.PointHelper; const inner = {}; const leaf = core.Leaf.prototype; leaf.__hitWorld = function (point) { const data = this.__; if (!data.hitSelf) return false; const world = this.__world, layout = this.__layout; const isSmall = world.width < 10 && world.height < 10; if (data.hitRadius) { copy(inner, point), point = inner; setRadius(point, data.hitRadius); } toInnerRadiusPointOf(point, world, inner); if (data.hitBox || isSmall) { if (core.BoundsHelper.hitRadiusPoint(layout.boxBounds, inner)) return true; if (isSmall) return false; } if (layout.hitCanvasChanged || !this.__hitCanvas) { this.__updateHitCanvas(); if (!layout.boundsChanged) layout.hitCanvasChanged = false; } return this.__hit(inner); }; leaf.__hitFill = function (inner) { const h = this.__hitCanvas; return h && h.hitFill(inner, this.__.windingRule); }; leaf.__hitStroke = function (inner, strokeWidth) { const h = this.__hitCanvas; return h && h.hitStroke(inner, strokeWidth); }; leaf.__hitPixel = function (inner) { const h = this.__hitCanvas; return h && h.hitPixel(inner, this.__layout.renderBounds, h.hitScale); }; leaf.__drawHitPath = function (canvas) { canvas && this.__drawRenderPath(canvas); }; const matrix = new core.Matrix(); const ui$1 = draw.UI.prototype; ui$1.__updateHitCanvas = function () { if (this.__box) this.__box.__updateHitCanvas(); const leafer = this.leafer || (this.parent && this.parent.leafer); if (!leafer) return; const data = this.__, { hitCanvasManager } = leafer; const isHitPixelFill = (data.__isAlphaPixelFill || data.__isCanvas) && data.hitFill === 'pixel'; const isHitPixelStroke = data.__isAlphaPixelStroke && data.hitStroke === 'pixel'; const isHitPixel = isHitPixelFill || isHitPixelStroke; if (!this.__hitCanvas) this.__hitCanvas = isHitPixel ? hitCanvasManager.getPixelType(this, { contextSettings: { willReadFrequently: true } }) : hitCanvasManager.getPathType(this); const h = this.__hitCanvas; if (isHitPixel) { const { renderBounds } = this.__layout; const size = core.Platform.image.hitCanvasSize; const scale = h.hitScale = core.tempBounds.set(0, 0, size, size).getFitMatrix(renderBounds).a; const { x, y, width, height } = core.tempBounds.set(renderBounds).scale(scale); h.resize({ width, height, pixelRatio: 1 }); h.clear(); core.ImageManager.patternLocked = true; this.__renderShape(h, { matrix: matrix.setWith(this.__world).scaleWith(1 / scale).invertWith().translate(-x, -y), ignoreFill: !isHitPixelFill, ignoreStroke: !isHitPixelStroke }); core.ImageManager.patternLocked = false; h.resetTransform(); data.__isHitPixel = true; } else { data.__isHitPixel && (data.__isHitPixel = false); } this.__drawHitPath(h); h.setStrokeOptions(data); }; ui$1.__hit = function (inner) { if (this.__box && this.__box.__hit(inner)) return true; const data = this.__; if (data.__isHitPixel && this.__hitPixel(inner)) return true; const { hitFill } = data; const needHitFillPath = ((data.fill || data.__isCanvas) && (hitFill === 'path' || (hitFill === 'pixel' && !(data.__isAlphaPixelFill || data.__isCanvas)))) || hitFill === 'all'; if (needHitFillPath && this.__hitFill(inner)) return true; const { hitStroke, __maxStrokeWidth: strokeWidth } = data; const needHitStrokePath = (data.stroke && (hitStroke === 'path' || (hitStroke === 'pixel' && !data.__isAlphaPixelStroke))) || hitStroke === 'all'; if (!needHitFillPath && !needHitStrokePath) return false; const radiusWidth = inner.radiusX * 2; let hitWidth = radiusWidth; if (needHitStrokePath) { switch (data.strokeAlign) { case 'inside': hitWidth += strokeWidth * 2; if (!needHitFillPath && this.__hitFill(inner) && this.__hitStroke(inner, hitWidth)) return true; hitWidth = radiusWidth; break; case 'center': hitWidth += strokeWidth; break; case 'outside': hitWidth += strokeWidth * 2; if (!needHitFillPath) { if (!this.__hitFill(inner) && this.__hitStroke(inner, hitWidth)) return true; hitWidth = radiusWidth; } break; } } return hitWidth ? this.__hitStroke(inner, hitWidth) : false; }; const ui = draw.UI.prototype, rect = draw.Rect.prototype, box = draw.Box.prototype; rect.__updateHitCanvas = box.__updateHitCanvas = function () { if (this.stroke || this.cornerRadius || ((this.fill || this.__.__isCanvas) && this.hitFill === 'pixel') || this.hitStroke === 'all') ui.__updateHitCanvas.call(this); else if (this.__hitCanvas) this.__hitCanvas = null; }; rect.__hitFill = box.__hitFill = function (inner) { return this.__hitCanvas ? ui.__hitFill.call(this, inner) : core.BoundsHelper.hitRadiusPoint(this.__layout.boxBounds, inner); }; draw.Text.prototype.__drawHitPath = function (canvas) { const { __lineHeight, fontSize, __baseLine, __letterSpacing, __textDrawData: data } = this.__; canvas.beginPath(); if (__letterSpacing < 0) this.__drawPathByBox(canvas); else data.rows.forEach(row => canvas.rect(row.x, row.y - __baseLine, row.width, __lineHeight < fontSize ? fontSize : __lineHeight)); }; function getSelector(ui) { return ui.leafer ? ui.leafer.selector : (core.Platform.selector || (core.Platform.selector = core.Creator.selector())); } draw.Group.prototype.pick = function (hitPoint, options) { options || (options = draw.emptyData); this.updateLayout(); return getSelector(this).getByPoint(hitPoint, options.hitRadius || 0, Object.assign(Object.assign({}, options), { target: this })); }; const canvas = core.LeaferCanvasBase.prototype; canvas.hitFill = function (point, fillRule) { return fillRule ? this.context.isPointInPath(point.x, point.y, fillRule) : this.context.isPointInPath(point.x, point.y); }; canvas.hitStroke = function (point, strokeWidth) { this.strokeWidth = strokeWidth; return this.context.isPointInStroke(point.x, point.y); }; canvas.hitPixel = function (radiusPoint, offset, scale = 1) { let { x, y, radiusX, radiusY } = radiusPoint; if (offset) x -= offset.x, y -= offset.y; core.tempBounds.set(x - radiusX, y - radiusY, radiusX * 2, radiusY * 2).scale(scale).ceil(); const { data } = this.context.getImageData(core.tempBounds.x, core.tempBounds.y, core.tempBounds.width || 1, core.tempBounds.height || 1); for (let i = 0, len = data.length; i < len; i += 4) { if (data[i + 3] > 0) return true; } return data[3] > 0; }; exports.Cursor = Cursor; exports.Dragger = Dragger; exports.HitCanvasManager = HitCanvasManager; exports.InteractionBase = InteractionBase; exports.InteractionHelper = InteractionHelper; exports.Keyboard = Keyboard; exports.MyDragEvent = MyDragEvent; exports.MyPointerEvent = MyPointerEvent; exports.PointerButton = PointerButton; exports.UIEvent = UIEvent; Object.keys(draw).forEach(function (k) { if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { enumerable: true, get: function () { return draw[k]; } }); });