dragon support drap api

This commit is contained in:
kangwei 2020-03-16 00:39:42 +08:00
parent 07a681511d
commit b9272d6bab

View File

@ -19,7 +19,7 @@ export interface LocateEvent {
/** /**
* *
*/ */
readonly originalEvent: MouseEvent; readonly originalEvent: MouseEvent | DragEvent;
/** /**
* *
*/ */
@ -124,7 +124,7 @@ const SHAKE_DISTANCE = 4;
/** /**
* mouse shake check * mouse shake check
*/ */
export function isShaken(e1: MouseEvent, e2: MouseEvent): boolean { export function isShaken(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent): boolean {
if ((e1 as any).shaken) { if ((e1 as any).shaken) {
return true; return true;
} }
@ -134,6 +134,19 @@ export function isShaken(e1: MouseEvent, e2: MouseEvent): boolean {
return Math.pow(e1.clientY - e2.clientY, 2) + Math.pow(e1.clientX - e2.clientX, 2) > SHAKE_DISTANCE; return Math.pow(e1.clientY - e2.clientY, 2) + Math.pow(e1.clientX - e2.clientX, 2) > SHAKE_DISTANCE;
} }
function isInvalidPoint(e: any, last: any): boolean {
return (
e.clientX === 0 &&
e.clientY === 0 &&
last &&
(Math.abs(last.clientX - e.clientX) > 5 || Math.abs(last.clientY - e.clientY) > 5)
);
}
function isSameAs(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent): boolean {
return e1.clientY === e2.clientY && e1.clientX === e2.clientX;
}
export function setShaken(e: any) { export function setShaken(e: any) {
e.shaken = true; e.shaken = true;
} }
@ -145,17 +158,29 @@ function getSourceSensor(dragObject: DragObject): ISimulator | null {
return dragObject.nodes[0]?.document.simulator || null; return dragObject.nodes[0]?.document.simulator || null;
} }
function makeSimulatorListener(masterSensors: ISimulator[]): (fn: (sdoc: Document) => void) => void { function makeEventsHandler(
boostEvent: MouseEvent | DragEvent,
sensors?: ISimulator[],
): (fn: (sdoc: Document) => void) => void {
const doc = boostEvent.view?.document || document;
if (doc === document && !isDragEvent(boostEvent)) {
sensors = undefined;
}
return (fn: (sdoc: Document) => void) => { return (fn: (sdoc: Document) => void) => {
masterSensors.forEach(sim => { fn(doc);
sensors?.forEach(sim => {
const sdoc = sim.contentDocument; const sdoc = sim.contentDocument;
if (sdoc) { if (sdoc && sdoc !== doc) {
fn(sdoc); fn(sdoc);
} }
}); });
}; };
} }
function isDragEvent(e: any): e is DragEvent {
return e?.type?.substr(0, 4) === 'drag';
}
export default class Dragon { export default class Dragon {
private sensors: ISensor[] = []; private sensors: ISensor[] = [];
@ -167,14 +192,17 @@ export default class Dragon {
return this._activeSensor; return this._activeSensor;
} }
@obx.ref private _dragging: boolean = false; @obx.ref private _dragging = false;
get dragging(): boolean { get dragging(): boolean {
return this._dragging; return this._dragging;
} }
private emitter = new EventEmitter(); private emitter = new EventEmitter();
private emptyImage: HTMLImageElement = new Image();
constructor(readonly designer: Designer) {} constructor(readonly designer: Designer) {
this.emptyImage.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
}
from(shell: Element, boost: (e: MouseEvent) => DragObject | null) { from(shell: Element, boost: (e: MouseEvent) => DragObject | null) {
const mousedown = (e: MouseEvent) => { const mousedown = (e: MouseEvent) => {
@ -197,22 +225,17 @@ export default class Dragon {
}; };
} }
boost(dragObject: DragObject, boostEvent: MouseEvent) { boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) {
const doc = document;
const sourceDoc = boostEvent.view?.document;
const masterSensors = this.getMasterSensors();
const listenSimulators = !sourceDoc || sourceDoc === doc ? makeSimulatorListener(masterSensors) : null;
const alwaysListen = listenSimulators ? doc : sourceDoc!;
const designer = this.designer; const designer = this.designer;
const masterSensors = this.getMasterSensors();
const handleEvents = makeEventsHandler(boostEvent, masterSensors);
const newBie = !isDragNodeObject(dragObject); const newBie = !isDragNodeObject(dragObject);
const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some(node => node.isSlotRoot); const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some(node => node.isSlotRoot);
const isBoostFromDragAPI = boostEvent.type.substr(0, 4) === 'drag';
let lastSensor: ISensor | undefined; let lastSensor: ISensor | undefined;
this._dragging = false; this._dragging = false;
// 禁用默认的文稿拖选
this.setNativeSelection(false);
const checkesc = (e: KeyboardEvent) => { const checkesc = (e: KeyboardEvent) => {
if (e.keyCode === 27) { if (e.keyCode === 27) {
designer.clearLocation(); designer.clearLocation();
@ -221,24 +244,46 @@ export default class Dragon {
}; };
let copy = false; let copy = false;
const checkcopy = (e: MouseEvent) => { const checkcopy = (e: MouseEvent | DragEvent | KeyboardEvent) => {
if (isDragEvent(e) && e.dataTransfer) {
if (newBie || forceCopyState) {
e.dataTransfer.dropEffect = 'copy';
}
return;
}
if (newBie) { if (newBie) {
return; return;
} }
if (e.altKey || e.ctrlKey) { if (e.altKey || e.ctrlKey) {
copy = true; copy = true;
this.setCopyState(true); this.setCopyState(true);
if (isDragEvent(e) && e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy';
}
} else { } else {
copy = false; copy = false;
if (!forceCopyState) { if (!forceCopyState) {
this.setCopyState(false); this.setCopyState(false);
if (isDragEvent(e) && e.dataTransfer) {
e.dataTransfer.dropEffect = 'move';
}
} }
} }
}; };
const drag = (e: MouseEvent) => { let lastArrive: any;
const drag = (e: MouseEvent | DragEvent) => {
checkcopy(e); checkcopy(e);
if (isInvalidPoint(e, lastArrive)) return;
if (lastArrive && isSameAs(e, lastArrive)) {
lastArrive = e;
return;
}
lastArrive = e;
const locateEvent = createLocateEvent(e); const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent); const sensor = chooseSensor(locateEvent);
if (sensor) { if (sensor) {
@ -251,6 +296,8 @@ export default class Dragon {
}; };
const dragstart = () => { const dragstart = () => {
this._dragging = true;
setShaken(boostEvent);
const locateEvent = createLocateEvent(boostEvent); const locateEvent = createLocateEvent(boostEvent);
if (newBie || forceCopyState) { if (newBie || forceCopyState) {
this.setCopyState(true); this.setCopyState(true);
@ -259,35 +306,51 @@ export default class Dragon {
} }
this.setDraggingState(true); this.setDraggingState(true);
// ESC cancel drag // ESC cancel drag
alwaysListen.addEventListener('keydown', checkesc, false); if (!isBoostFromDragAPI) {
listenSimulators && handleEvents(doc => {
listenSimulators(sdoc => { doc.addEventListener('keydown', checkesc, false);
sdoc.addEventListener('keydown', checkesc, false);
}); });
}
this.emitter.emit('dragstart', locateEvent); this.emitter.emit('dragstart', locateEvent);
}; };
const move = (e: MouseEvent) => { const move = (e: MouseEvent | DragEvent) => {
if (this.dragging) { if (isBoostFromDragAPI) {
e.preventDefault();
}
if (this._dragging) {
drag(e); drag(e);
return; return;
} }
if (isShaken(boostEvent, e)) { if (isShaken(boostEvent, e)) {
this._dragging = true;
setShaken(boostEvent);
dragstart(); dragstart();
drag(e); drag(e);
} }
}; };
let didDrop = true;
const drop = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
didDrop = true;
};
const over = (e?: any) => { const over = (e?: any) => {
if (e && isDragEvent(e)) {
e.preventDefault();
}
if (lastSensor) { if (lastSensor) {
lastSensor.deactiveSensor(); lastSensor.deactiveSensor();
} }
this.setNativeSelection(true); if (isBoostFromDragAPI) {
if (!didDrop) {
designer.clearLocation();
}
} else {
this.setNativeSelection(true);
}
this.clearState(); this.clearState();
let exception; let exception;
@ -300,31 +363,26 @@ export default class Dragon {
} }
} }
alwaysListen.removeEventListener('mousemove', move, true); handleEvents(doc => {
alwaysListen.removeEventListener('mouseup', over, true); if (isBoostFromDragAPI) {
alwaysListen.removeEventListener('mousedown', over, true); doc.removeEventListener('dragover', move, true);
alwaysListen.removeEventListener('keydown', checkesc, false); doc.removeEventListener('dragend', over, true);
alwaysListen.removeEventListener('keydown', checkcopy as any, false); doc.removeEventListener('drop', drop, true);
alwaysListen.removeEventListener('keyup', checkcopy as any, false); } else {
listenSimulators && doc.removeEventListener('mousemove', move, true);
listenSimulators(sdoc => { doc.removeEventListener('mouseup', over, true);
sdoc.removeEventListener('mousemove', move, true); }
sdoc.removeEventListener('mouseup', over, true); doc.removeEventListener('mousedown', over, true);
sdoc.removeEventListener('mousedown', over, true); doc.removeEventListener('keydown', checkesc, false);
sdoc.removeEventListener('keydown', checkesc, false); doc.removeEventListener('keydown', checkcopy, false);
sdoc.removeEventListener('keydown', checkcopy as any, false); doc.removeEventListener('keyup', checkcopy, false);
sdoc.removeEventListener('keyup', checkcopy as any, false); });
});
if (exception) { if (exception) {
throw exception; throw exception;
} }
}; };
const createLocateEvent = (e: MouseEvent): LocateEvent => { const createLocateEvent = (e: MouseEvent | DragEvent): LocateEvent => {
if (isLocateEvent(e)) {
return e;
}
const evt: any = { const evt: any = {
type: 'LocateEvent', type: 'LocateEvent',
dragObject, dragObject,
@ -339,7 +397,7 @@ export default class Dragon {
evt.globalY = e.clientY; evt.globalY = e.clientY;
} else { } else {
let srcSim: ISimulator | undefined; let srcSim: ISimulator | undefined;
let lastSim = lastSensor && isSimulator(lastSensor) ? lastSensor : null; const lastSim = lastSensor && isSimulator(lastSensor) ? lastSensor : null;
if (lastSim && lastSim.contentDocument === sourceDocument) { if (lastSim && lastSim.contentDocument === sourceDocument) {
srcSim = lastSim; srcSim = lastSim;
} else { } else {
@ -391,28 +449,46 @@ export default class Dragon {
return sensor; return sensor;
}; };
alwaysListen.addEventListener('mousemove', move, true); if (isDragEvent(boostEvent)) {
alwaysListen.addEventListener('mouseup', over, true); const { dataTransfer } = boostEvent;
alwaysListen.addEventListener('mousedown', over, true);
listenSimulators && if (dataTransfer) {
listenSimulators(sdoc => { dataTransfer.setDragImage(this.emptyImage, 0, 0);
// alwaysListen = global document dataTransfer.effectAllowed = 'all';
// listen others simulator iframe dataTransfer.dropEffect = newBie || forceCopyState ? 'copy' : 'move';
sdoc.addEventListener('mousemove', move, true);
sdoc.addEventListener('mouseup', over, true); try {
sdoc.addEventListener('mousedown', over, true); dataTransfer.setData('application/json', '{}');
}); } catch (ex) {
// ignore
}
}
dragstart();
} else {
this.setNativeSelection(false);
}
handleEvents(doc => {
if (isBoostFromDragAPI) {
doc.addEventListener('dragover', move, true);
didDrop = false;
doc.addEventListener('drop', drop, true);
doc.addEventListener('dragend', over, true);
} else {
doc.addEventListener('mousemove', move, true);
doc.addEventListener('mouseup', over, true);
}
doc.addEventListener('mousedown', over, true);
});
// future think: drag things from browser-out or a iframe-pane // future think: drag things from browser-out or a iframe-pane
if (!newBie) { if (!newBie && !isBoostFromDragAPI) {
alwaysListen.addEventListener('keydown', checkcopy as any, false); handleEvents(doc => {
alwaysListen.addEventListener('keyup', checkcopy as any, false); doc.addEventListener('keydown', checkcopy, false);
listenSimulators && doc.addEventListener('keyup', checkcopy, false);
listenSimulators(sdoc => { });
sdoc.addEventListener('keydown', checkcopy as any, false);
sdoc.addEventListener('keyup', checkcopy as any, false);
});
} }
} }