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 206日志打印: 207 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