fix(highlight): 修复固定定位和弹窗场景下鼠标高亮偏移的问题

This commit is contained in:
parisma 2022-04-28 17:50:09 +08:00 committed by jia000
parent a1ae3dd88d
commit 15b202a9be
5 changed files with 172 additions and 4 deletions

View File

@ -46,11 +46,11 @@ export default class StageDragResize extends EventEmitter {
public horizontalGuidelines: number[] = [];
public verticalGuidelines: number[] = [];
public elementGuidelines: HTMLElement[] = [];
public mode: Mode = Mode.ABSOLUTE;
private moveableOptions: MoveableOptions = {};
private dragStatus: ActionStatus = ActionStatus.END;
private ghostEl: HTMLElement | undefined;
private mode: Mode = Mode.ABSOLUTE;
private moveableHelper?: MoveableHelper;
constructor(config: StageDragResizeConfig) {
@ -354,6 +354,7 @@ export default class StageDragResize extends EventEmitter {
top: ${offset.top}px;
width: ${width}px;
height: ${height}px;
z-index: 9;
`;
this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;

View File

@ -21,19 +21,27 @@ import { EventEmitter } from 'events';
import Moveable from 'moveable';
import { HIGHLIGHT_EL_ID_PREFIX } from './const';
import StageCore from './StageCore';
import TargetCalibrate from './TargetCalibrate';
import type { StageHighlightConfig } from './types';
export default class StageHighlight extends EventEmitter {
public core: StageCore;
public container: HTMLElement;
public target?: HTMLElement;
public moveable?: Moveable;
public calibrationTarget: TargetCalibrate;
constructor(config: StageHighlightConfig) {
super();
this.core = config.core;
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;
this.target = el;
this.moveable?.destroy();
this.moveable = new Moveable(this.container, {
target: this.target,
scrollable: true,
target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
origin: false,
rootContainer: this.core.container,
zoom: 1,
});
}
@ -66,5 +76,6 @@ export default class StageHighlight extends EventEmitter {
*/
public destroy(): void {
this.moveable?.destroy();
this.calibrationTarget.destroy();
}
}

View 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,
};
}
}

View File

@ -21,6 +21,8 @@ export const GHOST_EL_ID_PREFIX = 'ghost_el_';
export const DRAG_EL_ID_PREFIX = 'drag_el_';
export const HIGHLIGHT_EL_ID_PREFIX = 'highlight_el_';
// 默认放到缩小倍数
export const DEFAULT_ZOOM = 1;

View File

@ -22,6 +22,8 @@ import { Id, MApp, MNode } from '@tmagic/schema';
import { GuidesType } from './const';
import StageCore from './StageCore';
import StageDragResize from './StageDragResize';
import StageMask from './StageMask';
export type CanSelect = (el: HTMLElement, event: MouseEvent, stop: () => boolean) => boolean | Promise<boolean>;
@ -54,7 +56,6 @@ export type Rect = {
width: number;
height: number;
} & Offset;
export interface Offset {
left: number;
top: number;
@ -119,3 +120,9 @@ export interface StageHighlightConfig {
core: StageCore;
container: HTMLElement;
}
export interface TargetCalibrateConfig {
parent: HTMLElement;
mask: StageMask;
dr: StageDragResize;
}