File size: 6,041 Bytes
494c9e4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | /**
* Resize处理器
* 负责监听容器大小变化并智能更新SVG位置
*/
// todo: 接口设计评审改进
export interface ResizeHandlerOptions {
/** 快速变化阈值(毫秒) */
rapidResizeThresholdMs?: number;
/** 快速变化计数阈值 */
rapidResizeCountThreshold?: number;
/** 防抖时间(毫秒) */
resizeDebounceMs?: number;
/** 位置更新回调 */
onPositionUpdate: () => void;
/** 获取当前SVG元素 */
getCurrentSvg: () => SVGSVGElement | undefined;
/** 过渡开始回调(快速resize时调用) */
onTransitionStart?: () => void;
}
export class ResizeHandler {
private resizeObserver?: ResizeObserver;
private baseNode: HTMLElement;
private options: Required<Omit<ResizeHandlerOptions, 'onPositionUpdate' | 'getCurrentSvg'>> & Pick<ResizeHandlerOptions, 'onPositionUpdate' | 'getCurrentSvg'>;
// 智能检测相关状态
private lastResizeTime = 0;
private resizeEventCount = 0;
private resizeEndTimer?: number;
private positionUpdateTimer?: number;
constructor(baseNode: HTMLElement, options: ResizeHandlerOptions) {
this.baseNode = baseNode;
this.options = {
rapidResizeThresholdMs: options.rapidResizeThresholdMs ?? 100,
rapidResizeCountThreshold: options.rapidResizeCountThreshold ?? 3,
resizeDebounceMs: options.resizeDebounceMs ?? 100,
onPositionUpdate: options.onPositionUpdate,
getCurrentSvg: options.getCurrentSvg,
onTransitionStart: options.onTransitionStart,
};
}
/**
* 设置ResizeObserver,监听容器大小变化并更新SVG rect位置
*/
setup(): void {
// 如果已经设置了,就不重复设置
if (this.resizeObserver) {
return;
}
// 创建ResizeObserver,使用智能检测
this.resizeObserver = new ResizeObserver((entries) => {
const now = Date.now();
const timeSinceLastResize = now - this.lastResizeTime;
// 检测是否是快速连续变化(过渡中)
const isRapidChange = timeSinceLastResize < this.options.rapidResizeThresholdMs;
if (isRapidChange) {
// 快速连续变化,增加计数
this.resizeEventCount++;
} else {
// 不是快速连续变化,重置计数
this.resizeEventCount = 1;
}
this.lastResizeTime = now;
// 判断是否是"过渡中"(快速连续变化)
const isInTransition = this.resizeEventCount >= this.options.rapidResizeCountThreshold;
if (isInTransition) {
this.handleRapidResize();
} else {
this.handleSingleResize();
}
});
// 开始观察容器
this.resizeObserver.observe(this.baseNode);
}
/**
* 处理快速连续变化(过渡中)
*/
private handleRapidResize(): void {
const svg = this.options.getCurrentSvg();
// 过渡中:隐藏SVG,等待稳定后更新
if (svg && svg.style.opacity !== '0') {
svg.style.opacity = '0';
svg.style.pointerEvents = 'none';
}
// 调用过渡开始回调(隐藏minimap等)
if (this.options.onTransitionStart) {
this.options.onTransitionStart();
}
// 取消待处理的位置更新
if (this.positionUpdateTimer !== undefined) {
cancelAnimationFrame(this.positionUpdateTimer);
this.positionUpdateTimer = undefined;
}
// 取消之前的结束检测
if (this.resizeEndTimer !== undefined) {
clearTimeout(this.resizeEndTimer);
}
// 设置结束检测:RESIZE_DEBOUNCE_MS 没有新事件则认为结束
this.resizeEndTimer = window.setTimeout(() => {
this.resizeEventCount = 0; // 重置计数
// 立即更新位置
this.options.onPositionUpdate();
// 显示SVG
const svg = this.options.getCurrentSvg();
if (svg) {
svg.style.opacity = '1';
svg.style.pointerEvents = '';
}
this.resizeEndTimer = undefined;
}, this.options.resizeDebounceMs);
}
/**
* 处理单次变化(如字体改变)
*/
private handleSingleResize(): void {
// 单次变化:直接更新,不隐藏
// 取消之前的结束检测(如果有)
if (this.resizeEndTimer !== undefined) {
clearTimeout(this.resizeEndTimer);
this.resizeEndTimer = undefined;
}
// 取消待处理的位置更新
if (this.positionUpdateTimer !== undefined) {
cancelAnimationFrame(this.positionUpdateTimer);
}
// 立即更新位置(使用 requestAnimationFrame 确保不阻塞)
this.positionUpdateTimer = requestAnimationFrame(() => {
this.options.onPositionUpdate();
this.positionUpdateTimer = undefined;
});
}
/**
* 清理资源:停止ResizeObserver并清理定时器
*/
destroy(): void {
// 清理ResizeObserver
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = undefined;
}
// 取消待处理的位置更新
if (this.positionUpdateTimer !== undefined) {
cancelAnimationFrame(this.positionUpdateTimer);
this.positionUpdateTimer = undefined;
}
// 取消resize结束检测定时器
if (this.resizeEndTimer !== undefined) {
clearTimeout(this.resizeEndTimer);
this.resizeEndTimer = undefined;
}
}
}
|