Перетаскивание между фреймами для платформы визуального построения

JavaScript

предисловие

Некоторое время назад я построил платформу для операционной деятельности.Одна из основных функций заключается в том, что страница редактирования разделена на левую область компонента и правую область предварительного просмотра.Необходимо понимать, что содержимое области компонента может быть свободно размещены в области предварительного просмотра.

Подобно изображению ниже:

В сообществе есть несколько похожих реализаций функций, но способ использования схож, и они неотделимы оттянутьспособность. Мы часто используем перетаскивание в нашей повседневной разработке, например сортировку перетаскиванием, загрузку перетаскиванием и т. д. Конечно, есть также много пакетов npm, которые можно перетаскивать, и наиболее полезными из них являются:react-dnd, vueВстроенные возможности перетаскивания.

Однако в нашей области предварительного просмотра используется метод iframe, а библиотеки классов, которые легко использовать в сообществе, обычно не поддерживают возможность перетаскивания между iframe. Здесь мы решили использоватьСобственный API перетаскивания с помощью перетаскивания

Необходимо реализовать две основные функции:

1. Обнаружение перетаскивания внутри и снаружи iframe.

2. Отображение внутренних компонентов iframe на основе данных.

Мы просто генерируем функцию страницы:

//搭建编辑页
//drag.jsx
import React, { useState, useEffect } from 'react';
import Drag from './drag.js';

require('./styles.less');

//iframe hooks
const useIframeLoad = () => {
  const [iframeState, setIframeState] = useState(false);
  const [windowState, setWindowState] = useState( document.readyState === "complete");

  const iframeLoad = () => {
    const iframeEle = document.getElementById("my-iframe");
    iframeEle && setIframeState(iframeEle.contentDocument.readyState === "complete");
    if (!iframeState && iframeEle) {
      iframeEle.onload = () => {
        setIframeState(true);
      };
    }
  };
  useEffect(() => {
    if (!windowState) {
      setIframeState(false);
      window.addEventListener('load', () => {
        setWindowState(true);
        iframeLoad();
      })
    } else {
      iframeLoad();
    }
  }, []);
  return iframeState;
}

export default () => {

  const init = () => {
    Drag.init({
      dragEle: document.getElementById('drag-box'),
      dropEle: document.getElementById('my-iframe').contentDocument.getElementById('drop-box')
    })
  }

  useIframeLoad() && init();

  return <>
   <!-- 组件区 -->
    <div id="drag-box">
      <div className="drag-item">拖动元素</div>
      <div className="drag-item">拖动元素</div>
      <div className="drag-item">拖动元素</div>
    </div>
    <!-- 预览区 -->
    <div className="drop-content">
      <iframe id="my-iframe" src="#/iframe" style={{ width: "100%", height: "480px", border: "none" }}/>
    </div>
  </>
}

Страница iframe области предварительного просмотра:

//iframe.jsx
import React from 'react';

require('./styles.less');

export default () => {
  return <div id="drop-box">
    <div className="item">元素1</div>
    <div className="item">元素2</div>
    <div className="item">元素3</div>
  </div>
}

На этом простой макет сборки-редактирования завершен. Далее, давайте посмотрим на часть перетаскивания:

Перетаскивание через iframe

Во-первых, мы можем посмотреть, что такое нативные события.

родное событие

drag // 拖动元素或文本选择时将触发此事件 (相当于拖动过程中,一直触发此事件)
dragstart //当用户开始拖动一个元素或者一个选择文本的时候 ,将触发此事件
dragend //当拖动操作结束时(通过释放鼠标按钮或按退出键),将触发此事件

dragover //当被拖动元素在释放区内移动时,将触发此事件
dragenter //被拖动元素进入到释放区所占据得屏幕空间时,将触发此事件
dragleave //当被拖动元素没有放下就离开释放区时,将触发此事件
dragexit //当元素不再是拖动操作的立即选择目标时,将触发此事件
drop //当被拖动元素在释放区里放下时,将触发此事件

Встроенное перетаскивание методом перетаскивания

В зависимости от требований основные процессы перетаскивания разделены:

  • инициализировать элементУстановите элемент перетаскивания и целевой узел
  • Проблема с регистрациейРегистрация событий перетаскивания для перетаскиваемого элемента и элемента целевого узла
  • прослушать событиеУзел-заполнитель создается в процессе перетаскивания, а узел-заполнитель удаляется после перетаскивания.

Неполный код выглядит следующим образом:

//drag.js
class Drag {
  params = {}

  init = (params) => {
    ....
  };

  //初始化设置拖动元素
  initDrag = dragEle => {
    if(dragEle.childNodes.length) {
      const { length } = dragEle.childNodes;
      let i = 0
      while (i< length) {
        this.setDrag(dragEle.childNodes[i]);
        i += 1;
      }
    } else {
      this.setDrag(dragEle);
    }
  }

  //初始化释放区
  initDrop = dropEle => {
    if (dropEle.childNodes.length) {
      const { length } = dropEle.childNodes;
      let i = 0;
      while (i < length) {
        this.setDrop(dropEle.childNodes[i]);
        i += 1;
      }
    } else {
      this.setDrop(dropEle);
    }
  }

  //拖动元素注册事件
  setDrag = el => {
    el.setAttribute("draggable", "true");
    el.ondragstart = this.dragStartEvent;
    el.ondrag = this.dragEvent;
    el.ondragend = this.dragEndEvent;
  };

  //释放区注册事件
  setDrop = el => {
    el.ondrop = this.dropEvent;
    el.ondragenter = this.dragEnterEvent;
    el.ondragover = this.dragOverEvent;
    el.ondragleave = this.dragLeaveEvent;
  }
   ......

  //创建占位元素
  createElePlaceholder = (() => {
    let ele = null;
    return () => {
      if (!ele) {
        ele = document.createElement("div");
        ele.setAttribute("id", "drag-ele-placeholder");
        ele.innerHTML = `<div style="width: 100%; height:50px; position: relative">
            <div style="width: 150px; height: 40px; text-align: center; position: absolute;
            left: 0; right: 0; top: 0; bottom:0; margin: auto; background: #878; line-height: 40px">放置组件</div>
          </div>`;
      }
      return ele;
    };
  })();

  //移除占位元素
  removePlaceholderEle = () => {
    const iframe = this.getIframe();
    const removeEle = iframe.contentDocument.getElementById("drag-ele-placeholder");
    const { dropEle } = this.params;
    if(this.isHasPlaceholderEle()) { dropEle.removeChild(removeEle) };
  }

  /****** 事件处理 ******/
  dragEndEvent = ev => {
    this.removePlaceholderEle()
    console.log('拖拽结束');
    console.log('删除占位元素');
  };
  //插入占位元素
  dragEnterEvent = ev => {
    ev.preventDefault();
    const insertEle = this.createElePlaceholder();
    ev.target.before(insertEle);
    console.log('进入到可放置区');
    console.log('插入占位元素');
  };
   //删除占位元素
  dragLeaveEvent = ev => {
    ev.preventDefault();
    this.removePlaceholderEle()
    console.log('离开放置区');
    console.log('删除占位元素');
  };

  dropEvent = ev => {
    ev.preventDefault();
    console.log('在放置区放开鼠标');
  }
}

export default new Drag();

После первоначального завершения эффект следующий:

Здесь есть некоторые проблемы:

  1. При вставке страница мерцает
  2. Событие входа запускается только тогда, когда позиция мыши входит в область освобождения
  3. Не удалось добавить первый элемент

анализ проблемы

  • При перетаскивании в область предварительного просмотра запускается событие перетаскивания узла в области предварительного просмотра. Всякий раз, когда элемент-заполнитель вставляется в текущий узел, положение этого узла изменяется, запускается событие перетаскивания узла, и элемент-заполнитель удаляется. Этот процесс продолжает повторяться, что приводит к постоянному мерцанию.
  • Вышеупомянутые проблемы 2 и 3 связаны с ограничением API на само перетаскивание.

Поскольку текущий метод не может реализовать эту функцию идеально, было решеноУстаревшие события dragover, dragenter, dragleave

Реорганизуйте функциональные точки, которые необходимо оптимизировать:

  1. Когда перетаскиваемый элемент соприкасается с краем iframe, это означает попадание в зону освобождения
  2. Перетаскивание может быть вставлено над элементом и вставлено под элементом

использоватьТочный расчет координат, чтобы обрабатывать вход в область выпуска и вставку выше и ниже элемента

Внесите некоторые изменения в drag.js:

class Drag {
  params = {}

  // 声明
  mouseOffsetBottom = 0;
  mouseOffsetRight = 0;

  init = (params) => {
    ...
  };
  //初始化设置拖动元素
  initDrag = dragEle => {
    ....
  }
  //初始化释放区
  initDrop = dropEle => {
    ...
  }
  //拖动元素注册事件
  setDrag = el => {
    ...
  };
  //释放区注册事件
  setDrop = el => {
    ...
  }

  //获取iframe的位置
  getIframeOffset = () => {
    const iframeEle = this.getIframe();
    return iframeEle
      ? this.getRealOffset(iframeEle)
      : { offsetLeft: 0, offsetTop: 0 };
  };

  //递归计算元素距离父元素的offset
  getRealOffset = (el, parentName) => {
    let left = el.offsetLeft;
    let top = el.offsetTop;
    if (el.offsetParent && el.offsetParent.tagName !== parentName) {
      const p = this.getRealOffset(el.offsetParent, parentName);
      left += p.offsetLeft;
      top += p.offsetTop;
    }
    return { offsetLeft: left, offsetTop: top };
  }

  //获取元素位置
  getElOffset = el => {
    const { offsetTop: iframeTop } = this.getIframeOffset();
    const { offsetTop: targetOffsetTop } = this.getRealOffset(el);
    return {
      midLine: el.clientHeight / 2 + targetOffsetTop + iframeTop,
      topLine: targetOffsetTop + iframeTop,
      bottomLine: el.clientHeight + targetOffsetTop + iframeTop
    };
  };

  //释放区内部元素位置
  getDropOffset = () => {
    const result = [];
    const { dropEle } = this.params;
    const el = dropEle.childNodes;

    let i = 0;
    while (i < el.length) {
      const midLine = this.getElOffset(el[i]);
      result.push(midLine);
      i += 1;
    }
    return result;
  };

  //位置比较
  locationCompare = (ev) => {
    let inside = false;
    const { dropEle } = this.params;
    console.log(ev.clientX);
    // 拖动元素的位置
    const sourceRight = ev.clientX + this.mouseOffsetRight;
    const sourceLeft = sourceRight - ev.currentTarget.clientWidth;

    const { offsetLeft: iframeLeft } = this.getIframeOffset();
    const { offsetLeft: targetLeft } = this.getRealOffset(dropEle);

    /*释放区的位置*/
    const targetOffsetLeft = iframeLeft + targetLeft;
    const targetOffsetRight = targetOffsetLeft + dropEle.clientWidth;

    if (sourceRight > targetOffsetLeft && sourceLeft < targetOffsetRight) {
      //拖动到释放区
      inside = true;
    } else {
      //释放区外面
      inside = false;
    }
    return inside;

  }

  //插入占位元素
  insertPlaceholderEle = (sourceMidLine) => {
    const dropOffset = this.getDropOffset(); //释放区的位置属性
    const insertEl = this.createElePlaceholder();
    const { dropEle } = this.params;
    const dropEleChild = dropEle.childNodes;
    if (dropOffset.length) {
      dropOffset.map((item, i) => {
        const Ele = dropEleChild[i];
        //在元素前面插入占位元素
        if (sourceMidLine > item.topLine && sourceMidLine < item.midLine) {
          Ele.before(insertEl);
        }
        //在元素后面插入占位元素
        if (sourceMidLine < item.bottomLine && sourceMidLine > item.midLine) {
          this.index = i + 1;
          Ele.after(insertEl);
        }
        //追加一个占位元素
        if (sourceMidLine > dropOffset[dropOffset.length - 1].bottomLine) {
          dropEle.append(insertEl);
        }
        return item;
      });
    }
    //插入第一个占位元素(当iframe内部没有组件)
    if (!dropEleChild.length) {
      dropEle.append(insertEl);
    }
  }

  /****** 事件处理 ******/
  dragStartEvent = ev => {
    // console.log('开始拖拽');
    //获得鼠标距离拖拽元素的下边的距离
    this.mouseOffsetBottom = ev.currentTarget.clientHeight - ev.offsetY;
    //获得鼠标距离拖拽元素的右边的距离
    this.mouseOffsetRight = ev.currentTarget.clientWidth - ev.offsetX;
  };

  dragEvent = ev => {
    //获取拖拽元素中线距离屏幕上方的距离
    const sourceMidLine =
      ev.clientY + this.mouseOffsetBottom - ev.currentTarget.clientHeight / 2;
    if(this.locationCompare(ev)) {
      this.insertPlaceholderEle(sourceMidLine)
      console.log('释放区内部')
    } else {
      this.removePlaceholderEle()
      console.log('释放区外面')
    }
  };
}

export default new Drag();

Результат выглядит следующим образом:

На данный момент решена проблема непрерывного мерцания, а также точного расчета координат для реализации верхней и нижней вставки элементов.

Но есть еще некоторые проблемы:

  • На демонстрационном изображении хорошо видно, что когда правая сторона перетаскиваемого элемента входит в iframe, может быть вставлен элемент-заполнитель, но когда позиция мыши входит в iframe, элемент снова удаляется

Что является причиной этого?

Давайте посмотрим на координаты напечатанной мыши, и мы увидим, что когда позиция мыши входит в iframe,ev.clientXМутировал до 0, видно, что когда координаты мыши вводят IFrame, iFrame используется в качестве окна. В результате положение мышиной мыши на 0, что вызывает отклонение в рассчитанном положении, так что перетаскиваемый элемент считается не в области выпуска, поэтому элемент заполнителя удаляется.

Как решить эту проблему?

На ум пришло несколько вариантов:

  1. Один из них — отслеживать мутацию координат, а затем пересчитывать положение для дальнейшего сравнения положения.
  2. Сделайте iframe больше, а экран больше или равен размеру экрана и сделайте его внутри iframe с начала перетаскивания.

анализ случая:

  • Первое решение заключается в том, что критическое условие отслеживания изменения координаты до 0 ненадежно, потому что событие перетаскивания запускается каждые 50 мс.В зависимости от того, насколько быстро вы перемещаете мышь, clientX, полученный каждый раз, когда мышь входит в iframe, несовместим.Первый вариант невозможен.
  • Второе решение, увеличение iframe, теоретически возможно, попробуем. в основномизменить макет.

код показывает, как показано ниже:

.drop-content {
  position: absolute;
  width: 100vw; //iframe放大和窗口一般大
  height: 100%;
}

#drop-box {
  width: 375px; //iframe内部元素设置宽度
  margin: 100px auto;

  .item {
    ...
  }
}

Эффект демонстрации следующийДемо можно посмотреть,Закрывает область компонента слева. Это связано с более высоким z-индексом области просмотра справа.

Оптимизация

Есть два варианта

  • Макет элемента перемещается и меняется местами, так что элемент dom в правой области просмотра помещается перед областью компонента.
  • Измените z-индекс так, чтобы z-индекс правой области обзора был ниже
план 1

основной код

//drag.jsx
//调换两个元素的位置
<>
  <div className="drop-content">
    <iframe id="my-iframe" src="#/iframe" style={{ width: "100%", height: "480px", border: "none" }}/>
  </div>
  <div id="drag-box">
    <div className="drag-item">拖动元素</div>
    <div className="drag-item">拖动元素</div>
    <div className="drag-item">拖动元素</div>
  </div>
</>

После воздействия

Видно, что проблема перетаскивания прекрасно решена. Но схема изменилась.

Сценарий 2

основной код

.drop-content {
  position: absolute;
  z-index: -1; //让iframe的z-index低一点
  width: 100vw; //iframe放大和窗口一般大
  height: 100%;
}

#drop-box {
  width: 375px; //iframe内部元素设置宽度
  margin: 100px auto;

  .item {
    width: 100%;
    height: 50px;
    background-color: #875;
  }
}

После воздействияИз демо видно, что проблема перетаскивания решена отлично, но событие щелчка элемента внутри iframe не срабатывает.

Подумав об этом, поскольку z-index может решить проблему мутации clientX, можно ли это сделать без увеличения iframe? Это не повлияет на срабатывание события, так что давайте попробуем.

основной код

//drag.js
//开始拖拽
dragStartEvent = ev => {
  document.getElementsByClassName("drop-content")[0].style.zIndex =
    "-1";
};

//拖拽结束
dragEndEvent = ev => {
  ev.preventDefault();
  document.getElementsByClassName("drop-content")[0].style.zIndex = "0";
};

Эффект демонстрации следующийОтлично, это также отлично решает проблему перетаскивания без изменения положения дома.

обработка прокрутки

Когда в области просмотра много элементов и на странице появляются полосы прокрутки, не возникнет ли проблемы? Попробуем сделать высоту iframe выше

 <iframe id="my-iframe" src="#/iframe" style={{ width: "100%", height: "880px", border: "none" }}/>

Эффект демонстрации следующий

Из демонстрации видно, что на странице появляется полоса прокрутки, область просмотра прокручивается вверх, и когда верхняя часть iframe прокручивается в верхнюю часть экрана, когда мы перетаскиваем элемент для вставки, он появится, и вставка будет не на месте Это проблема с расчетом?

Если внимательно посмотреть на код, то при прокрутке верхней части iframe к верхней части экрана будет вычислено отрицательное число, что приведет к отклонению расчета, что приведет к неправильной вставке элементов-заполнителей.

//递归计算元素距离父元素的offset
getRealOffset = (el, parentName) => {
  let left = el.offsetLeft;
  let top = el.offsetTop;
  if (el.offsetParent && el.offsetParent.tagName !== parentName) {
    const p = this.getRealOffset(el.offsetParent, parentName);
    left += p.offsetLeft;
    top += p.offsetTop;
  }
  return { offsetLeft: left, offsetTop: top };
}

Оптимизированный план расчета

основной код

//计算元素距离父元素的offset
getRealOffset = (el, parentName) => {
  const { left, top } = el.getBoundingClientRect();
  return { offsetLeft: left, offsetTop: top };
}

Используйте метод getBoundingClientRect, чтобы получить позицию конкретного окна.

Демо выглядит следующим образомЭта оптимизация может прекрасно решить некоторые проблемы перетаскивания, и оба вышеуказанных решения вполне осуществимы.

Общайтесь через iframe

Как сделать, чтобы данные внутри iframe обновлялись и отображались в реальном времени после вставки элемента перетаскивания?

Идея заключается в следующем:

  • Установите метод обновления внутри iframe
  • В обратном вызове после завершения перетаскивания вызовите обновление и передайте данные
  • Запуск рендеринга элементов внутри iframe
  1. поддерживать хранилище данных компонента,getStore и setStoreметод
//store.js
class Store {
  state = {
    list: []
  }
  getStore = () => this.state
  setStore = (data) => {
    this.state = { ...this.state, ...data }
  }
}

export default new Store()
  1. Вставка компонентов соответствует обработке данных, в том числе,добавить и вставитьоперация, иОбновлять iframe синхронноМетоды
// update.js
import Store from './store';

const add = (params) => {
  const { list } = Store.getStore()
  Store.setStore({ list: [...list, params.data]})
};

const insert = (params) => {
  const { list } = Store.getStore()
  const { index } = params;
  list.splice(index, 0, params.data)
  Store.setStore({ list: [...list] })
};

const update = {
  add,
  insert
}

//更新iframe内部数据方法
const iframeUpdate = (params) => {
  document.getElementById("my-iframe") &&
    document.getElementById("my-iframe").contentWindow &&
    document.getElementById("my-iframe").contentWindow.update &&
    document.getElementById("my-iframe").contentWindow.update(params);
}

export default (params) => {
  const { type, ...argv } = params;
  if(!type) return Promise.reject()
  return new Promise(r => r())
    .then(() => update[type](argv))
    .then(() => {
      const { list } = Store.getStore()
      iframeUpdate(list)
    })
}
  1. При перетаскивании после перетаскивания тип операции элемента и позиция вставляемого элемента передаются через функцию обратного вызова
//drag.js
class Drag {
  params = {}

  mouseOffsetBottom = 0;
  mouseOffsetRight = 0;

  index = 0; //插入元素的下标
  type = 'add'; //操作类型

  init = (params) => {
    ...
  };

  ...

  //计算元素距离父元素的offset
  getRealOffset = (el, parentName) => {
    const { left, top } = el.getBoundingClientRect();
    return { offsetLeft: left, offsetTop: top };
  }

  //获取元素位置
  getElOffset = el => {
    const { offsetTop: iframeTop } = this.getIframeOffset();
    const { offsetTop: targetOffsetTop } = this.getRealOffset(el);
    return {
      midLine: el.clientHeight / 2 + targetOffsetTop + iframeTop,
      topLine: targetOffsetTop + iframeTop,
      bottomLine: el.clientHeight + targetOffsetTop + iframeTop
    };
  };

  //释放区内部元素位置
  getDropOffset = () => {
    const result = [];
    const { dropEle } = this.params;
    const el = dropEle.childNodes;

    let i = 0;
    while (i < el.length) {
      const midLine = this.getElOffset(el[i]);
      result.push(midLine);
      i += 1;
    }
    return result;
  };

  ...

  //插入占位元素
  insertPlaceholderEle = (sourceMidLine) => {
    const dropOffset = this.getDropOffset(); //释放区的位置属性
    const insertEl = this.createElePlaceholder();
    const { dropEle } = this.params;
    const dropEleChild = dropEle.childNodes;
    if (dropOffset.length) {
      dropOffset.map((item, i) => {
        const Ele = dropEleChild[i];
        //在元素前面插入占位元素
        if (sourceMidLine > item.topLine && sourceMidLine < item.midLine) {
          Ele.before(insertEl);
          this.index = i;
          this.type = 'insert'
        }
        //在元素后面插入占位元素
        if (sourceMidLine < item.bottomLine && sourceMidLine > item.midLine) {
          this.index = i + 1;
          Ele.after(insertEl);
          this.type = 'insert'
        }
        //追加一个占位元素
        if (sourceMidLine > dropOffset[dropOffset.length - 1].bottomLine) {
          dropEle.append(insertEl);
          this.type = 'add'
        }
        return item;
      });
    }
    //插入第一个占位元素(当iframe内部没有组件)
    if (!dropEleChild.length) {
      this.type = 'add'
      dropEle.append(insertEl);
    }
  }

  /****** 事件处理 ******/
  //开始拖拽
  dragStartEvent = ev => {
    document.getElementsByClassName("drop-content")[0].style.zIndex =
      "-1";
    //获得鼠标距离拖拽元素的下边的距离
    this.mouseOffsetBottom = ev.currentTarget.clientHeight - ev.offsetY;
    //获得鼠标距离拖拽元素的右边的距离
    this.mouseOffsetRight = ev.currentTarget.clientWidth - ev.offsetX;
  };

  dragEvent = ev => {
    //获取拖拽元素中线距离屏幕上方的距离
    const sourceMidLine =
      ev.clientY + this.mouseOffsetBottom - ev.currentTarget.clientHeight / 2;
    if(this.locationCompare(ev)) {
      this.insertPlaceholderEle(sourceMidLine)
      // console.log('释放区内部')
    } else {
      this.removePlaceholderEle()
      // console.log('释放区外面')
    }
  };

  //拖拽结束
  dragEndEvent = ev => {
    ev.preventDefault();
    document.getElementsByClassName("drop-content")[0].style.zIndex = "0";
    const { callback } = this.params;
    this.locationCompare(ev) &&
      callback &&
      callback({
        type: this.type,
        index: this.index
      });
  };
}

export default new Drag();
  1. Вызов обновления после перетаскивания для обновления источника данных
//drag.jsx
import React, { useState, useEffect } from 'react';
import Drag from './drag';
import update from '@/store/update';

require('./styles.less');

//iframe hooks
const useIframeLoad = () => {
  ...
  //iframe加载状态的hooks
  return iframeState;
}

export default () => {

  const callback = params => {
    update({ ...params, data: { name: new Date().getTime() } })
  }

  const init = () => {
    Drag.init({
      dragEle: document.getElementById('drag-box'),
      dropEle: document.getElementById('my-iframe').contentDocument.getElementById('drop-box'),
      callback
    })
  }

  useIframeLoad() && init();
  return <>
    ...
  </>
}
  1. Внутренний метод UPDATE IFRAME вызывается для запуска обновления данных и визуализации компонента.
//iframe.jsx
import React, { useState } from 'react';

require('./styles.less');

export default () => {
  const [list, setList] = useState([]);

  //挂载update方法,跨iframe数据传递,更新
  window.update = params => {
    setList(params);
  }

  return <div id="drop-box">
    {
      list.map((item) =>
        <div className="item" key={item.name} onClick={() => alert('点击事件')}>元素{item.name}</div>
      )
    }
  </div>
}

Эффект демонстрации следующий

Наконец, реализовано перетаскивание и связь между фреймами.

Суммировать

На этот раз на странице операции была встроена функция связи перетаскиванием, которая дорабатывалась в процессе постоянных боев с монстрами и прокачки. Он включает в себя следующие моменты:

  • Оценка элемента, входящего в зону просмотраКоордината x левой стороны iframe на экране
  • Вставка элементов вверх и внизЦентральная линия перетаскиваемого элемента находится по координате y экрана координата y центральной линии внутреннего элемента iframe с экрана принадлежит задней вставке.
  • Проблема мутации координат clientXобработка разрешения z-индекса.
  • проблема с положением прокруткиgetBoundingClientRect разрешается.

Я надеюсь, что эта статья будет полезна для вас, и вы можете поделиться ею с нами.

Небольшой прогресс каждый день, качественные изменения от вниманияБольшой поворот FEНачинать.