feat: add coreEvent and rewrite image render

This commit is contained in:
chenshenhai 2021-06-05 14:36:32 +08:00
parent 90412a04e1
commit 750c16412c
15 changed files with 351 additions and 103 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

View file

@ -9,9 +9,6 @@ const data = {
w: 200,
h: 100,
type: 'circle',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#f0f0f0',
}
@ -24,9 +21,6 @@ const data = {
h: 120,
// angle: 30,
type: 'rect',
borderRadius: 60,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#cccccc',
}
@ -39,9 +33,6 @@ const data = {
h: 20,
type: 'rect',
angle: 80,
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#c0c0c0',
}
@ -53,9 +44,6 @@ const data = {
w: 200,
h: 100,
type: 'rect',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#e0e0e0',
}

View file

@ -9,11 +9,11 @@ const data = {
w: 200,
h: 100,
type: 'rect',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#f0f0f0',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
}
},
{
@ -24,11 +24,11 @@ const data = {
h: 120,
// angle: 30,
type: 'rect',
borderRadius: 60,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#cccccc',
borderRadius: 60,
borderWidth: 10,
borderColor: '#bd0b64',
}
},
{
@ -39,11 +39,11 @@ const data = {
h: 20,
type: 'rect',
angle: 80,
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#c0c0c0',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
}
},
{
@ -53,11 +53,11 @@ const data = {
w: 200,
h: 100,
type: 'rect',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
color: '#e0e0e0',
borderRadius: 20,
borderWidth: 10,
borderColor: '#bd0b64',
}
}
]

View file

@ -9,14 +9,14 @@ const data = {
w: 200,
h: 100,
type: 'text',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
desc: {
fontSize: 20,
color: '#333333',
text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。',
fontFamily: ''
fontFamily: '',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
}
},
{
@ -27,13 +27,13 @@ const data = {
h: 120,
// angle: 30,
type: 'text',
borderRadius: 60,
borderWidth: 10,
borderColor: '#bd0b64',
desc: {
fontSize: 20,
text: 'Hello Text',
color: '#666666',
borderRadius: 60,
borderWidth: 10,
borderColor: '#bd0b64',
}
},
{
@ -43,15 +43,15 @@ const data = {
w: 200,
h: 100,
type: 'text',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
desc: {
fontSize: 20,
color: '#333333',
text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。',
fontFamily: '',
textAlign: 'right',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
}
},
{
@ -61,15 +61,15 @@ const data = {
w: 200,
h: 100,
type: 'text',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
desc: {
fontSize: 20,
color: '#333333',
text: '生活就像海洋,只有意志坚强的人,才能到达彼岸。',
fontFamily: '',
textAlign: 'left',
borderRadius: 20,
borderWidth: 2,
borderColor: '#bd0b64',
}
}
]

View file

@ -19,6 +19,23 @@ const core = new Core(mount, {
});
core.on('error', (data) => {
console.log('error: ', data);
});
core.on('screenSelectElement', (data) => {
console.log('screenSelectElement: ', data);
});
core.on('screenMoveElementStart', (data) => {
console.log('screenMoveElementStart: ', data);
});
core.on('screenMoveElementEnd', (data) => {
console.log('screenMoveElementEnd: ', data);
});
core.on('screenChangeElement', (data) => {
console.log('screenChangeElement: ', data);
});
core.setData(data);
core.draw();

View file

@ -1,10 +1,19 @@
import { TypeData, TypePoint, TypeHelperWrapperDotDirection, TypeConfig, TypeConfigStrict } from '@idraw/types';
import {
TypeData,
TypePoint,
TypeHelperWrapperDotDirection,
TypeConfig,
TypeConfigStrict,
TypeElement,
TypeElemDesc
} from '@idraw/types';
import Board from '@idraw/board';
import util from '@idraw/util';
import { Renderer } from './lib/renderer';
import { Element } from './lib/element';
import { Helper } from './lib/helper';
import { mergeConfig } from './lib/config';
import { CoreEvent, TypeCoreEventArgMap } from './lib/core-event';
const { time } = util;
const { deepClone } = util.data;
@ -34,6 +43,7 @@ const _mode = Symbol('_mode');
const _selectedUUID = Symbol('_selectedUUID');
const _prevPoint = Symbol('_prevPoint');
const _selectedDotDirection = Symbol('_selectedDotDirection');
const _coreEvent = Symbol('_coreEvent');
class Core {
@ -46,6 +56,7 @@ class Core {
private [_helper]: Helper;
private [_hasInited] = false;
private [_mode]: Mode = Mode.NULL;
private [_coreEvent]: CoreEvent = new CoreEvent();
private [_selectedUUID]: string | null = null;
private [_prevPoint]: TypePoint | null = null;
@ -67,7 +78,7 @@ class Core {
this[_helper].updateConfig(this[_data], {
selectedUUID: this[_selectedUUID],
devicePixelRatio: this[_opts].devicePixelRatio,
scale: this[_board].getTransform().scale // TODO
scale: this[_board].getTransform().scale,
});
this[_renderer].render(this[_data], this[_helper].getConfig());
}
@ -126,6 +137,27 @@ class Core {
setData(data: TypeData): void {
this[_data] = this[_element].initData(deepClone(data));
this.draw();
}
updateElement(elem: TypeElement<keyof TypeElemDesc>) {
const _elem = deepClone(elem) as TypeElement<keyof TypeElemDesc>;
const data = this[_data];
for (let i = 0; i < data.elements.length; i++) {
if (_elem.uuid === data.elements[i]?.uuid) {
data.elements[i] = _elem;
break;
}
}
this.draw();
}
on<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
this[_coreEvent].on(key, callback);
}
off<T extends keyof TypeCoreEventArgMap >(key: T, callback: (p: TypeCoreEventArgMap[T]) => void) {
this[_coreEvent].off(key, callback);
}
private _initEvent(): void {
@ -149,12 +181,27 @@ class Core {
} else {
const [index] = this[_element].isPointInElement(point, this[_data]);
this.selectElement(index);
if (typeof uuid === 'string') {
this[_coreEvent].trigger(
'screenSelectElement',
{ index, uuid, element: deepClone(this[_data].elements?.[index])}
);
}
}
this.draw();
}
private _handleMoveStart(point: TypePoint): void {
this[_prevPoint] = point;
const uuid = this[_selectedUUID];
if (typeof uuid === 'string') {
this[_coreEvent].trigger('screenMoveElementStart', {
index: this[_element].getElementIndex(this[_data], uuid),
uuid,
x: point.x,
y: point.y
});
}
}
private _handleMove(point: TypePoint): void {
@ -164,13 +211,44 @@ class Core {
this.draw();
} else if (this[_mode] === Mode.SELECT_ELEMENT_WRAPPER_DOT && this[_selectedDotDirection]) {
this._transfromElement(this[_selectedUUID] as string, point, this[_prevPoint], this[_selectedDotDirection] as TypeHelperWrapperDotDirection);
// const changeData = this._transfromElement(this[_selectedUUID] as string, point, this[_prevPoint], this[_selectedDotDirection] as TypeHelperWrapperDotDirection);
// const uuid = this[_selectedUUID];
// if (changeData && typeof uuid === 'string') {
// this[_coreEvent].trigger('screenChangeElement', {
// index: this[_element].getElementIndex(this[_data], uuid),
// uuid,
// width: changeData.width,
// height: changeData.height,
// angle: changeData.angle
// })
// }
}
}
this[_prevPoint] = point;
}
private _handleMoveEnd(): void {
private _handleMoveEnd(point: TypePoint): void {
const uuid = this[_selectedUUID];
if (typeof uuid === 'string') {
const index = this[_element].getElementIndex(this[_data], uuid);
const elem = this[_data].elements[index];
if (elem) {
this[_coreEvent].trigger('screenMoveElementEnd', {
index,
uuid,
x: point.x,
y: point.y
});
this[_coreEvent].trigger('screenChangeElement', {
index,
uuid,
width: elem.w,
height: elem.h,
angle: elem.angle || 0
});
}
}
this[_selectedUUID] = null;
this[_prevPoint] = null;
}
@ -183,12 +261,19 @@ class Core {
this.draw();
}
private _transfromElement(uuid: string, point: TypePoint, prevPoint: TypePoint|null, direction: TypeHelperWrapperDotDirection) {
private _transfromElement(
uuid: string, point: TypePoint, prevPoint: TypePoint|null, direction: TypeHelperWrapperDotDirection
): null | {
width: number,
height: number,
angle: number,
} {
if (!prevPoint) {
return;
return null;
}
this[_element].transformElement(this[_data], uuid, point, prevPoint, this[_board].getContext().getTransform().scale, direction);
const result = this[_element].transformElement(this[_data], uuid, point, prevPoint, this[_board].getContext().getTransform().scale, direction);
this.draw();
return result;
}
}

View file

@ -0,0 +1,82 @@
import {
TypeElement,
TypeElemDesc,
TypePoint
} from '@idraw/types';
export type TypeCoreEventSelectBaseArg = {
index: number;
uuid: string;
}
export type TypeCoreEventArgMap = {
'error': any;
'screenSelectElement': TypeCoreEventSelectBaseArg & { element: TypeElement<keyof TypeElemDesc> }
'screenMoveElementStart': TypeCoreEventSelectBaseArg & TypePoint,
'screenMoveElementEnd': TypeCoreEventSelectBaseArg & TypePoint,
'screenChangeElement': TypeCoreEventSelectBaseArg & { width: number, height: number, angle: number};
}
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

@ -1,6 +1,6 @@
import {
TypeContext,
TypeElemDesc,
// TypeElemDesc,
TypeElement,
} from '@idraw/types';
import util from '@idraw/util';
@ -21,14 +21,14 @@ export function drawBgColor(ctx: TypeContext, color: string) {
export function drawBox(
ctx: TypeContext,
elem: TypeElement<keyof TypeElemDesc>,
elem: TypeElement<'text' | 'rect'>,
pattern: string | CanvasPattern | null,
): void {
clearContext(ctx);
drawBoxBorder(ctx, elem);
rotateElement(ctx, elem, () => {
const { x, y, w, h } = elem;
let r: number = elem.borderRadius || 0;
let r: number = elem.desc.borderRadius || 0;
r = Math.min(r, w / 2, h / 2);
if (w < r * 2 || h < r * 2) {
r = 0;
@ -52,24 +52,24 @@ export function drawBox(
export function drawBoxBorder(
ctx: TypeContext,
elem: TypeElement<keyof TypeElemDesc>,
elem: TypeElement<'text'|'rect'>,
): void {
clearContext(ctx);
rotateElement(ctx, elem, () => {
if (!(elem.borderWidth && elem.borderWidth > 0)) {
if (!(elem.desc.borderWidth && elem.desc.borderWidth > 0)) {
return;
}
let bw = elem.borderWidth;
let bw = elem.desc.borderWidth;
let borderColor: string = '#000000';
if (color.isColorStr(elem.borderColor) === true) {
borderColor = elem.borderColor as string;
if (color.isColorStr(elem.desc.borderColor) === true) {
borderColor = elem.desc.borderColor as string;
}
const x = elem.x - bw / 2;
const y = elem.y - bw / 2;
const w = elem.w + bw;
const h = elem.h + bw;
let r: number = elem.borderRadius || 0;
let r: number = elem.desc.borderRadius || 0;
r = Math.min(r, w / 2, h / 2);
if (r < w / 2 && r < h / 2) {
r = r + bw / 2

View file

@ -1,23 +1,55 @@
import {
TypeContext,
TypeContext,
TypeElement,
TypeHelperConfig,
TypeElemDesc,
// TypePoint,
} from '@idraw/types';
import { rotateElement } from '../transform';
import Loader from '../loader';
import { drawBox } from './base';
export function drawImage(
export function drawImage<T extends keyof TypeElemDesc>(
ctx: TypeContext,
elem: TypeElement<'image'>,
elem: TypeElement<T>,
loader: Loader,
helperConfig: TypeHelperConfig
) {
const content = loader.getPattern(elem, {
forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
// const desc = elem.desc as TypeElemDesc['rect'];
const content = loader.getContent(elem.uuid);
rotateElement(ctx, elem, () => {
// ctx.setFillStyle(desc.color);
// ctx.fillRect(elem.x, elem.y, elem.w, elem.h);
if (content) {
// ctx.drawImage(content, 0, 0, elem.w, elem.h, elem.x, elem.y, elem.w, elem.h);
ctx.drawImage(content, elem.x, elem.y, elem.w, elem.h);
}
});
drawBox(ctx, elem, content);
}
// import {
// TypeContext,
// TypeElement,
// TypeHelperConfig,
// TypeElemDesc,
// } from '@idraw/types';
// import Loader from '../loader';
// import { drawBox } from './base';
// export function drawImage(
// ctx: TypeContext,
// elem: TypeElement<'image'>,
// loader: Loader,
// helperConfig: TypeHelperConfig
// ) {
// const content = loader.getPattern(elem, {
// forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
// });
// drawBox(ctx, elem, content);
// }

View file

@ -26,8 +26,6 @@ export function drawContext(
const size = ctx.getSize();
ctx.clearRect(0, 0, size.width, size.height);
drawElementWrapper(ctx, helperConfig);
if (typeof data.bgColor === 'string' && isColorStr(data.bgColor)) {
drawBgColor(ctx, data.bgColor);
}
@ -43,11 +41,11 @@ export function drawContext(
break;
}
case 'image': {
drawImage(ctx, elem as TypeElement<'image'>, loader, helperConfig);
drawImage(ctx, elem as TypeElement<'image'>, loader);
break;
}
case 'svg': {
drawSVG(ctx, elem as TypeElement<'svg'>, loader, helperConfig);
drawSVG(ctx, elem as TypeElement<'svg'>, loader);
break;
}
default: {
@ -56,5 +54,6 @@ export function drawContext(
}
}
}
drawElementWrapper(ctx, helperConfig);
}

View file

@ -1,22 +1,48 @@
import {
TypeContext,
TypeElement,
TypeHelperConfig,
TypeElemDesc,
} from '@idraw/types';
import { rotateElement } from '../transform';
import Loader from '../loader';
import { drawBox } from './base';
export function drawSVG(
export function drawSVG<T extends keyof TypeElemDesc>(
ctx: TypeContext,
elem: TypeElement<'svg'>,
elem: TypeElement<T>,
loader: Loader,
helperConfig: TypeHelperConfig
) {
const content = loader.getPattern(elem, {
forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
// const desc = elem.desc as TypeElemDesc['rect'];
const content = loader.getContent(elem.uuid);
rotateElement(ctx, elem, () => {
// ctx.setFillStyle(desc.color);
// ctx.fillRect(elem.x, elem.y, elem.w, elem.h);
if (content) {
// ctx.drawImage(content, 0, 0, elem.w, elem.h, elem.x, elem.y, elem.w, elem.h);
ctx.drawImage(content, elem.x, elem.y, elem.w, elem.h);
}
});
drawBox(ctx, elem, content);
}
// import {
// TypeContext,
// TypeElement,
// TypeHelperConfig,
// } from '@idraw/types';
// import Loader from '../loader';
// import { drawBox } from './base';
// export function drawSVG(
// ctx: TypeContext,
// elem: TypeElement<'svg'>,
// loader: Loader,
// helperConfig: TypeHelperConfig
// ) {
// const content = loader.getPattern(elem, {
// forceUpdate: helperConfig?.selectedElementWrapper?.uuid === elem.uuid
// });
// drawBox(ctx, elem, content);
// }

View file

@ -34,7 +34,12 @@ export class Element {
let uuid = null;
for (let i = data.elements.length - 1; i >= 0; i--) {
const ele = data.elements[i];
const bw = ele.borderWidth || 0;
let bw = 0;
// @ts-ignore
if (ele.desc?.borderWidth > 0) {
// @ts-ignore
bw = ele.desc.borderWidth;
}
rotateElement(ctx, ele, () => {
ctx.beginPath();
@ -70,10 +75,21 @@ export class Element {
data.elements[index].y += (moveY / scale);
}
transformElement(data: TypeData, uuid: string, point: TypePoint, prevPoint: TypePoint, scale: number, direction: TypeHelperWrapperDotDirection): void {
transformElement(
data: TypeData,
uuid: string,
point: TypePoint,
prevPoint: TypePoint,
scale: number,
direction: TypeHelperWrapperDotDirection
): null | {
width: number,
height: number,
angle: number,
} {
const index = this.getElementIndex(data, uuid);
if (!data.elements[index]) {
return;
return null;
}
const moveX = (point.x - prevPoint.x) / scale;
const moveY = (point.y - prevPoint.y) / scale;
@ -149,6 +165,12 @@ export class Element {
break;
}
}
return {
width: elem.w,
height: elem.h,
angle: elem.angle || 0,
}
}
getElementIndex(data: TypeData, uuid: string): number {

View file

@ -103,53 +103,48 @@ export class Helper implements TypeHelper {
const lineWidth = this._coreConfig.elementWrapper.lineWidth / scale;
const lineDash = this._coreConfig.elementWrapper.lineDash.map(n => (n / scale));
const rotateLimit = 12;
const borderWidth = elem.borderWidth || 0;
// if (!(
// elem.x - dotSize - borderWidth > 0
// && elem.y - dotSize - borderWidth > 0
// && elem.x - dotSize - borderWidth > 0)) {
// return;
// }
// @ts-ignore
const bw = elem.desc?.borderWidth || 0;
const wrapper: TypeHelperConfig['selectedElementWrapper'] = {
uuid,
dotSize: dotSize,
dots: {
topLeft: {
x: elem.x - dotSize - borderWidth,
y: elem.y - dotSize - borderWidth,
x: elem.x - dotSize - bw,
y: elem.y - dotSize - bw,
},
top: {
x: elem.x + elem.w / 2,
y: elem.y - dotSize - borderWidth,
y: elem.y - dotSize - bw,
},
topRight: {
x: elem.x + elem.w + dotSize + borderWidth,
y: elem.y - dotSize - borderWidth,
x: elem.x + elem.w + dotSize + bw,
y: elem.y - dotSize - bw,
},
right: {
x: elem.x + elem.w + dotSize + borderWidth,
x: elem.x + elem.w + dotSize + bw,
y: elem.y + elem.h / 2,
},
bottomRight: {
x: elem.x + elem.w + dotSize + borderWidth,
y: elem.y + elem.h + dotSize + borderWidth,
x: elem.x + elem.w + dotSize + bw,
y: elem.y + elem.h + dotSize + bw,
},
bottom: {
x: elem.x + elem.w / 2,
y: elem.y + elem.h + dotSize + borderWidth,
y: elem.y + elem.h + dotSize + bw,
},
bottomLeft: {
x: elem.x - dotSize - borderWidth,
y: elem.y + elem.h + dotSize + borderWidth,
x: elem.x - dotSize - bw,
y: elem.y + elem.h + dotSize + bw,
},
left: {
x: elem.x - dotSize - borderWidth,
x: elem.x - dotSize - bw,
y: elem.y + elem.h / 2,
},
rotate: {
x: elem.x + elem.w / 2,
y: elem.y - dotSize - (dotSize * 2 + rotateLimit) - borderWidth,
y: elem.y - dotSize - (dotSize * 2 + rotateLimit) - bw,
}
},
lineWidth: lineWidth,

View file

@ -9,10 +9,13 @@ type TypeElement<T extends keyof TypeElemDesc> = {
w: number;
h: number;
angle?: number;
desc: TypeElemDesc[T];
}
type TypeElemBoxDesc = {
borderRadius?: number;
borderWidth?: number;
borderColor?: string;
desc: TypeElemDesc[T];
}
type TypeElemDesc = {
@ -26,7 +29,7 @@ type TypeElemDesc = {
type TypeElemDescRect = {
color: string;
}
} & TypeElemBoxDesc
type TypeElemDescText = {
text: string;
@ -35,9 +38,8 @@ type TypeElemDescText = {
lineHeight?: number;
fontWeight?: string;
fontFamily?: string;
textAlign?: 'center' | 'left' | 'right'
// backgroundColor?: string;
}
textAlign?: 'center' | 'left' | 'right';
} & TypeElemBoxDesc
type TypeElemDescCircle = {
r: number;