JavaScript implementiert Drag-and-Drop-Anti-Shake basierend auf MutationObserver


Vorwort

Letztes Mal mit dem 2D-Anmerkungssystem verbunden, muss eine Beschriftung automatisch generiert werden, nachdem die Anmerkung hinzugefügt wurde, und da das Zeichenbrett der Anmerkung skaliert werden kann, muss die Beschriftungsposition aktualisiert werden. Aber dies beinhaltet DOM-Operationen, ich nicht will verrückt sein beim
Ziehen Holen Sie sich das DOM.


1. Überwachung der Leinwandverformung

Ich habe über die Verwendung von resizeEreignissen nachgedacht, aber es ist ein DOM-Element, also musste ich auf keinen Fall einen Weg finden, auf DOM-Änderungen zu hören. Die API
gefunden MutationObserver(Änderungsbeobachter?):

MDN:
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。
它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。

Beispiel laut MDN:

 // 选择需要观察变动的节点
const targetNode = document.getElementById('some-id');

// 观察器的配置(需要观察什么变动)
const config = {
    
     attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
    
    

};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

Ich muss nicht auf die beiden im Callback mutationsList& verfügbaren Werte observerhören (Beobachter ist der Beobachter selbst), und ich muss das Beobachten im letzten Schritt nicht beenden, ich muss das Beobachten fortsetzen, bis sich der Benutzer abmeldet vom System.

Ich habe eine funktionierende Demo geschrieben, lass es mich zuerst testen, divdie style- Eigenschaft ist so eingestellt, dass die Breite und Höhe angepasst werden kann, indem die untere rechte Ecke gezogen wird (der Wert bezieht sich auf die Breite und Höhe):resizedivresizebothboth

<div id="image-wrapper"></div>
class ImageEditor {
    
    
  ui = document.querySelector('#image-wrapper');

  init_observer() {
    
    
    const observer = new MutationObserver(() => {
    
     this.debounce() }); // 属性改变调用debounce
    observer.observe(this.ui, {
    
     attributes: true }); // 只需要监听自身属性改变(比如尺寸)
  }

  debounce() {
    
     // 防抖
    let timer = null;
    const that = this;
    return function () {
    
     
    // 实际上这个函数依旧会在每次debounce调用后被压入调用栈, 只是执行是在停止拖拽1s后执行, 输出几百个'Drag End'
      if (timer !== null) {
    
    
        clearTimeout(timer);
      }
      timer = setTimeout(that.annotate_pic_update_label.bind(that), 1000);
      // annotate_pic_update_label作为setTimeout的回调, 内部this指向window
     // bind创建新函数, 其参数将作为新函数的this来解决这个问题
    }()
  }

  annotate_pic_update_label() {
    
    
    console.log('Drag End');
  }
}

const imageEditor = new ImageEditor();
imageEditor.init_observer();
#image-wrapper {
    
    
  resize: both;
  width: 100px;
  height: 100px;
  overflow: hidden; /* resize必须在overflow:hidden下才能生效. */
  background-color: skyblue;
}

Die Callback-Funktion, die für jede Änderung aufgerufen wird, wird weiterhin in den Call-Stack geschoben. Obwohl sie während des Ziehens nicht funktioniert, macht sie verrückte Aufrufe, nachdem das Ziehen beendet ist. Das ist nicht sehr gut. Es kann als Fehler bezeichnet werden. Es soll nur das
letzte Ergebnis ausgeführt werden.


2. Reduzieren Sie die Anzahl der Anrufe

Der Grund ist, dass eine selbstausführende Funktion verwendet wird, die jedes Mal ausgeführt werden sollte, aber weil das Timing nicht ausreicht, wird sie einfach in den Aufrufstapel geschoben, aber wenn die selbstausführende Funktion nicht angegeben ist, wird sie
es debouncenicht functiontun erneut ausführen, also wird es nicht funktionieren ...

Es ist fataler oder es ist ein falscher Ort, es wird jedes Mal zurückgesetzt , timerwenn es aufgerufen wird , und es schiebt jedes Mal etwas in den Aufrufstapel.debouncenull

const ui = document.querySelector('#image-wrapper');

function init() {
    
    
  const outputRes = debounce(domInfo, 1000); // 实际上是给debounce返回的函数传参, ...args = ui
  // 上面这是很关键的一步, 这使得debounce只执行一次
  const observer = new MutationObserver(() => {
    
    
    outputRes(ui)
  });
  observer.observe(ui, {
    
     attributes: true }); // 只需要监听自身属性改变(比如尺寸)
}

function debounce(fn, delay = 1000) {
    
    
  let timer = null;
  console.log('debouce') // 只输出一次
  return function (...args) {
    
     // args is ui-dom
    // console.log(timer); // 定时器编号, 仅首次null
    if (timer !== null) {
    
    
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
    
    
      fn.apply(this, args);
    }, delay);
  }
}

function domInfo(ele) {
    
    
  const tem = ele.getBoundingClientRect();
  console.log(tem);
  return tem;
}

init();

debounceWird nur einmal ausgeführt, was bedeutet, dass timeres sich nach jeder Änderung nicht ändert nullund dann etwas direkt in die Aufrufliste schiebt, ohne die Aufrufliste zu löschen.

Jede Änderung ruft nur debouncedie zurückgegebene Funktion auf.

Bei jeder Änderung debouncelöscht die zurückgegebene Funktion den letzten Timer und dessen Callback-Funktion, die wegen weniger als 1s nicht ausgeführt wurde (ebenfalls aus dem Call-Stack gelöscht), also obwohl die debouncezurückgegebene Funktion viele Male ausgeführt wurde, jedoch solange Löschen und Hinzufügen (den vorherigen löschen und einen neuen hinzufügen), gibt es immer nur eine Funktion im Aufrufstapel. Nach der letzten Änderung bleibt nur ein neuester Timer übrig und wird nicht gelöscht. Am Ende ist es nur ausgeführt und abschließend
hinzugefügt Die Funktion von wird nach 1s ausgeführt.


Zusammenfassen

おすすめ

転載: blog.csdn.net/qq_52697994/article/details/130150629