feat(stage): 支持rotate scale fix #92

This commit is contained in:
roymondchen 2022-06-09 19:16:05 +08:00 committed by jia000
parent 46b26328ef
commit a9936b5276
6 changed files with 123 additions and 92 deletions

View File

@ -107,6 +107,16 @@ class App extends EventEmitter {
Object.entries(styleObj).forEach(([key, value]) => { Object.entries(styleObj).forEach(([key, value]) => {
if (key === 'backgroundImage') { if (key === 'backgroundImage') {
value && (results[key] = fillBackgroundImage(value)); 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)) { } else if (!whiteList.includes(key) && value && /^[-]?[0-9]*[.]?[0-9]*$/.test(value)) {
results[key] = `${value / 100}rem`; results[key] = `${value / 100}rem`;
} else { } else {

View File

@ -161,6 +161,21 @@ export const fillConfig = (config: FormConfig = []) => [
}, },
], ],
}, },
{
type: 'fieldset',
legend: '变形',
name: 'transform',
items: [
{
name: 'rotate',
text: '旋转角度',
},
{
name: 'scale',
text: '缩放',
},
],
},
], ],
}, },
], ],

View File

@ -203,49 +203,41 @@ export default class StageDragResize extends EventEmitter {
this.bindResizeEvent(); this.bindResizeEvent();
this.bindDragEvent(); this.bindDragEvent();
this.bindRotateEvent();
this.bindScaleEvent();
} }
private bindResizeEvent(): void { private bindResizeEvent(): void {
if (!this.moveable) throw new Error('moveable 为初始化'); if (!this.moveable) throw new Error('moveable 为初始化');
const frame = { const frame = {
translate: [0, 0], left: 0,
top: 0,
}; };
this.moveable this.moveable
.on('resizeStart', (e) => { .on('resizeStart', (e) => {
if (e.dragStart) { if (!this.target) return;
const rect = this.moveable!.getRect();
const offset = getAbsolutePosition(e.target as HTMLElement, rect); this.dragStatus = ActionStatus.START;
e.dragStart.set([offset.left, offset.top]); 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; if (!this.moveable || !this.target || !this.dragEl) return;
const { beforeTranslate } = drag; const { beforeTranslate } = drag;
frame.translate = beforeTranslate;
this.dragStatus = ActionStatus.ING; this.dragStatus = ActionStatus.ING;
this.moveableHelper?.onResize(e);
this.target.style.width = `${width}px`; this.target.style.width = `${width}px`;
this.target.style.height = `${height}px`; this.target.style.height = `${height}px`;
this.dragEl.style.width = `${width}px`; this.target.style.left = `${frame.left + beforeTranslate[0]}px`;
this.dragEl.style.height = `${height}px`; this.target.style.top = `${frame.top + beforeTranslate[1]}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`;
}) })
.on('resizeEnd', () => { .on('resizeEnd', () => {
this.dragStatus = ActionStatus.END; this.dragStatus = ActionStatus.END;
@ -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 { private sort(): void {
if (!this.target || !this.ghostEl) throw new Error('未知错误'); if (!this.target || !this.ghostEl) throw new Error('未知错误');
const { top } = this.ghostEl.getBoundingClientRect(); const { top } = this.ghostEl.getBoundingClientRect();
@ -331,14 +377,15 @@ export default class StageDragResize extends EventEmitter {
} }
private update(isResize = false): void { private update(isResize = false): void {
const rect = this.moveable!.getRect(); if (!this.target) return;
const offset = 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 left = this.calcValueByFontsize(offset.left);
const top = this.calcValueByFontsize(offset.top); const top = this.calcValueByFontsize(offset.top);
const width = this.calcValueByFontsize(rect.width); const width = this.calcValueByFontsize(this.target.clientWidth);
const height = this.calcValueByFontsize(rect.height); const height = this.calcValueByFontsize(this.target.clientHeight);
this.emit('update', { this.emit('update', {
el: this.target, el: this.target,
@ -369,15 +416,16 @@ export default class StageDragResize extends EventEmitter {
} }
private updateDragEl(el: HTMLElement) { private updateDragEl(el: HTMLElement) {
const { width, height } = el.getBoundingClientRect();
const offset = getOffset(el); const offset = getOffset(el);
const { transform } = getComputedStyle(el);
this.dragEl.style.cssText = ` this.dragEl.style.cssText = `
position: absolute; position: absolute;
transform: ${transform};
left: ${offset.left}px; left: ${offset.left}px;
top: ${offset.top}px; top: ${offset.top}px;
width: ${width}px; width: ${el.clientWidth}px;
height: ${height}px; height: ${el.clientHeight}px;
z-index: ${ZIndex.DRAG_EL}; z-index: ${ZIndex.DRAG_EL};
`; `;
@ -415,6 +463,8 @@ export default class StageDragResize extends EventEmitter {
dragArea: false, dragArea: false,
draggable: true, draggable: true,
resizable: true, resizable: true,
scalable: false,
rotatable: false,
snappable: isAbsolute || isFixed, snappable: isAbsolute || isFixed,
snapGap: isAbsolute || isFixed, snapGap: isAbsolute || isFixed,
snapThreshold: 5, snapThreshold: 5,

View File

@ -46,14 +46,15 @@ export default class TargetCalibrate extends EventEmitter {
} }
public update(el: HTMLElement, prefix: String): HTMLElement { public update(el: HTMLElement, prefix: String): HTMLElement {
const { width, height } = el.getBoundingClientRect();
const { left, top } = this.getOffset(el); const { left, top } = this.getOffset(el);
const { transform } = getComputedStyle(el);
this.operationEl.style.cssText = ` this.operationEl.style.cssText = `
position: absolute; position: absolute;
transform: ${transform};
left: ${left}px; left: ${left}px;
top: ${top}px; top: ${top}px;
width: ${width}px; width: ${el.clientWidth}px;
height: ${height}px; height: ${el.clientHeight}px;
`; `;
this.operationEl.id = `${prefix}${el.id}`; this.operationEl.id = `${prefix}${el.id}`;
@ -65,35 +66,10 @@ export default class TargetCalibrate extends EventEmitter {
} }
private getOffset(el: HTMLElement): Offset { private getOffset(el: HTMLElement): Offset {
const { transform } = getComputedStyle(el);
const { offsetParent } = el; const { offsetParent } = el;
let left = el.offsetLeft; const left = el.offsetLeft;
let top = el.offsetTop; const 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;
}
if (offsetParent) { if (offsetParent) {
const parentOffset = this.getOffset(offsetParent as HTMLElement); const parentOffset = this.getOffset(offsetParent as HTMLElement);

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { MoveableOptions } from 'react-moveable/declaration/types'; import { MoveableOptions } from 'moveable';
import Core from '@tmagic/core'; import Core from '@tmagic/core';
import { Id, MApp, MNode } from '@tmagic/schema'; import { Id, MApp, MNode } from '@tmagic/schema';
@ -58,6 +58,7 @@ export type Rect = {
width: number; width: number;
height: number; height: number;
} & Offset; } & Offset;
export interface Offset { export interface Offset {
left: number; left: number;
top: number; top: number;
@ -72,10 +73,14 @@ export interface UpdateEventData {
el: HTMLElement; el: HTMLElement;
ghostEl: HTMLElement; ghostEl: HTMLElement;
style: { style: {
width: number; width?: number;
height: number; height?: number;
left?: number; left?: number;
top?: number; top?: number;
transform?: {
rotate?: string;
scale?: string;
};
}; };
} }

View File

@ -30,35 +30,10 @@ const getParents = (el: Element, relative: Element) => {
}; };
export const getOffset = (el: HTMLElement): Offset => { export const getOffset = (el: HTMLElement): Offset => {
const { transform } = getComputedStyle(el);
const { offsetParent } = el; const { offsetParent } = el;
let left = el.offsetLeft; const left = el.offsetLeft;
let top = el.offsetTop; const 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;
}
if (offsetParent) { if (offsetParent) {
const parentOffset = getOffset(offsetParent as HTMLElement); const parentOffset = getOffset(offsetParent as HTMLElement);