• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用Web组件的拖拽功能与网页交互
2<!--Kit: ArkWeb-->
3<!--Subsystem: Web-->
4<!--Owner: @zourongchun-->
5<!--Designer: @zhufenghao-->
6<!--Tester: @ghiker-->
7<!--Adviser: @HelloCrease-->
8
9ArkWeb的拖拽功能使应用能够在网页中实现元素的拖放,用户可以长按可拖拽的元素,将其拖至可放置的元素上,然后松手完成放置。ArkWeb在网页内容中的拖拽功能满足H5标准。
10
11## 将网页内容拖拽至其他应用
12
13ArkWeb目前支持以下四种数据格式。应用按照 H5 标准设置这些格式的拖拽数据,即可将内容拖拽到其他应用中。
14
15| 数据格式      | 说明     |
16| ------------- | -------- |
17| text/plain    | 文本     |
18| text/uri-list | 链接     |
19| text/html     | HTML格式 |
20| Files         | 文件     |
21
22## 拖拽事件通知
23
24ArkWeb拖拽不同于ArkUI的组件级拖拽,主要针对网页内容的拖拽,因此仅支持部分拖拽事件的监听方法。
25
26| 监听方法    | 说明                                                  |
27| ----------- | ----------------------------------------------------- |
28| [onDragStart](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragstart)  | 不建议使用此方法,否则会影响Web组件的拖拽行为,造成拖拽逻辑不符合预期,如无法触发html拖拽事件监听,预览图无法创建或预览图错误,拖拽数据无法预置等。|
29|  [onDragEnter](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragenter) | 拖拽的元素进入Web区域。 |
30| [onDragMove](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragmove)  | 拖拽的元素在Web区域移动。  |
31| [onDragLeave](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragleave) | 拖拽的元素离开Web区域。          |
32| [onDragEnd](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragend10) | 由Web发起的拖拽元素结束拖拽。         |
33
34## 在ArkTS侧实现拖拽相关逻辑
35
36在多数情况下,应用在H5端实现的拖拽功能能够满足需求。如有需要,请参考以下案例,实现在ArkTS端进行拖拽数据读取等操作。
371. [建立应用侧与前端页面数据通道](web-app-page-data-channel.md)。
382. 在onDrop方法中,做简单逻辑,例如暂存一些关键数据。
393. 在ArkTS侧接受消息的方法中,添加应用处理逻辑,可以进行耗时任务。
40
41由于ArkTS侧的onDrop方法会早于H5中放置事件的处理方法(html示例中的droppable.addEventListener('drop'))执行,若在onDrop方法中进行页面跳转等操作,将导致H5中的drop方法无法正确执行,产生不符合预期的结果。因此,应建立双向通信机制,在H5中的drop方法执行完毕后,通知ArkTS侧执行相应的业务逻辑,以确保业务逻辑的预期执行。
42
43```ts
44import { webview } from '@kit.ArkWeb'
45import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
46
47@Entry
48@Component
49struct DragDrop {
50  private controller: webview.WebviewController = new webview.WebviewController()
51  @State ports: Array<webview.WebMessagePort> = []
52  @State dragData: Array<unifiedDataChannel.UnifiedRecord> = []
53
54  build() {
55    Column() {
56      Web({
57        src: $rawfile("drag.html"),
58        controller: this.controller,
59      }).onPageEnd((event) => {
60        //注册通信端口
61        this.ports = this.controller.createWebMessagePorts();
62        this.ports[1].onMessageEvent((result: webview.WebMessage) => {
63          //ArkTS收到html传来的数据后的处理,可以先打日志确认下消息,双端的消息格式可以自己约定,能唯一识别就行
64          console.info("ETS receive Message: typeof (result) = " + typeof (result) + ";" + result);
65          //这里添加result中消息接收到后的处理,可进行耗时任务
66        });
67        console.info("ETS postMessage set h5port ");
68        //完成通信端口注册后,向前端发送注册完成消息,完成双向的端口绑定
69        this.controller.postMessage('__init_port__', [this.ports[0]], '*');
70      })// onDrop 可做简单逻辑,例如暂存一些关键数据
71        .onDrop((DragEvent: DragEvent) => {
72          console.info("ETS onDrop!")
73          let data: UnifiedData = DragEvent.getData();
74          if(!data) {
75            return false;
76          }
77          let uriArr: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
78          if (!uriArr || uriArr.length <= 0) {
79            return false;
80          }
81          // 可以遍历records取数据暂存,或者以其他方式暂存数据
82          for (let i = 0; i < uriArr.length; ++i) {
83            if (uriArr[i].getType() === uniformTypeDescriptor.UniformDataType.PLAIN_TEXT) {
84              let plainText = uriArr[i] as unifiedDataChannel.PlainText;
85              if (plainText.textContent) {
86                console.info("plainText.textContent: ", plainText.textContent);
87              }
88            }
89          }
90          return true
91        })
92    }
93
94  }
95}
96```
97
98html示例:
99
100```html
101<html lang="zh-CN">
102<head>
103    <meta charset="UTF-8">
104    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
105    <title>H5 拖拽 Demo</title>
106</head>
107<style>
108    body {
109      font-family: Arial, sans-serif;
110      padding: 20px;
111    }
112
113    .draggable {
114      width: 100px;
115      height: 100px;
116      background-color: #4CAF50;
117      color: white;
118      text-align: center;
119      line-height: 100px;
120      margin-bottom: 20px;
121      cursor: grab;
122    }
123
124    .droppable {
125      width: 300px;
126      height: 150px;
127      border: 2px dashed #999;
128      background-color: #f0f0f0;
129      text-align: center;
130      line-height: 150px;
131      font-size: 16px;
132    }
133
134    .success {
135      background-color: #4CAF50;
136      color: white;
137    }
138</style>
139<body>
140
141<h2>H5 拖拽 Demo</h2>
142
143<div id="draggable" class="draggable" draggable="true">可拖拽元素</div>
144
145<div id="droppable" class="droppable">请将方块拖到这里</div>
146
147<script>
148    const draggable = document.getElementById('draggable');
149    const droppable = document.getElementById('droppable');
150
151    // 拖拽开始事件
152    draggable.addEventListener('dragstart', function (e) {
153      e.dataTransfer.setData('text/plain', this.id);
154      this.style.opacity = '0.4';
155    });
156
157    // 拖拽结束事件
158    draggable.addEventListener('dragend', function (e) {
159      this.style.opacity = '1';
160    });
161
162    // 拖入目标区域时触发
163    droppable.addEventListener('dragover', function (e) {
164      e.preventDefault(); // 必须调用,否则无法触发 drop 事件
165    });
166
167    // 放置事件
168    droppable.addEventListener('drop', function (e) {
169      e.preventDefault();
170      const data = e.dataTransfer.getData('text/plain');
171      // 传入ArkTS
172      PostMsgToArkTS(data);
173      const draggableEl = document.getElementById(data);
174      this.appendChild(draggableEl);
175      this.classList.add('success');
176      this.textContent = "放置成功!";
177    });
178
179    // 	scriptproxy端口在js侧设置
180    var h5Port;
181    window.addEventListener('message', function (event) {
182    console.info("H5 receive settingPort message");
183        if (event.data == '__init_port__') {
184            if (event.ports[0] != null) {
185                console.info("H5 set h5Port " + event.ports[0]);
186                h5Port = event.ports[0];
187            }
188        }
189    });
190
191    // 通过scriptproxy方式,发送数据到ArkTS侧的实现
192    function PostMsgToArkTS(data) {
193        console.info("H5 PostMsgToArkTS, h5Port " + h5Port);
194        if (h5Port) {
195          h5Port.postMessage(data);
196        } else {
197          console.error("h5Port is null, Please initialize first");
198        }
199    }
200</script>
201
202</body>
203</html>
204```
205![web-drag-drop](figures/web-dragdrop.gif)
206日志打印:
207![web-drag-log](figures/web-drag-log.png)
208
209## 常见问题
210
211### 为什么H5设置的拖拽事件没有触发?
212请检查相关CSS资源是否正常设置,因为有些网页UA做了判断,针对特定设备的UA才会进行CSS样式设置。可以考虑在Web组件设置自定义UA解决这种问题,例如:
213
214```ts
215import { webview } from '@kit.ArkWeb'
216
217@Entry
218@Component
219struct Index {
220    private webController: webview.WebviewController = new webview.WebviewController()
221    build(){
222      Column() {
223        Web({
224          src: "example.com",
225          controller: this.webController,
226        }).onControllerAttached(() => {
227          // 特定UA
228          let customUA = 'android'
229          this.webController.setCustomUserAgent(this.webController.getUserAgent() + customUA)
230        })
231      }
232    }
233}
234```
235