增加商品客服显示

This commit is contained in:
wenfp
2026-04-21 23:55:16 +08:00
parent 74572077e4
commit 1a5279a8f5
2 changed files with 432 additions and 0 deletions
+284
View File
@@ -0,0 +1,284 @@
// utils/ObserverInteraction.js
class ObserverInteraction {
constructor(options = {}) {
this.options = {
attributes: true, // 观察属性变化
childList: false, // 观察子节点变化
subtree: false, // 观察后代节点
attributeFilter: [], // 观察特定属性
attributeOldValue: false, // 记录属性旧值
...options,
};
this.observer = null;
this.callbacks = new Map();
this.targets = new Set();
}
/**
* 开始观察目标元素
* @param {HTMLElement} target - 要观察的DOM元素
* @param {Function} callback - 变化时的回调函数
* @param {Object} customOptions - 自定义观察选项
*/
observe(target, callback, customOptions = {}) {
if (!target) {
console.error("ObserverInteraction: 目标元素不存在");
return;
}
const options = { ...this.options, ...customOptions };
// 如果已存在观察器,先断开
if (this.observer) {
this.disconnect();
}
// 创建新的观察器
this.observer = new MutationObserver((mutationsList) => {
const changes = this._processMutations(mutationsList);
if (changes.length > 0 && callback) {
callback(changes, target);
}
// 触发所有注册的回调
this.callbacks.forEach((cb, element) => {
if (element === target || element.contains(target)) {
cb(changes, target);
}
});
});
// 开始观察
this.observer.observe(target, options);
this.targets.add(target);
// 存储回调
if (callback) {
this.callbacks.set(target, callback);
}
return this;
}
/**
* 监听特定属性的变化
* @param {HTMLElement} target - 目标元素
* @param {string} attributeName - 属性名
* @param {Function} callback - 回调函数
*/
watchAttribute(target, attributeName, callback) {
return this.observe(target, callback, {
attributes: true,
attributeFilter: [attributeName],
attributeOldValue: true,
});
}
/**
* 监听元素大小变化(使用 ResizeObserver)
* @param {HTMLElement} target - 目标元素
* @param {Function} callback - 回调函数
*/
observeResize(target, callback) {
if (!window.ResizeObserver) {
console.warn("ResizeObserver 不支持当前浏览器");
return;
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
callback({ width, height, target: entry.target });
}
});
resizeObserver.observe(target);
// 存储以便清理
this.resizeObservers = this.resizeObservers || new Map();
this.resizeObservers.set(target, resizeObserver);
return () => this.unobserveResize(target);
}
/**
* 处理 mutations 数据
* @private
*/
_processMutations(mutationsList) {
const changes = [];
for (const mutation of mutationsList) {
const change = {
type: mutation.type,
target: mutation.target,
timestamp: Date.now(),
};
if (mutation.type === "attributes") {
change.attributeName = mutation.attributeName;
change.oldValue = mutation.oldValue;
change.newValue = mutation.target.getAttribute(mutation.attributeName);
}
if (mutation.type === "childList") {
change.addedNodes = Array.from(mutation.addedNodes);
change.removedNodes = Array.from(mutation.removedNodes);
}
changes.push(change);
}
return changes;
}
/**
* 注册交互事件
* @param {HTMLElement} element - 元素
* @param {string} eventType - 事件类型
* @param {Function} callback - 回调
*/
on(element, eventType, callback) {
if (!element || !callback) return;
const handler = (event) => {
callback(event, element);
};
element.addEventListener(eventType, handler);
// 存储以便清理
this.eventHandlers = this.eventHandlers || new Map();
if (!this.eventHandlers.has(element)) {
this.eventHandlers.set(element, new Map());
}
this.eventHandlers.get(element).set(eventType, handler);
return () => this.off(element, eventType);
}
/**
* 移除事件监听
* @param {HTMLElement} element - 元素
* @param {string} eventType - 事件类型
*/
off(element, eventType) {
if (!this.eventHandlers?.has(element)) return;
const handlers = this.eventHandlers.get(element);
if (eventType) {
const handler = handlers.get(eventType);
if (handler) {
element.removeEventListener(eventType, handler);
handlers.delete(eventType);
}
} else {
handlers.forEach((handler, type) => {
element.removeEventListener(type, handler);
});
this.eventHandlers.delete(element);
}
}
/**
* 观察元素可见性(Intersection Observer)
* @param {HTMLElement} target - 目标元素
* @param {Function} callback - 可见性变化回调
* @param {Object} options - IntersectionObserver 配置
*/
observeVisibility(target, callback, options = {}) {
const defaultOptions = {
threshold: 0.1,
rootMargin: "0px",
};
const intersectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
callback({
isIntersecting: entry.isIntersecting,
intersectionRatio: entry.intersectionRatio,
boundingClientRect: entry.boundingClientRect,
target: entry.target,
});
});
},
{ ...defaultOptions, ...options }
);
intersectionObserver.observe(target);
this.intersectionObservers = this.intersectionObservers || new Map();
this.intersectionObservers.set(target, intersectionObserver);
return () => this.unobserveVisibility(target);
}
/**
* 停止观察可见性
* @param {HTMLElement} target - 目标元素
*/
unobserveVisibility(target) {
const observer = this.intersectionObservers?.get(target);
if (observer) {
observer.unobserve(target);
this.intersectionObservers.delete(target);
}
}
/**
* 停止观察大小变化
* @param {HTMLElement} target - 目标元素
*/
unobserveResize(target) {
const observer = this.resizeObservers?.get(target);
if (observer) {
observer.unobserve(target);
this.resizeObservers.delete(target);
}
}
/**
* 断开所有观察器
*/
disconnect() {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
// 清理 ResizeObservers
this.resizeObservers?.forEach((observer) => {
observer.disconnect();
});
this.resizeObservers?.clear();
// 清理 IntersectionObservers
this.intersectionObservers?.forEach((observer) => {
observer.disconnect();
});
this.intersectionObservers?.clear();
this.targets.clear();
this.callbacks.clear();
}
/**
* 销毁实例
*/
destroy() {
this.disconnect();
// 清理所有事件监听
this.eventHandlers?.forEach((handlers, element) => {
handlers.forEach((handler, type) => {
element.removeEventListener(type, handler);
});
});
this.eventHandlers?.clear();
}
}
export default ObserverInteraction;