Skip to content

Commit

Permalink
feat(stage): 支持rotate scale fix #92
Browse files Browse the repository at this point in the history
  • Loading branch information
roymondchen authored and jia000 committed Jun 9, 2022
1 parent 46b2632 commit a9936b5
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 92 deletions.
10 changes: 10 additions & 0 deletions packages/core/src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ class App extends EventEmitter {
Object.entries(styleObj).forEach(([key, value]) => {
if (key === 'backgroundImage') {
value && (results[key] = fillBackgroundImage(value));
} else if (key === 'transform' && typeof value !== 'string') {
results[key] = Object.entries(value as Record<string, string>)
.map(([transformKey, transformValue]) => {
let defaultValue = 0;
if (transformKey === 'scale') {
defaultValue = 1;
}
return `${transformKey}(${transformValue || defaultValue})`;
})
.join(' ');
} else if (!whiteList.includes(key) && value && /^[-]?[0-9]*[.]?[0-9]*$/.test(value)) {
results[key] = `${value / 100}rem`;
} else {
Expand Down
15 changes: 15 additions & 0 deletions packages/editor/src/utils/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ export const fillConfig = (config: FormConfig = []) => [
},
],
},
{
type: 'fieldset',
legend: '变形',
name: 'transform',
items: [
{
name: 'rotate',
text: '旋转角度',
},
{
name: 'scale',
text: '缩放',
},
],
},
],
},
],
Expand Down
114 changes: 82 additions & 32 deletions packages/stage/src/StageDragResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,49 +203,41 @@ export default class StageDragResize extends EventEmitter {

this.bindResizeEvent();
this.bindDragEvent();
this.bindRotateEvent();
this.bindScaleEvent();
}

private bindResizeEvent(): void {
if (!this.moveable) throw new Error('moveable 为初始化');

const frame = {
translate: [0, 0],
left: 0,
top: 0,
};

this.moveable
.on('resizeStart', (e) => {
if (e.dragStart) {
const rect = this.moveable!.getRect();
const offset = getAbsolutePosition(e.target as HTMLElement, rect);
e.dragStart.set([offset.left, offset.top]);
}
if (!this.target) return;

this.dragStatus = ActionStatus.START;
this.moveableHelper?.onResizeStart(e);

frame.top = this.target.offsetTop;
frame.left = this.target.offsetLeft;
})
.on('resize', ({ width, height, drag }) => {
.on('resize', (e) => {
const { width, height, drag } = e;
if (!this.moveable || !this.target || !this.dragEl) return;

const { beforeTranslate } = drag;
frame.translate = beforeTranslate;
this.dragStatus = ActionStatus.ING;

this.moveableHelper?.onResize(e);

this.target.style.width = `${width}px`;
this.target.style.height = `${height}px`;
this.dragEl.style.width = `${width}px`;
this.dragEl.style.height = `${height}px`;

// 流式布局
if (this.mode === Mode.SORTABLE) {
this.dragEl.style.top = `${beforeTranslate[1]}px`;
this.target.style.top = `0px`;
return;
}

this.dragEl.style.left = `${beforeTranslate[0]}px`;
this.dragEl.style.top = `${beforeTranslate[1]}px`;

const offset = getAbsolutePosition(this.target, { left: beforeTranslate[0], top: beforeTranslate[1] });

this.target.style.left = `${offset.left}px`;
this.target.style.top = `${offset.top}px`;
this.target.style.left = `${frame.left + beforeTranslate[0]}px`;
this.target.style.top = `${frame.top + beforeTranslate[1]}px`;
})
.on('resizeEnd', () => {
this.dragStatus = ActionStatus.END;
Expand Down Expand Up @@ -311,6 +303,60 @@ export default class StageDragResize extends EventEmitter {
});
}

private bindRotateEvent(): void {
if (!this.moveable) throw new Error('moveable 为初始化');

this.moveable
.on('rotateStart', (e) => {
this.dragStatus = ActionStatus.START;
this.moveableHelper?.onRotateStart(e);
})
.on('rotate', (e) => {
if (!this.target || !this.dragEl) return;
this.dragStatus = ActionStatus.ING;
this.moveableHelper?.onRotate(e);
const frame = this.moveableHelper?.getFrame(e.target);
this.target.style.transform = frame?.toCSSObject().transform || '';
})
.on('rotateEnd', (e) => {
this.dragStatus = ActionStatus.END;
const frame = this.moveableHelper?.getFrame(e.target);
this.emit('update', {
el: this.target,
style: {
transform: frame?.get('transform'),
},
});
});
}

private bindScaleEvent(): void {
if (!this.moveable) throw new Error('moveable 为初始化');

this.moveable
.on('scaleStart', (e) => {
this.dragStatus = ActionStatus.START;
this.moveableHelper?.onScaleStart(e);
})
.on('scale', (e) => {
if (!this.target || !this.dragEl) return;
this.dragStatus = ActionStatus.ING;
this.moveableHelper?.onScale(e);
const frame = this.moveableHelper?.getFrame(e.target);
this.target.style.transform = frame?.toCSSObject().transform || '';
})
.on('scaleEnd', (e) => {
this.dragStatus = ActionStatus.END;
const frame = this.moveableHelper?.getFrame(e.target);
this.emit('update', {
el: this.target,
style: {
transform: frame?.get('transform'),
},
});
});
}

private sort(): void {
if (!this.target || !this.ghostEl) throw new Error('未知错误');
const { top } = this.ghostEl.getBoundingClientRect();
Expand All @@ -331,14 +377,15 @@ export default class StageDragResize extends EventEmitter {
}

private update(isResize = false): void {
const rect = this.moveable!.getRect();
if (!this.target) return;

const offset =
this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : getAbsolutePosition(this.target as HTMLElement, rect);
this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: this.target.offsetLeft, top: this.target.offsetTop };

const left = this.calcValueByFontsize(offset.left);
const top = this.calcValueByFontsize(offset.top);
const width = this.calcValueByFontsize(rect.width);
const height = this.calcValueByFontsize(rect.height);
const width = this.calcValueByFontsize(this.target.clientWidth);
const height = this.calcValueByFontsize(this.target.clientHeight);

this.emit('update', {
el: this.target,
Expand Down Expand Up @@ -369,15 +416,16 @@ export default class StageDragResize extends EventEmitter {
}

private updateDragEl(el: HTMLElement) {
const { width, height } = el.getBoundingClientRect();
const offset = getOffset(el);
const { transform } = getComputedStyle(el);

this.dragEl.style.cssText = `
position: absolute;
transform: ${transform};
left: ${offset.left}px;
top: ${offset.top}px;
width: ${width}px;
height: ${height}px;
width: ${el.clientWidth}px;
height: ${el.clientHeight}px;
z-index: ${ZIndex.DRAG_EL};
`;

Expand Down Expand Up @@ -415,6 +463,8 @@ export default class StageDragResize extends EventEmitter {
dragArea: false,
draggable: true,
resizable: true,
scalable: false,
rotatable: false,
snappable: isAbsolute || isFixed,
snapGap: isAbsolute || isFixed,
snapThreshold: 5,
Expand Down
36 changes: 6 additions & 30 deletions packages/stage/src/TargetCalibrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ export default class TargetCalibrate extends EventEmitter {
}

public update(el: HTMLElement, prefix: String): HTMLElement {
const { width, height } = el.getBoundingClientRect();
const { left, top } = this.getOffset(el);
const { transform } = getComputedStyle(el);
this.operationEl.style.cssText = `
position: absolute;
transform: ${transform};
left: ${left}px;
top: ${top}px;
width: ${width}px;
height: ${height}px;
width: ${el.clientWidth}px;
height: ${el.clientHeight}px;
`;

this.operationEl.id = `${prefix}${el.id}`;
Expand All @@ -65,35 +66,10 @@ export default class TargetCalibrate extends EventEmitter {
}

private getOffset(el: HTMLElement): Offset {
const { transform } = getComputedStyle(el);
const { offsetParent } = el;

let left = el.offsetLeft;
let top = el.offsetTop;

if (transform.indexOf('matrix') > -1) {
let a = 1;
let b = 1;
let c = 1;
let d = 1;
let e = 0;
let f = 0;
transform.replace(
/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/,
($0: string, $1: string, $2: string, $3: string, $4: string, $5: string, $6: string): string => {
a = +$1;
b = +$2;
c = +$3;
d = +$4;
e = +$5;
f = +$6;
return transform;
},
);

left = a * left + c * top + e;
top = b * left + d * top + f;
}
const left = el.offsetLeft;
const top = el.offsetTop;

if (offsetParent) {
const parentOffset = this.getOffset(offsetParent as HTMLElement);
Expand Down
11 changes: 8 additions & 3 deletions packages/stage/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

import { MoveableOptions } from 'react-moveable/declaration/types';
import { MoveableOptions } from 'moveable';

import Core from '@tmagic/core';
import { Id, MApp, MNode } from '@tmagic/schema';
Expand Down Expand Up @@ -58,6 +58,7 @@ export type Rect = {
width: number;
height: number;
} & Offset;

export interface Offset {
left: number;
top: number;
Expand All @@ -72,10 +73,14 @@ export interface UpdateEventData {
el: HTMLElement;
ghostEl: HTMLElement;
style: {
width: number;
height: number;
width?: number;
height?: number;
left?: number;
top?: number;
transform?: {
rotate?: string;
scale?: string;
};
};
}

Expand Down
29 changes: 2 additions & 27 deletions packages/stage/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,10 @@ const getParents = (el: Element, relative: Element) => {
};

export const getOffset = (el: HTMLElement): Offset => {
const { transform } = getComputedStyle(el);
const { offsetParent } = el;

let left = el.offsetLeft;
let top = el.offsetTop;

if (transform.indexOf('matrix') > -1) {
let a = 1;
let b = 1;
let c = 1;
let d = 1;
let e = 0;
let f = 0;
transform.replace(
/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/,
($0: string, $1: string, $2: string, $3: string, $4: string, $5: string, $6: string): string => {
a = +$1;
b = +$2;
c = +$3;
d = +$4;
e = +$5;
f = +$6;
return transform;
},
);

left = a * left + c * top + e;
top = b * left + d * top + f;
}
const left = el.offsetLeft;
const top = el.offsetTop;

if (offsetParent) {
const parentOffset = getOffset(offsetParent as HTMLElement);
Expand Down

0 comments on commit a9936b5

Please sign in to comment.