@leafer-ui/core
Version:
1,408 lines (1,284 loc) • 47.6 kB
JavaScript
import { Leafer, DragBoundsHelper, State, UI, Rect, Box, Text, Group, emptyData } from "@leafer-ui/draw";
export * from "@leafer-ui/draw";
import { registerUI, Creator, isUndefined, DataHelper, canvasSizeAttrs, LayoutEvent, RenderEvent, Event, EventCreator, registerUIEvent, LeafList, PointHelper, BoundsHelper, LeafHelper, isString, isNumber, Debug, Platform, Bounds, ResizeEvent, LeaferEvent, CanvasManager, Leaf, Matrix, tempBounds, ImageManager, LeaferCanvasBase } from "@leafer/core";
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;
};
let App = class App extends 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: ground, tree: tree, sky: sky, editor: 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) Creator.editor(editor, this);
}
}
__setApp() {
const {canvas: canvas} = this;
const {realCanvas: realCanvas, view: 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 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 (!isUndefined(index)) 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.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) DataHelper.assign(config, userConfig);
if (this.autoLayout) DataHelper.copyAttrs(config, this, canvasSizeAttrs);
config.view = this.realCanvas ? undefined : this.view;
config.fill = undefined;
return config;
}
__listenChildEvents(leafer) {
leafer.once([ [ LayoutEvent.END, this.__onReady, this ], [ RenderEvent.START, this.__onCreated, this ], [ RenderEvent.END, this.__onViewReady, this ] ]);
if (this.realCanvas) this.__eventIds.push(leafer.on_(RenderEvent.END, this.__onChildRenderEnd, this));
}
};
App = __decorate([ registerUI() ], App);
const downKeyMap = {};
const Keyboard = {
isHoldSpaceKey() {
return Keyboard.isHold("Space");
},
isHold(code) {
return downKeyMap[code];
},
isHoldKeys(shortcutKeys, e) {
return e ? shortcutKeys(e) : undefined;
},
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 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);
}
isHoldKeys(shortcutKeys) {
return Keyboard.isHoldKeys(shortcutKeys, this);
}
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) {
EventCreator.changeName(oldName, newName);
}
}
let PointerEvent = class PointerEvent extends UIEvent {};
PointerEvent.POINTER = "pointer";
PointerEvent.BEFORE_DOWN = "pointer.before_down";
PointerEvent.BEFORE_MOVE = "pointer.before_move";
PointerEvent.BEFORE_UP = "pointer.before_up";
PointerEvent.DOWN = "pointer.down";
PointerEvent.MOVE = "pointer.move";
PointerEvent.UP = "pointer.up";
PointerEvent.OVER = "pointer.over";
PointerEvent.OUT = "pointer.out";
PointerEvent.ENTER = "pointer.enter";
PointerEvent.LEAVE = "pointer.leave";
PointerEvent.TAP = "tap";
PointerEvent.DOUBLE_TAP = "double_tap";
PointerEvent.CLICK = "click";
PointerEvent.DOUBLE_CLICK = "double_click";
PointerEvent.LONG_PRESS = "long_press";
PointerEvent.LONG_TAP = "long_tap";
PointerEvent.MENU = "pointer.menu";
PointerEvent.MENU_TAP = "pointer.menu_tap";
PointerEvent = __decorate([ registerUIEvent() ], PointerEvent);
const MyPointerEvent = PointerEvent;
const tempMove = {};
let DragEvent = class DragEvent extends PointerEvent {
static setList(data) {
this.list = data instanceof LeafList ? data : new LeafList(data);
}
static setData(data) {
this.data = data;
}
static getValidMove(leaf, localStart, worldTotal, checkLimit = true) {
const move = leaf.getLocalPoint(worldTotal, null, true);
PointHelper.move(move, localStart.x - leaf.x, localStart.y - leaf.y);
if (checkLimit) this.limitMove(leaf, move);
DragBoundsHelper.axisMove(leaf, move);
return move;
}
static limitMove(leaf, move) {
DragBoundsHelper.limitMove(leaf, 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 = {};
BoundsHelper.set(bounds, start.x - total.x, start.y - total.y, total.x, total.y);
BoundsHelper.unsign(bounds);
return bounds;
}
assignMove(total) {
tempMove.x = total ? this.totalX : this.moveX;
tempMove.y = total ? this.totalY : this.moveY;
}
};
DragEvent.BEFORE_DRAG = "drag.before_drag";
DragEvent.START = "drag.start";
DragEvent.DRAG = "drag";
DragEvent.END = "drag.end";
DragEvent.OVER = "drag.over";
DragEvent.OUT = "drag.out";
DragEvent.ENTER = "drag.enter";
DragEvent.LEAVE = "drag.leave";
DragEvent = __decorate([ registerUIEvent() ], DragEvent);
const MyDragEvent = DragEvent;
let DropEvent = class DropEvent extends PointerEvent {
static setList(data) {
DragEvent.setList(data);
}
static setData(data) {
DragEvent.setData(data);
}
};
DropEvent.DROP = "drop";
DropEvent = __decorate([ registerUIEvent() ], DropEvent);
let MoveEvent = class MoveEvent extends DragEvent {};
MoveEvent.BEFORE_MOVE = "move.before_move";
MoveEvent.START = "move.start";
MoveEvent.MOVE = "move";
MoveEvent.END = "move.end";
MoveEvent = __decorate([ registerUIEvent() ], MoveEvent);
let RotateEvent = class RotateEvent extends PointerEvent {};
RotateEvent.BEFORE_ROTATE = "rotate.before_rotate";
RotateEvent.START = "rotate.start";
RotateEvent.ROTATE = "rotate";
RotateEvent.END = "rotate.end";
RotateEvent = __decorate([ registerUIEvent() ], RotateEvent);
let SwipeEvent = class SwipeEvent extends DragEvent {};
SwipeEvent.SWIPE = "swipe";
SwipeEvent.LEFT = "swipe.left";
SwipeEvent.RIGHT = "swipe.right";
SwipeEvent.UP = "swipe.up";
SwipeEvent.DOWN = "swipe.down";
SwipeEvent = __decorate([ registerUIEvent() ], SwipeEvent);
let ZoomEvent = class ZoomEvent extends PointerEvent {};
ZoomEvent.BEFORE_ZOOM = "zoom.before_zoom";
ZoomEvent.START = "zoom.start";
ZoomEvent.ZOOM = "zoom";
ZoomEvent.END = "zoom.end";
ZoomEvent = __decorate([ registerUIEvent() ], ZoomEvent);
let KeyEvent = class KeyEvent extends UIEvent {};
KeyEvent.BEFORE_DOWN = "key.before_down";
KeyEvent.BEFORE_UP = "key.before_up";
KeyEvent.DOWN = "key.down";
KeyEvent.HOLD = "key.hold";
KeyEvent.UP = "key.up";
KeyEvent = __decorate([ registerUIEvent() ], 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: list,
data: data
});
},
getSwipeDirection(angle) {
if (angle < -45 && angle > -135) return SwipeEvent.UP; else if (angle > 45 && angle < 135) return SwipeEvent.DOWN; else if (angle <= 45 && angle >= -45) return SwipeEvent.RIGHT; else return 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(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: isUndefined(e.buttons) ? 1 : e.buttons === 0 ? pointerUpButtons : e.buttons,
origin: e
};
},
pathHasEventType(path, type) {
const {list: 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 LeafList;
const {list: 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 => LeafHelper.draggable(item) || !item.isLeafer && item.hasEvent(DragEvent.DRAG));
},
pathHasOutside(path) {
return path && path.list.some(item => item.isOutside);
}
};
const I = InteractionHelper;
const emptyList = new LeafList;
const {getDragEventData: getDragEventData, getDropEventData: getDropEventData, getSwipeEventData: 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: proxy} = this.interaction.selector;
const hasProxyList = proxy && proxy.list.length, dragList = DragEvent.list || this.draggableList || emptyList;
return this.dragging && (hasProxyList ? realDraggable ? emptyList : new LeafList(hover ? [ ...proxy.list, ...proxy.dragHoverExclude ] : proxy.list) : dragList);
}
checkDrag(data, canDrag) {
const {interaction: 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(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(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 (LeafHelper.draggable(leaf)) {
this.draggableList = new LeafList(leaf);
break;
}
}
}
drag(data) {
const {interaction: interaction, dragData: dragData, downData: downData} = this;
const {path: path, throughPath: 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(MoveEvent.BEFORE_MOVE, this.dragData);
interaction.emit(MoveEvent.MOVE, this.dragData);
} else if (this.dragging) {
this.dragReal();
interaction.emit(DragEvent.BEFORE_DRAG, this.dragData);
interaction.emit(DragEvent.DRAG, this.dragData);
}
}
dragReal(isDragEnd) {
const {interaction: interaction} = this, {running: running} = interaction;
const list = this.realDraggableList;
if (list.length && running) {
const {totalX: totalX, totalY: totalY} = this.dragData, {dragLimitAnimate: dragLimitAnimate} = interaction.p;
const checkLimitMove = !dragLimitAnimate || !!isDragEnd;
list.forEach(leaf => {
if (leaf.draggable) {
const axisDrag = isString(leaf.draggable);
const move = DragEvent.getValidMove(leaf, this.dragStartPoints[leaf.innerId], {
x: totalX,
y: totalY
}, checkLimitMove || axisDrag);
if (dragLimitAnimate && !axisDrag && isDragEnd) LeafHelper.animateMove(leaf, move, isNumber(dragLimitAnimate) ? dragLimitAnimate : .3); else leaf.move(move);
}
});
}
}
dragOverOrOut(data) {
const {interaction: interaction} = this;
const {dragOverPath: dragOverPath} = this;
const {path: path} = data;
this.dragOverPath = path;
if (dragOverPath) {
if (path.indexAt(0) !== dragOverPath.indexAt(0)) {
interaction.emit(DragEvent.OUT, data, dragOverPath);
interaction.emit(DragEvent.OVER, data, path);
}
} else interaction.emit(DragEvent.OVER, data, path);
}
dragEnterOrLeave(data) {
const {interaction: interaction} = this;
const {dragEnterPath: dragEnterPath} = this;
const {path: path} = data;
interaction.emit(DragEvent.LEAVE, data, dragEnterPath, path);
interaction.emit(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: interaction, downData: downData, dragData: dragData} = this;
if (!data) data = dragData;
const {path: path, throughPath: 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(MoveEvent.END, endDragData);
}
if (this.dragging) {
const dropList = this.getList();
this.dragging = false;
if (interaction.p.dragLimitAnimate) this.dragReal(true);
interaction.emit(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: interaction} = this;
if (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, DragEvent.data);
dropData.path = dragEnterPath;
this.interaction.emit(DropEvent.DROP, dropData);
this.interaction.emit(DragEvent.LEAVE, data, dragEnterPath);
}
dragReset() {
DragEvent.list = 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 = 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: 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 (State.updateEventStyle && !capture) State.updateEventStyle(leaf, type);
if (leaf.hasEvent(type, capture)) {
data.phase = capture ? 1 : leaf === data.target ? 2 : 3;
const event = 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: .5,
moveSpeed: .5,
rotateSpeed: .5,
delta: {
x: 80 / 4,
y: 8
}
},
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: pathHasEventType, pathCanDrag: pathCanDrag, pathHasOutside: 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 = DataHelper.clone(config);
this.tapCount = 0;
this.downKeyMap = {};
this.target = target;
this.canvas = canvas;
this.selector = selector;
this.defaultPath = new LeafList(target);
this.createTransformer();
this.dragger = new Dragger(this);
if (userConfig) this.config = 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(PointerEvent.BEFORE_DOWN, data);
this.emit(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: 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(PointerEvent.BEFORE_MOVE, data, this.defaultPath);
if (this.downData) {
const canDrag = 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(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: 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(PointerEvent.BEFORE_UP, data);
this.emit(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(PointerEvent.MENU, data);
this.waitMenuTap = true;
if (!this.downData && this.waitRightTap) this.menuTap(data);
}
menuTap(data) {
if (this.waitRightTap && this.waitMenuTap) {
this.emit(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;
this.emit(KeyEvent.BEFORE_DOWN, data, this.defaultPath);
const {code: code} = data;
if (!this.downKeyMap[code]) {
this.downKeyMap[code] = true;
Keyboard.setDownCode(code);
this.emit(KeyEvent.HOLD, data, this.defaultPath);
if (this.moveMode) {
this.cancelHover();
this.updateCursor();
}
}
this.emit(KeyEvent.DOWN, data, this.defaultPath);
}
keyUp(data) {
if (!this.config.keyEvent) return;
this.emit(KeyEvent.BEFORE_UP, data, this.defaultPath);
const {code: code} = data;
this.downKeyMap[code] = false;
Keyboard.setUpCode(code);
this.emit(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 LeafList);
this.pointerOverOrOut(data);
this.pointerEnterOrLeave(data);
}
}
pointerOverOrOut(data) {
const {path: path} = data;
const {overPath: overPath} = this;
this.overPath = path;
if (overPath) {
if (path.indexAt(0) !== overPath.indexAt(0)) {
this.emit(PointerEvent.OUT, data, overPath);
this.emit(PointerEvent.OVER, data, path);
}
} else {
this.emit(PointerEvent.OVER, data, path);
}
}
pointerEnterOrLeave(data) {
let {path: path} = data;
if (this.downData && !this.moveMode) {
path = path.clone();
this.downData.path.forEach(leaf => path.add(leaf));
}
const {enterPath: enterPath} = this;
this.enterPath = path;
this.emit(PointerEvent.LEAVE, data, enterPath, path);
this.emit(PointerEvent.ENTER, data, path, enterPath);
}
touchLeave(data) {
if (data.pointerType === "touch") {
if (this.enterPath) {
this.emit(PointerEvent.LEAVE, data);
if (this.dragger.dragging) this.emit(DropEvent.LEAVE, data);
}
}
}
tap(data) {
const {pointer: 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 = [ PointerEvent.DOUBLE_TAP, 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: hitRadius, through: through} = this.p;
const {bottomList: bottomList, target: target} = this;
if (!Platform.backgrounder && !data.origin) target && target.updateLayout();
const find = this.selector.getByPoint(data, hitRadius, Object.assign({
bottomList: bottomList,
name: data.type
}, options || {
through: 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: hoverData} = this;
if (hoverData) {
hoverData.path = this.defaultPath;
this.pointerHover(hoverData);
}
}
updateDownData(data, options, merge) {
const {downData: 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: 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: 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
};
const {bounds: bounds} = this.canvas;
point.x *= bounds.width / clientBounds.width;
point.y *= bounds.height / clientBounds.height;
if (this.p.snap) PointHelper.round(point);
return point;
}
emitTap(data) {
this.emit(PointerEvent.TAP, data);
this.emit(PointerEvent.CLICK, data);
}
emitDoubleTap(data) {
this.emit(PointerEvent.DOUBLE_TAP, data);
this.emit(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(PointerEvent.LONG_PRESS, data);
}, this.p.longPressTime);
}
longTap(data) {
let hasLong;
if (this.longPressed) {
this.emit(PointerEvent.LONG_TAP, data);
if (pathHasEventType(data.path, PointerEvent.LONG_TAP) || pathHasEventType(data.path, PointerEvent.LONG_PRESS)) hasLong = true;
}
this.longPressWaitCancel();
return hasLong;
}
longPressWaitCancel() {
if (this.longPressTimer) {
clearTimeout(this.longPressTimer);
this.longPressed = false;
}
}
__onResize() {
const {dragOut: dragOut} = this.m;
this.shrinkCanvasBounds = new Bounds(this.canvas.bounds);
this.shrinkCanvasBounds.spread(-(isNumber(dragOut) ? dragOut : 2));
}
__listenEvents() {
const {target: target} = this;
this.__eventIds = [ target.on_(ResizeEvent.RESIZE, this.__onResize, this) ];
target.once(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 CanvasManager {
constructor() {
super(...arguments);
this.maxTotal = 1e3;
this.pathList = new LeafList;
this.pixelList = new LeafList;
}
getPixelType(leaf, config) {
this.__autoClear();
this.pixelList.add(leaf);
return Creator.hitCanvas(config);
}
getPathType(leaf) {
this.__autoClear();
this.pathList.add(leaf);
return 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();
}
}
Platform.getSelector = function(leaf) {
return leaf.leafer ? leaf.leafer.selector : Platform.selector || (Platform.selector = Creator.selector());
};
const {toInnerRadiusPointOf: toInnerRadiusPointOf, copy: copy, setRadius: setRadius} = PointHelper;
const {hitRadiusPoint: hitRadiusPoint, hitPoint: hitPoint} = BoundsHelper;
const inner = {}, worldRadiusPoint = {};
const leaf = Leaf.prototype;
leaf.hit = function(worldPoint, hitRadius = 0) {
this.updateLayout();
copy(worldRadiusPoint, worldPoint);
setRadius(worldRadiusPoint, hitRadius);
const world = this.__world;
if (hitRadius ? !hitRadiusPoint(world, worldRadiusPoint) : !hitPoint(world, worldRadiusPoint)) return false;
return this.isBranch ? Platform.getSelector(this).hitPoint(Object.assign({}, worldRadiusPoint), hitRadius, {
target: this
}) : this.__hitWorld(worldRadiusPoint);
};
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 (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 Matrix;
const ui$1 = 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: 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: renderBounds} = this.__layout;
const size = Platform.image.hitCanvasSize;
const scale = h.hitScale = tempBounds.set(0, 0, size, size).getFitMatrix(renderBounds).a;
const {x: x, y: y, width: width, height: height} = tempBounds.set(renderBounds).scale(scale);
h.resize({
width: width,
height: height,
pixelRatio: 1
});
h.clear();
ImageManager.patternLocked = true;
this.__renderShape(h, {
matrix: matrix.setWith(this.__world).scaleWith(1 / scale).invertWith().translate(-x, -y),
ignoreFill: !isHitPixelFill,
ignoreStroke: !isHitPixelStroke
});
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: 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: 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 = UI.prototype, rect = Rect.prototype, box = 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) : BoundsHelper.hitRadiusPoint(this.__layout.boxBounds, inner);
};
Text.prototype.__drawHitPath = function(canvas) {
const {__lineHeight: __lineHeight, fontSize: fontSize, __baseLine: __baseLine, __letterSpacing: __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));
};
Group.prototype.pick = function(hitPoint, options) {
options || (options = emptyData);
this.updateLayout();
return Platform.getSelector(this).getByPoint(hitPoint, options.hitRadius || 0, Object.assign(Object.assign({}, options), {
target: this
}));
};
const canvas = 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: x, y: y, radiusX: radiusX, radiusY: radiusY} = radiusPoint;
if (offset) x -= offset.x, y -= offset.y;
tempBounds.set(x - radiusX, y - radiusY, radiusX * 2, radiusY * 2).scale(scale).ceil();
const {data: data} = this.context.getImageData(tempBounds.x, tempBounds.y, tempBounds.width || 1, 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;
};
export { App, Cursor, DragEvent, Dragger, DropEvent, HitCanvasManager, InteractionBase, InteractionHelper, KeyEvent, Keyboard, MoveEvent, MyDragEvent, MyPointerEvent, PointerButton, PointerEvent, RotateEvent, SwipeEvent, UIEvent, ZoomEvent };