可视化搭建平台之跨iframe拖拽

本文详细介绍了在可视化搭建平台中实现跨iframe拖拽功能的过程,包括原生事件处理、拖放优化方案、滚动处理以及跨iframe通信。通过解决元素闪烁、坐标计算、z-index调整和滚动条影响等问题,成功实现组件在预览区的自由放置,并确保数据实时更新渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前段时间做运营活动搭建平台,其中一个主要功能:编辑页面分为左侧-组件区与右侧-预览区,需要实现组件区的内容可自由放置到预览区内。

类似下图所示:

社区内有一些类似的功能实现,但使用的方式大同小异,都离不开拖拽能力。我们日常开发中会经常用到的拖拽,如拖拽排序,拖拽上传等。当然拖拽的 npm 包也有很多,比较好用的包有 react-dnd, vue 自带的拖拽能力等。

但我们的预览区采用的是 iframe 方式,社区好用的类库一般不支持跨 iframe 的拖拽的能力。此处我们选择了使用原生拖拽 drag 和 dropAPI

需要实现的主要功能,有两点:

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 和 drop 拖拽

基于需求,拆分出拖拽的关键流程:

  • 初始化元素 设置拖动元素和目标节点

  • 注册事件 对拖动元素和目标节点元素注册 drag 事件

  • 监听事件 拖动过程中生成占位节点,拖动结束删除此占位节点

不完全代码如下:

//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 &#
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值