feat: prefect @idraw/renderer

This commit is contained in:
chenshenhai 2021-11-13 00:03:35 +08:00
parent 265c901b34
commit 6d4d3151c6
24 changed files with 113 additions and 752 deletions

View file

@ -15,6 +15,13 @@ const renderer = new Renderer({
// onlyRender: true,
});
renderer.on('load', (e) => {
console.log('load =', e)
})
renderer.on('loadComplete', (e) => {
console.log('loadComplete =', e)
})
renderer.on('drawFrame', (e) => {
console.log('drawFrame =', e)
})
@ -25,6 +32,8 @@ renderer.on('drawFrameComplete', (e) => {
renderer.render(canvas, data)
renderer.render(canvas, { elements: data.elements.splice(1, 2) }, { forceUpdate: false })
console.log(renderer.getContext())
// setTimeout(() => {
// renderer.render(canvas, { elements: data.elements.splice(1, 2) }, { forceUpdate: false })
// }, 2000);

View file

@ -1,15 +0,0 @@
const elementTypes = {
'text': {}, // TODO
'rect': {}, // TODO
'image': {}, // TODO
'svg': {}, // TODO
'circle': {}, // TODO
'html': {}, // TODO
};
export const elementNames = Object.keys(elementTypes);
// limitQbliqueAngle
export const LIMIT_QBLIQUE_ANGLE = 15;

View file

@ -1,12 +0,0 @@
export enum Mode {
NULL = 'null',
SELECT_ELEMENT = 'select-element',
SELECT_ELEMENT_LIST = 'select-element-list',
SELECT_ELEMENT_WRAPPER_CONTROLLER = 'select-element-wrapper-controller',
SELECT_AREA = 'select-area',
}
export enum CursorStatus {
DRAGGING = 'dragging',
NULL = 'null',
}

View file

@ -1,6 +1,7 @@
import { TypeData, TypeContext, TypeElement, TypeElemDesc } from '@idraw/types';
import util from '@idraw/util';
import { drawContext } from './lib/draw';
import { TypeLoadDataItem } from './lib/loader-event';
import Loader from './lib/loader';
import { RendererEvent } from './lib/renderer-event';
import {
@ -8,10 +9,10 @@ import {
_drawFrame, _retainQueueOneItem
} from './names';
const { Context } = util;
const { requestAnimationFrame } = window;
const { createUUID } = util.uuid;
const { deepClone } = util.data;
const { Context } = util;
type QueueItem = { data: TypeData };
enum DrawStatus {
@ -44,15 +45,15 @@ export default class Renderer extends RendererEvent {
this[_loader] = new Loader({
maxParallelNum: 6
});
this[_loader].on('load', (res) => {
this[_loader].on('load', (res: TypeLoadDataItem) => {
this[_drawFrame]();
// console.log('Load: ', res);
this.trigger('load', { element: res.element });
});
this[_loader].on('error', (res) => {
console.log('Loader Error: ', res);
this[_loader].on('error', (res: TypeLoadDataItem) => {
this.trigger('error', { element: res.element, error: res.error });
});
this[_loader].on('complete', (res) => {
// console.log('complete: ', res);
this[_loader].on('complete', () => {
this.trigger('loadComplete', { t: Date.now() })
});
}
@ -104,6 +105,10 @@ export default class Renderer extends RendererEvent {
this[_loader].load(data, changeResourceUUIDs || []);
}
getContext(): TypeContext | null {
return this[_ctx]
}
private [_freeze]() {
this[_status] = DrawStatus.FREEZE;
}
@ -147,10 +152,10 @@ export default class Renderer extends RendererEvent {
} else {
this[_status] = DrawStatus.FREE;
}
this.trigger('drawFrame', undefined)
this.trigger('drawFrame', { t: Date.now() })
if (this[_loader].isComplete() === true && this[_queue].length === 1 && this[_status] === DrawStatus.FREE) {
this.trigger('drawFrameComplete', undefined);
this.trigger('drawFrameComplete', { t: Date.now() });
this[_freeze]();
}
});

View file

@ -1,27 +0,0 @@
import { TypeConfig, TypeConfigStrict } from '@idraw/types';
import util from '@idraw/util';
const defaultConfig: TypeConfigStrict = {
elementWrapper: {
color: '#2ab6f1',
lockColor: '#aaaaaa',
controllerSize: 6,
lineWidth: 1,
lineDash: [4, 3],
}
};
function mergeConfig(config?: TypeConfig): TypeConfigStrict {
const result = util.data.deepClone(defaultConfig);
if (config) {
if (config.elementWrapper) {
result.elementWrapper = {...result.elementWrapper, ...config.elementWrapper};
}
}
return result;
}
export {
mergeConfig,
};

View file

@ -1,94 +0,0 @@
import {
TypeElement,
TypeElemDesc,
TypePoint,
TypeData,
TypeScreenData,
} from '@idraw/types';
export type TypeCoreEventSelectBaseArg = {
index: number | null;
uuid: string | null;
}
export type TypeCoreEventArgMap = {
'error': any;
'mouseOverScreen': TypePoint,
'mouseLeaveScreen': void,
'mouseOverElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'mouseLeaveElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'screenClickElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'screenDoubleClickElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'screenSelectElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'screenMoveElementStart': TypeCoreEventSelectBaseArg & TypePoint,
'screenMoveElementEnd': TypeCoreEventSelectBaseArg & TypePoint,
'screenChangeElement': TypeCoreEventSelectBaseArg & { width: number, height: number, angle: number};
'changeData': TypeData;
'changeScreen': TypeScreenData,
'drawFrameComplete': void;
'drawFrame': void;
}
export interface TypeCoreEvent {
on<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void): void
off<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void): void
trigger<T extends keyof TypeCoreEventArgMap >(key: T, p: TypeCoreEventArgMap[T]): void
}
export class CoreEvent implements TypeCoreEvent {
private _listeners: Map<string, ((p: any) => void)[]>;
constructor() {
this._listeners = new Map();
}
on<T extends keyof TypeCoreEventArgMap >(eventKey: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
if (this._listeners.has(eventKey)) {
const callbacks = this._listeners.get(eventKey);
callbacks?.push(callback);
this._listeners.set(eventKey, callbacks || []);
} else {
this._listeners.set(eventKey, [callback]);
}
}
off<T extends keyof TypeCoreEventArgMap >(eventKey: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
if (this._listeners.has(eventKey)) {
const callbacks = this._listeners.get(eventKey);
if (Array.isArray(callbacks)) {
for (let i = 0; i < callbacks?.length; i++) {
if (callbacks[i] === callback) {
callbacks.splice(i, 1);
break;
}
}
}
this._listeners.set(eventKey, callbacks || []);
}
}
trigger<T extends keyof TypeCoreEventArgMap >(eventKey: T, arg: TypeCoreEventArgMap[T]) {
const callbacks = this._listeners.get(eventKey);
if (Array.isArray(callbacks)) {
callbacks.forEach((cb) => {
cb(arg);
});
return true;
} else {
return false;
}
}
has<T extends keyof TypeCoreEventArgMap> (name: string) {
if (this._listeners.has(name)) {
const list: ((p: TypeCoreEventArgMap[T]) => void)[] | undefined = this._listeners.get(name);
if (Array.isArray(list) && list.length > 0) {
return true;
}
}
return false;
}
}

View file

@ -3,11 +3,9 @@ import {
// TypeElemDesc,
TypeElement,
} from '@idraw/types';
import util from '@idraw/util';
import util from '@idraw/util';
import { rotateElement } from './../transform';
import is from './../is';
const { istype, color } = util;
const { is, istype, color } = util;
export function clearContext(ctx: TypeContext) {
// ctx.setFillStyle('rgb(0 0 0 / 100%)');
@ -85,7 +83,7 @@ export function drawBoxBorder(
r = r + bw / 2;
}
const { desc } = elem;
if (desc.shadowColor !== undefined && util.color.isColorStr(desc.shadowColor)) {
if (desc.shadowColor !== undefined && color.isColorStr(desc.shadowColor)) {
ctx.setShadowColor(desc.shadowColor);
}
if (desc.shadowOffsetX !== undefined && is.number(desc.shadowOffsetX)) {

View file

@ -1,7 +1,5 @@
import { TypeContext, TypeElement, } from '@idraw/types';
// import util from '@idraw/util'
import { rotateElement } from './../transform';
// import is from './../is';
import { clearContext } from './base';
export function drawCircle(ctx: TypeContext, elem: TypeElement<'circle'>) {

View file

@ -7,7 +7,8 @@ import util from '@idraw/util';
import Loader from '../loader';
import { clearContext, drawBox } from './base';
import { rotateElement } from './../transform';
import is from './../is';
const { is, color } = util;
export function drawText(
ctx: TypeContext,
@ -73,7 +74,7 @@ export function drawText(
if (lines.length * fontHeight < elem.h) {
_y += ((elem.h - lines.length * fontHeight) / 2);
}
if (desc.textShadowColor !== undefined && util.color.isColorStr(desc.textShadowColor)) {
if (desc.textShadowColor !== undefined && color.isColorStr(desc.textShadowColor)) {
ctx.setShadowColor(desc.textShadowColor);
}
if (desc.textShadowOffsetX !== undefined && is.number(desc.textShadowOffsetX)) {
@ -98,7 +99,7 @@ export function drawText(
}
// draw text stroke
if (util.color.isColorStr(desc.strokeColor) && desc.strokeWidth !== undefined && desc.strokeWidth > 0) {
if (color.isColorStr(desc.strokeColor) && desc.strokeWidth !== undefined && desc.strokeWidth > 0) {
let _y = elem.y;
if (lines.length * fontHeight < elem.h) {
_y += ((elem.h - lines.length * fontHeight) / 2);

View file

@ -1,5 +0,0 @@
class ElementBase {
constructor() {
}
}

View file

@ -1,6 +0,0 @@
class ElementController {
// TODO
}
export default ElementController;

View file

@ -1,26 +0,0 @@
import ElementController from './element-controller';
class ElementHub {
private _controllerMap: Map<string, ElementController> = new Map();
constructor() {
// TODO
}
register(type: string, controller: ElementController) {
if (this._controllerMap.has(type) !== true) {
this._controllerMap.set(type, controller)
}
}
clear() {
this._controllerMap.clear();
}
getDrawActions() {
// TODO
}
}
export default ElementHub;

View file

@ -1,12 +1,7 @@
export * from './draw/index';
export * from './check';
export * from './config';
export * from './core-event';
export * from './diff';
export * from './is';
export * from './loader-event';
export * from './loader';
export * from './parse';
export * from './temp';
export * from './transform';
export * from './value';

View file

@ -1,19 +1,23 @@
import { TypeElement, TypeElemDesc } from '@idraw/types';
export type TypeLoadDataItem = {
uuid: string,
type: 'image' | 'svg' | 'html',
status: 'null' | 'loaded' | 'fail',
content: null | HTMLImageElement | HTMLCanvasElement,
elemW: number;
elemH: number;
source: string,
element: TypeElement<keyof TypeElemDesc>
error?: any,
}
export type TypeLoadData = {
[uuid: string]: {
type: 'image' | 'svg' | 'html',
status: 'null' | 'loaded' | 'fail',
content: null | HTMLImageElement | HTMLCanvasElement,
elemW: number;
elemH: number;
source: string,
error?: any,
}
[uuid: string]: TypeLoadDataItem
}
export type TypeLoaderEventArgMap = {
'complete': undefined;
'complete': void;
'load': TypeLoadData[string];
'error': TypeLoadData[string];
}

View file

@ -4,6 +4,7 @@ import { LoaderEvent, TypeLoadData, TypeLoaderEventArgMap } from './loader-event
import { filterScript } from './../util/filter';
const { loadImage, loadSVG, loadHTML } = util.loader;
const { deepClone } = util.data;
type Options = {
maxParallelNum: number
@ -175,12 +176,14 @@ export default class Loader {
elemH = _elem.desc.height || elem.h;
}
return {
uuid: elem.uuid,
type: type,
status: 'null',
content: null,
source,
elemW,
elemH,
element: deepClone(elem),
};
}
@ -234,12 +237,14 @@ export default class Loader {
const status = _loadAction();
this._storageLoadData[uuid] = {
uuid,
type: this._currentLoadData[uuid].type,
status: 'loaded',
content: image,
source: this._currentLoadData[uuid].source,
elemW: this._currentLoadData[uuid].elemW,
elemH: this._currentLoadData[uuid].elemH,
element: this._currentLoadData[uuid].element,
};
if (loadUUIDList.length === 0 && uuids.length === 0 && status === true) {
@ -247,12 +252,14 @@ export default class Loader {
this._loadTask();
}
this._event.trigger('load', {
uuid: this._storageLoadData[uuid]?.uuid,
type: this._storageLoadData[uuid].type,
status: this._storageLoadData[uuid].status,
content: this._storageLoadData[uuid].content,
source: this._storageLoadData[uuid].source,
elemW: this._storageLoadData[uuid].elemW,
elemH: this._storageLoadData[uuid].elemH,
element: this._storageLoadData[uuid]?.element,
});
}).catch((err) => {
console.warn(err);
@ -262,6 +269,7 @@ export default class Loader {
if (this._currentLoadData[uuid]) {
this._storageLoadData[uuid] = {
uuid,
type: this._currentLoadData[uuid]?.type,
status: 'fail',
content: null,
@ -269,6 +277,7 @@ export default class Loader {
source: this._currentLoadData[uuid]?.source,
elemW: this._currentLoadData[uuid]?.elemW,
elemH: this._currentLoadData[uuid]?.elemH,
element: this._currentLoadData[uuid]?.element,
};
}
@ -279,12 +288,14 @@ export default class Loader {
if (this._currentLoadData[uuid]) {
this._event.trigger('error', {
uuid: uuid,
type: this._storageLoadData[uuid]?.type,
status: this._storageLoadData[uuid]?.status,
content: this._storageLoadData[uuid]?.content,
source: this._storageLoadData[uuid]?.source,
elemW: this._storageLoadData[uuid]?.elemW,
elemH: this._storageLoadData[uuid]?.elemH,
element: this._storageLoadData[uuid]?.element,
});
}

View file

@ -1,7 +1,12 @@
import { TypeElement, TypeElemDesc } from '@idraw/types';
export type TypeRendererEventArgMap = {
'drawFrame': void;
'drawFrameComplete': void;
'drawFrame': { t: number };
'drawFrameComplete': { t: number };
'load': { element: TypeElement<keyof TypeElemDesc> },
'loadComplete': { t: number },
'error': { element: TypeElement<keyof TypeElemDesc>, error: any }
}
export interface TypeRendererEvent {

View file

@ -1,182 +0,0 @@
import {
TypeElement, TypeElemDesc, TypeElementBase,
} from '@idraw/types';
import util from '@idraw/util';
import {
_board, _data, _opts, _config, _renderer, _element, _helper,
_tempData, _draw, _coreEvent, _emitChangeScreen, _emitChangeData,
} from './../names';
import { diffElementResourceChange } from './../lib/diff';
import Core from './../index';
import { Mode } from './../constant/static';
// const { time } = util;
const { deepClone } = util.data;
const { createUUID } = util.uuid;
export function getSelectedElements(core: Core): TypeElement<keyof TypeElemDesc>[] {
const elems: TypeElement<keyof TypeElemDesc>[] = [];
let list: string[] = [];
const uuid = core[_tempData].get('selectedUUID');
if (typeof uuid === 'string' && uuid) {
list.push(uuid);
} else {
list = core[_tempData].get('selectedUUIDList');
}
list.forEach((uuid) => {
const index = core[_helper].getElementIndexByUUID(uuid);
if (index !== null && index >= 0) {
const elem = core[_data]?.elements[index];
if (elem) elems.push(elem);
}
});
return deepClone(elems);
}
export function getElement(core: Core, uuid: string): TypeElement<keyof TypeElemDesc>|null {
let elem: TypeElement<keyof TypeElemDesc>|null = null;
const index = core[_helper].getElementIndexByUUID(uuid);
if (index !== null && core[_data].elements[index]) {
elem = deepClone(core[_data].elements[index]);
}
return elem;
}
export function getElementByIndex(core: Core, index: number): TypeElement<keyof TypeElemDesc>|null {
let elem: TypeElement<keyof TypeElemDesc>|null = null;
if (index >=0 && core[_data].elements[index]) {
elem = deepClone(core[_data].elements[index]);
}
return elem;
}
export function updateElement(core: Core, elem: TypeElement<keyof TypeElemDesc>) {
const _elem = deepClone(elem) as TypeElement<keyof TypeElemDesc>;
const data = core[_data];
const resourceChangeUUIDs: string[] = [];
for (let i = 0; i < data.elements.length; i++) {
if (_elem.uuid === data.elements[i]?.uuid) {
const result = diffElementResourceChange(data.elements[i], _elem);
if (typeof result === 'string') {
resourceChangeUUIDs.push(result);
}
data.elements[i] = _elem;
break;
}
}
core[_emitChangeData]();
core[_draw]({ resourceChangeUUIDs });
}
export function selectElementByIndex(core: Core, index: number, opts?: { useMode?: boolean }): void {
if (core[_tempData].get('onlyRender') === true) return;
if (core[_data].elements[index]) {
const uuid = core[_data].elements[index].uuid;
if (opts?.useMode === true) {
core[_tempData].set('mode', Mode.SELECT_ELEMENT);
} else {
core[_tempData].set('mode', Mode.NULL);
}
if (typeof uuid === 'string') {
core[_tempData].set('selectedUUID', uuid);
core[_tempData].set('selectedUUIDList', []);
}
core[_draw]();
}
}
export function selectElement(core: Core, uuid: string, opts?: { useMode?: boolean }): void {
if (core[_tempData].get('onlyRender') === true) return;
const index = core[_helper].getElementIndexByUUID(uuid);
if (typeof index === 'number' && index >= 0) {
core.selectElementByIndex(index, opts);
}
}
export function moveUpElement(core: Core, uuid: string): void {
// if (this[_onlyRender] === true) return;
const index = core[_helper].getElementIndexByUUID(uuid);
if (typeof index === 'number' && index >= 0 && index < core[_data].elements.length - 1) {
const temp = core[_data].elements[index];
core[_data].elements[index] = core[_data].elements[index + 1];
core[_data].elements[index + 1] = temp;
}
core[_emitChangeData]();
core[_draw]();
}
export function moveDownElement(core: Core, uuid: string): void {
// if (this[_onlyRender] === true) return;
const index = core[_helper].getElementIndexByUUID(uuid);
if (typeof index === 'number' && index > 0 && index < core[_data].elements.length) {
const temp = core[_data].elements[index];
core[_data].elements[index] = core[_data].elements[index - 1];
core[_data].elements[index - 1] = temp;
}
core[_emitChangeData]();
core[_draw]();
}
export function addElement(core: Core, elem: TypeElementBase<keyof TypeElemDesc>): string | null {
// if (this[_onlyRender] === true) return null;
const _elem = deepClone(elem);
_elem.uuid = createUUID();
core[_data].elements.push(_elem);
core[_emitChangeData]();
core[_draw]();
return _elem.uuid;
}
export function deleteElement(core: Core, uuid: string) {
// if (this[_onlyRender] === true) return;
const index = core[_element].getElementIndex(core[_data], uuid);
if (index >= 0) {
core[_data].elements.splice(index, 1);
core[_emitChangeData]();
core[_draw]();
}
}
export function insertElementBefore(core: Core, elem: TypeElementBase<keyof TypeElemDesc>, beforeUUID: string) {
const index = core[_helper].getElementIndexByUUID(beforeUUID);
if (index !== null) {
return core.insertElementBeforeIndex(elem, index);
}
return null;
}
export function insertElementBeforeIndex(core: Core, elem: TypeElementBase<keyof TypeElemDesc>, index: number) {
const _elem = deepClone(elem);
_elem.uuid = createUUID();
if (index >= 0) {
core[_data].elements.splice(index, 0, _elem);
core[_emitChangeData]();
core[_draw]();
return _elem.uuid;
}
return null;
}
export function insertElementAfter(core: Core, elem: TypeElementBase<keyof TypeElemDesc>, beforeUUID: string) {
const index = core[_helper].getElementIndexByUUID(beforeUUID);
if (index !== null) {
return core.insertElementAfterIndex(elem, index);
}
return null;
}
export function insertElementAfterIndex(core: Core, elem: TypeElementBase<keyof TypeElemDesc>, index: number) {
const _elem = deepClone(elem);
_elem.uuid = createUUID();
if (index >= 0) {
core[_data].elements.splice(index + 1, 0, _elem);
core[_emitChangeData]();
core[_draw]();
return _elem.uuid;
}
return null;
}

View file

@ -1,298 +0,0 @@
import { TypePoint, TypeHelperWrapperControllerDirection } from '@idraw/types';
import util from '@idraw/util';
import Core from './../index';
import {
_board, _data, _opts, _config, _renderer, _element, _helper,
_tempData, _draw, _coreEvent, _mapper,
_emitChangeScreen, _emitChangeData,
} from './../names';
import { Mode, CursorStatus } from './../constant/static';
const { time } = util;
const { deepClone } = util.data;
export function initEvent(core: Core): void {
if (core[_tempData].get('hasInited') === true) {
return;
}
core[_board].on('hover', time.throttle(handleHover(core), 32));
core[_board].on('leave', time.throttle(handleLeave(core), 32));
core[_board].on('point', time.throttle(handleClick(core), 16));
core[_board].on('doubleClick', handleDoubleClick(core));
if (core[_tempData].get('onlyRender') === true) {
return;
}
core[_board].on('point', handlePoint(core));
core[_board].on('moveStart', handleMoveStart(core));
core[_board].on('move', time.throttle(handleMove(core), 16));
core[_board].on('moveEnd', handleMoveEnd(core));
core[_renderer].on('drawFrame', () => {
core[_coreEvent].trigger('drawFrame', undefined);
});
core[_renderer].on('drawFrameComplete', () => {
core[_coreEvent].trigger('drawFrameComplete', undefined);
})
core[_tempData].set('hasInited', true);
}
function handleDoubleClick(core: Core) {
return function ( point: TypePoint) {
const [index, uuid] = core[_element].isPointInElement(point, core[_data]);
if (index >= 0 && uuid) {
const elem = deepClone(core[_data].elements?.[index]);
if (elem?.operation?.invisible !== true) {
core[_coreEvent].trigger(
'screenDoubleClickElement',
{ index, uuid, element: deepClone(core[_data].elements?.[index])}
);
}
}
core[_draw]();
}
}
function handlePoint(core: Core) {
return function(point: TypePoint): void {
if (!core[_mapper].isEffectivePoint(point)) {
return;
}
if (core[_helper].isPointInElementList(point, core[_data])) {
// Coontroll Element-List
core[_tempData].set('mode', Mode.SELECT_ELEMENT_LIST);
} else {
const {
uuid, selectedControllerDirection
} = core[_helper].isPointInElementWrapperController(point, core[_data]);
if (uuid && selectedControllerDirection) {
// Controll Element-Wrapper
core[_tempData].set('mode', Mode.SELECT_ELEMENT_WRAPPER_CONTROLLER);
core[_tempData].set('selectedControllerDirection', selectedControllerDirection);
core[_tempData].set('selectedUUID', uuid);
} else {
const [index, uuid] = core[_element].isPointInElement(point, core[_data]);
if (index >= 0 && core[_data].elements[index]?.operation?.invisible !== true) {
// Controll Element
core.selectElementByIndex(index, { useMode: true });
if (typeof uuid === 'string' && core[_coreEvent].has('screenSelectElement')) {
core[_coreEvent].trigger(
'screenSelectElement',
{ index, uuid, element: deepClone(core[_data].elements?.[index])}
);
core[_emitChangeScreen]();
}
core[_tempData].set('mode', Mode.SELECT_ELEMENT);
} else {
// Controll Area
core[_tempData].set('selectedUUIDList', []);
core[_tempData].set('selectedUUID', null);
core[_tempData].set('mode', Mode.SELECT_AREA);
}
}
}
core[_draw]();
}
}
function handleClick(core: Core) {
return function(point: TypePoint): void {
const [index, uuid] = core[_element].isPointInElement(point, core[_data]);
if (index >= 0 && uuid) {
core[_coreEvent].trigger(
'screenClickElement',
{ index, uuid, element: deepClone(core[_data].elements?.[index])}
);
}
core[_draw]();
}
}
function handleMoveStart(core: Core) {
return function(point: TypePoint): void {
core[_tempData].set('prevPoint', point);
const uuid = core[_tempData].get('selectedUUID');
if (core[_tempData].get('mode') === Mode.SELECT_ELEMENT_LIST) {
// TODO
} else if (core[_tempData].get('mode') === Mode.SELECT_ELEMENT) {
if (typeof uuid === 'string' && core[_coreEvent].has('screenMoveElementStart')) {
core[_coreEvent].trigger('screenMoveElementStart', {
index: core[_element].getElementIndex(core[_data], uuid),
uuid,
x: point.x,
y: point.y
});
}
} else if (core[_tempData].get('mode') === Mode.SELECT_AREA) {
core[_helper].startSelectArea(point);
}
}
}
function handleMove(core: Core) {
return function(point: TypePoint): void {
if (core[_tempData].get('mode') === Mode.SELECT_ELEMENT_LIST) {
dragElements(core, core[_tempData].get('selectedUUIDList'), point, core[_tempData].get('prevPoint'));
core[_draw]();
core[_tempData].set('cursorStatus', CursorStatus.DRAGGING);
} else if (typeof core[_tempData].get('selectedUUID') === 'string') {
if (core[_tempData].get('mode') === Mode.SELECT_ELEMENT) {
dragElements(core, [core[_tempData].get('selectedUUID') as string], point, core[_tempData].get('prevPoint'));
core[_draw]();
core[_tempData].set('cursorStatus', CursorStatus.DRAGGING);
} else if (core[_tempData].get('mode') === Mode.SELECT_ELEMENT_WRAPPER_CONTROLLER && core[_tempData].get('selectedControllerDirection')) {
transfromElement(
core,
core[_tempData].get('selectedUUID') as string,
point,
core[_tempData].get('prevPoint'),
core[_tempData].get('selectedControllerDirection') as TypeHelperWrapperControllerDirection
);
core[_tempData].set('cursorStatus', CursorStatus.DRAGGING)
}
} else if (core[_tempData].get('mode') === Mode.SELECT_AREA) {
core[_helper].changeSelectArea(point);
core[_draw]();
}
core[_tempData].set('prevPoint', point)
}
}
function dragElements(core: Core, uuids: string[], point: TypePoint, prevPoint: TypePoint|null): void {
if (!prevPoint) {
return;
}
uuids.forEach((uuid) => {
const idx = core[_helper].getElementIndexByUUID(uuid);
if (idx === null) return;
const elem = core[_data].elements[idx];
if (elem?.operation?.lock !== true && elem?.operation?.invisible !== true) {
core[_element].dragElement(core[_data], uuid, point, prevPoint, core[_board].getContext().getTransform().scale);
}
});
core[_draw]();
}
function handleMoveEnd(core: Core) {
return function (point: TypePoint): void {
const uuid = core[_tempData].get('selectedUUID');
if (typeof uuid === 'string') {
const index = core[_element].getElementIndex(core[_data], uuid);
const elem = core[_data].elements[index];
if (elem) {
if (core[_coreEvent].has('screenMoveElementEnd')) {
core[_coreEvent].trigger('screenMoveElementEnd', {
index,
uuid,
x: point.x,
y: point.y
});
}
if (core[_coreEvent].has('screenChangeElement')) {
core[_coreEvent].trigger('screenChangeElement', {
index,
uuid,
width: elem.w,
height: elem.h,
angle: elem.angle || 0
});
}
core[_emitChangeData]();
}
} else if (core[_tempData].get('mode') === Mode.SELECT_AREA) {
const uuids = core[_helper].calcSelectedElements(core[_data]);
if (uuids.length > 0) {
core[_tempData].set('selectedUUIDList', uuids);
core[_tempData].set('selectedUUID', null);
} else {
core[_tempData].set('mode', Mode.NULL);
}
core[_helper].clearSelectedArea();
core[_draw]();
}
if (core[_tempData].get('mode') !== Mode.SELECT_ELEMENT) {
core[_tempData].set('selectedUUID', null);
}
core[_tempData].set('cursorStatus', CursorStatus.NULL);
core[_tempData].set('mode', Mode.NULL);
}
}
function handleHover(core: Core) {
return function (point: TypePoint): void {
let isMouseOverElement: boolean = false;
if (core[_tempData].get('mode') === Mode.SELECT_AREA) {
if (core[_tempData].get('onlyRender') !== true) core[_board].resetCursor();
} else if (core[_tempData].get('cursorStatus') === CursorStatus.NULL) {
const { cursor, elementUUID } = core[_mapper].judgePointCursor(point, core[_data]);
if (core[_tempData].get('onlyRender') !== true) core[_board].setCursor(cursor);
if (elementUUID) {
const index: number | null = core[_helper].getElementIndexByUUID(elementUUID);
if (index !== null && index >= 0) {
const elem = core[_data].elements[index];
if (elem?.operation?.lock === true || elem?.operation?.invisible === true) {
core[_board].resetCursor();
return;
}
if (core[_tempData].get('hoverUUID') !== elem.uuid) {
const preIndex = core[_helper].getElementIndexByUUID(core[_tempData].get('hoverUUID') || '');
if (preIndex !== null && core[_data].elements[preIndex]) {
core[_coreEvent].trigger('mouseLeaveElement', {
uuid: core[_tempData].get('hoverUUID'),
index: preIndex,
element: core[_data].elements[preIndex]
});
}
}
if (elem) {
core[_coreEvent].trigger('mouseOverElement', { uuid: elem.uuid, index, element: elem, });
core[_tempData].set('hoverUUID', elem.uuid);
isMouseOverElement = true;
}
}
}
}
if (isMouseOverElement !== true && core[_tempData].get('hoverUUID') !== null) {
const uuid = core[_tempData].get('hoverUUID');
const index: number | null = core[_helper].getElementIndexByUUID(uuid || '');
if (index !== null) core[_coreEvent].trigger('mouseLeaveElement', { uuid, index, element: core[_data].elements[index] })
core[_tempData].set('hoverUUID', null);
}
if (core[_coreEvent].has('mouseOverScreen')) core[_coreEvent].trigger('mouseOverScreen', point);
}
}
function handleLeave(core: Core) {
return function(): void {
if (core[_coreEvent].has('mouseLeaveScreen')) {
core[_coreEvent].trigger('mouseLeaveScreen', undefined);
}
}
}
function transfromElement(
core: Core,
uuid: string, point: TypePoint, prevPoint: TypePoint|null, direction: TypeHelperWrapperControllerDirection
): null | {
width: number,
height: number,
angle: number,
} {
if (!prevPoint) {
return null;
}
const result = core[_element].transformElement(core[_data], uuid, point, prevPoint, core[_board].getContext().getTransform().scale, direction);
core[_draw]();
return result;
}

View file

@ -8,4 +8,5 @@ export * from './lib/config';
export * from './lib/core';
export * from './lib/screen';
export * from './lib/device';
export * from './lib/plugin';
export * from './lib/plugin';
export * from './lib/common';

View file

@ -0,0 +1,38 @@
type TypeIs = {
x: (value: any) => boolean,
y: (value: any) => boolean,
w: (value: any) => boolean,
h: (value: any) => boolean,
angle: (value: any) => boolean,
number: (value: any) => boolean,
borderWidth: (value: any) => boolean,
borderRadius: (value: any) => boolean,
color: (value: any) => boolean,
imageSrc: (value: any) => boolean,
imageURL: (value: any) => boolean,
imageBase64: (value: any) => boolean,
svg: (value: any) => boolean,
html: (value: any) => boolean,
text: (value: any) => boolean,
fontSize: (value: any) => boolean,
fontWeight: (value: any) => boolean,
lineHeight: (value: any) => boolean,
textAlign: (value: any) => boolean,
fontFamily: (value: any) => boolean,
strokeWidth: (value: any) => boolean,
}
type TypeCheck = {
attrs: (value: any) => boolean,
rectDesc: (value: any) => boolean,
circleDesc: (value: any) => boolean,
imageDesc: (value: any) => boolean,
svgDesc: (value: any) => boolean,
htmlDesc: (value: any) => boolean,
textDesc: (value: any) => boolean,
}
export {
TypeIs,
TypeCheck,
}

View file

@ -8,6 +8,9 @@ const types = {
uuid: 'Object',
istype: 'Object',
data: 'Object',
is: 'Object',
check: 'Object',
Context: 'Function',
}
function getType (data: any): string {

View file

@ -6,8 +6,12 @@ import { deepClone } from './lib/data';
import istype from './lib/istype';
import { loadImage, loadSVG, loadHTML } from './lib/loader';
import Context from './lib/context';
import is from './lib/is';
import check from './lib/check';
export default {
is,
check,
time: {
delay,
compose,

View file

@ -1,10 +1,9 @@
import { TypeElementAttrs } from '@idraw/types';
// import { TypeElementAttrs } from '@idraw/types';
import is from './is';
function attrs(
attrs: TypeElementAttrs
attrs: any
): boolean {
const { x, y, w, h, angle } = attrs;
if (!(is.x(x) && is.y(y) && is.w(w) && is.h(h) && is.angle(angle))) {
@ -146,18 +145,5 @@ const check = {
htmlDesc,
};
type TypeCheck = {
attrs: (value: any) => boolean,
rectDesc: (value: any) => boolean,
circleDesc: (value: any) => boolean,
imageDesc: (value: any) => boolean,
svgDesc: (value: any) => boolean,
htmlDesc: (value: any) => boolean,
textDesc: (value: any) => boolean,
}
export {
TypeCheck
};
export default check;

View file

@ -1,8 +1,4 @@
import util from "@idraw/util";
const { isColorStr } = util.color;
import { isColorStr } from './color';
function number(value: any) {
return (typeof value === 'number' && (value > 0 || value <= 0));
@ -97,7 +93,7 @@ function fontWeight(value: any) {
return ['bold'].includes(value);
}
const is: TypeIs = {
const is = {
x, y, w, h, angle, number,
borderWidth, borderRadius, color,
imageSrc, imageURL, imageBase64, svg, html,
@ -105,33 +101,5 @@ const is: TypeIs = {
strokeWidth,
};
type TypeIs = {
x: (value: any) => boolean,
y: (value: any) => boolean,
w: (value: any) => boolean,
h: (value: any) => boolean,
angle: (value: any) => boolean,
number: (value: any) => boolean,
borderWidth: (value: any) => boolean,
borderRadius: (value: any) => boolean,
color: (value: any) => boolean,
imageSrc: (value: any) => boolean,
imageURL: (value: any) => boolean,
imageBase64: (value: any) => boolean,
svg: (value: any) => boolean,
html: (value: any) => boolean,
text: (value: any) => boolean,
fontSize: (value: any) => boolean,
fontWeight: (value: any) => boolean,
lineHeight: (value: any) => boolean,
textAlign: (value: any) => boolean,
fontFamily: (value: any) => boolean,
strokeWidth: (value: any) => boolean,
}
export default is;
export {
TypeIs,
};