mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-05-11 13:29:00 +08:00
fix(highlight): 修复固定定位和弹窗场景下鼠标高亮偏移的问题
This commit is contained in:
parent
a1ae3dd88d
commit
15b202a9be
@ -46,11 +46,11 @@ export default class StageDragResize extends EventEmitter {
|
|||||||
public horizontalGuidelines: number[] = [];
|
public horizontalGuidelines: number[] = [];
|
||||||
public verticalGuidelines: number[] = [];
|
public verticalGuidelines: number[] = [];
|
||||||
public elementGuidelines: HTMLElement[] = [];
|
public elementGuidelines: HTMLElement[] = [];
|
||||||
|
public mode: Mode = Mode.ABSOLUTE;
|
||||||
|
|
||||||
private moveableOptions: MoveableOptions = {};
|
private moveableOptions: MoveableOptions = {};
|
||||||
private dragStatus: ActionStatus = ActionStatus.END;
|
private dragStatus: ActionStatus = ActionStatus.END;
|
||||||
private ghostEl: HTMLElement | undefined;
|
private ghostEl: HTMLElement | undefined;
|
||||||
private mode: Mode = Mode.ABSOLUTE;
|
|
||||||
private moveableHelper?: MoveableHelper;
|
private moveableHelper?: MoveableHelper;
|
||||||
|
|
||||||
constructor(config: StageDragResizeConfig) {
|
constructor(config: StageDragResizeConfig) {
|
||||||
@ -354,6 +354,7 @@ export default class StageDragResize extends EventEmitter {
|
|||||||
top: ${offset.top}px;
|
top: ${offset.top}px;
|
||||||
width: ${width}px;
|
width: ${width}px;
|
||||||
height: ${height}px;
|
height: ${height}px;
|
||||||
|
z-index: 9;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
|
||||||
|
@ -21,19 +21,27 @@ import { EventEmitter } from 'events';
|
|||||||
|
|
||||||
import Moveable from 'moveable';
|
import Moveable from 'moveable';
|
||||||
|
|
||||||
|
import { HIGHLIGHT_EL_ID_PREFIX } from './const';
|
||||||
import StageCore from './StageCore';
|
import StageCore from './StageCore';
|
||||||
|
import TargetCalibrate from './TargetCalibrate';
|
||||||
import type { StageHighlightConfig } from './types';
|
import type { StageHighlightConfig } from './types';
|
||||||
export default class StageHighlight extends EventEmitter {
|
export default class StageHighlight extends EventEmitter {
|
||||||
public core: StageCore;
|
public core: StageCore;
|
||||||
public container: HTMLElement;
|
public container: HTMLElement;
|
||||||
public target?: HTMLElement;
|
public target?: HTMLElement;
|
||||||
public moveable?: Moveable;
|
public moveable?: Moveable;
|
||||||
|
public calibrationTarget: TargetCalibrate;
|
||||||
|
|
||||||
constructor(config: StageHighlightConfig) {
|
constructor(config: StageHighlightConfig) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.core = config.core;
|
this.core = config.core;
|
||||||
this.container = config.container;
|
this.container = config.container;
|
||||||
|
this.calibrationTarget = new TargetCalibrate({
|
||||||
|
parent: this.core.mask.content,
|
||||||
|
mask: this.core.mask,
|
||||||
|
dr: this.core.dr,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,10 +52,12 @@ export default class StageHighlight extends EventEmitter {
|
|||||||
if (!el || el === this.target) return;
|
if (!el || el === this.target) return;
|
||||||
this.target = el;
|
this.target = el;
|
||||||
this.moveable?.destroy();
|
this.moveable?.destroy();
|
||||||
|
|
||||||
this.moveable = new Moveable(this.container, {
|
this.moveable = new Moveable(this.container, {
|
||||||
target: this.target,
|
target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
|
||||||
scrollable: true,
|
|
||||||
origin: false,
|
origin: false,
|
||||||
|
rootContainer: this.core.container,
|
||||||
|
zoom: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,5 +76,6 @@ export default class StageHighlight extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
this.moveable?.destroy();
|
this.moveable?.destroy();
|
||||||
|
this.calibrationTarget.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
147
packages/stage/src/TargetCalibrate.ts
Normal file
147
packages/stage/src/TargetCalibrate.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
import { Mode } from './const';
|
||||||
|
import StageDragResize from './StageDragResize';
|
||||||
|
import StageMask from './StageMask';
|
||||||
|
import type { Offset, Rect, TargetCalibrateConfig } from './types';
|
||||||
|
import { getMode } from './util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将选中的节点修正定位后,添加一个操作节点到蒙层上
|
||||||
|
*/
|
||||||
|
export default class TargetCalibrate extends EventEmitter {
|
||||||
|
public parent: HTMLElement;
|
||||||
|
public mask: StageMask;
|
||||||
|
public dr: StageDragResize;
|
||||||
|
public operationEl: HTMLElement;
|
||||||
|
|
||||||
|
constructor(config: TargetCalibrateConfig) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.parent = config.parent;
|
||||||
|
this.mask = config.mask;
|
||||||
|
this.dr = config.dr;
|
||||||
|
|
||||||
|
this.operationEl = globalThis.document.createElement('div');
|
||||||
|
this.parent.append(this.operationEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(el: HTMLElement, prefix: String): HTMLElement {
|
||||||
|
const { width, height } = el.getBoundingClientRect();
|
||||||
|
const { left, top } = this.getOffset(el);
|
||||||
|
this.operationEl.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
left: ${left}px;
|
||||||
|
top: ${top}px;
|
||||||
|
width: ${width}px;
|
||||||
|
height: ${height}px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.operationEl.id = `${prefix}${el.id}`;
|
||||||
|
return this.operationEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置样式属性
|
||||||
|
* @param rect 样式属性
|
||||||
|
*/
|
||||||
|
public resetRect(rect: Rect): void {
|
||||||
|
this.operationEl.style.width = `${rect.width}px`;
|
||||||
|
this.operationEl.style.height = `${rect.height}px`;
|
||||||
|
Object.keys(rect).forEach((key: string) => {
|
||||||
|
this.operationEl.style[key] = `${rect[key]}px`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
this.operationEl?.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetParent) {
|
||||||
|
const parentOffset = this.getOffset(offsetParent as HTMLElement);
|
||||||
|
return {
|
||||||
|
left: left + parentOffset.left,
|
||||||
|
top: top + parentOffset.top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中固定定位元素后editor-mask高度被置为视窗大小
|
||||||
|
if (this.dr.mode === Mode.FIXED) {
|
||||||
|
// 弹窗的情况
|
||||||
|
if (getMode(el) === Mode.FIXED) {
|
||||||
|
return {
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: left - this.mask.scrollLeft,
|
||||||
|
top: top - this.mask.scrollTop,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无父元素的固定定位需按滚动值计算
|
||||||
|
if (getMode(el) === Mode.FIXED) {
|
||||||
|
return {
|
||||||
|
left: left + this.mask.scrollLeft,
|
||||||
|
top: top + this.mask.scrollTop,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@ export const GHOST_EL_ID_PREFIX = 'ghost_el_';
|
|||||||
|
|
||||||
export const DRAG_EL_ID_PREFIX = 'drag_el_';
|
export const DRAG_EL_ID_PREFIX = 'drag_el_';
|
||||||
|
|
||||||
|
export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
|
||||||
|
|
||||||
// 默认放到缩小倍数
|
// 默认放到缩小倍数
|
||||||
export const DEFAULT_ZOOM = 1;
|
export const DEFAULT_ZOOM = 1;
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ import { Id, MApp, MNode } from '@tmagic/schema';
|
|||||||
|
|
||||||
import { GuidesType } from './const';
|
import { GuidesType } from './const';
|
||||||
import StageCore from './StageCore';
|
import StageCore from './StageCore';
|
||||||
|
import StageDragResize from './StageDragResize';
|
||||||
|
import StageMask from './StageMask';
|
||||||
|
|
||||||
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
|
||||||
|
|
||||||
@ -54,7 +56,6 @@ 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;
|
||||||
@ -119,3 +120,9 @@ export interface StageHighlightConfig {
|
|||||||
core: StageCore;
|
core: StageCore;
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TargetCalibrateConfig {
|
||||||
|
parent: HTMLElement;
|
||||||
|
mask: StageMask;
|
||||||
|
dr: StageDragResize;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user