IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 基于uni-app的微信小程序Map事件穿透处理 -> 正文阅读

[移动开发]基于uni-app的微信小程序Map事件穿透处理

基于uni-app的微信小程序Map事件穿透处理

背景

业务需要在微信小程序中使用地图组件,上面需要有点位及点位的交互,同时地图上也会有一些悬浮的按钮、弹窗之类的。在微信小程序2.8.x的版本之后,地图这种原生组件是支持同层渲染的,也就是可以通过样式控制层级。在开发者工具中表现正常,但是上了真机后会发现点击事件会穿透(其实主要是marker的事件)。于是在微信社区找解决方案,结果又回到了用cover-image和cover-view的方式。这样又无法满足一些弹层(比如弹层中需要评论功能,需要使用input)的场景。

问题描述

demo

<template><view class="map_page_container"><mapclass="map"@tap="handleClickMap"@markertap="handleClickMarker"latitude="30.654228"longitude="104.066558":markers="markersList"></map><!-- 测试弹层穿透情况 --><div class="float_container" @tap="handleClickPrevent"><!-- input用于测试focus时的情况 --><input /><!-- 按钮用于测试点击穿透情况--><button @tap="handleClickButton">测试按钮</button></div></view>
</template>
<script> export default {data() {return {markersList: [{id: '1',latitude: '30.654228',longitude: '104.066558',iconPath: '/static/images/ge/guide.png',},],};},methods: {handleClickMap() {console.log('click map');},handleClickButton() {console.log('click button');},handleClickMarker(event) {console.log('click marker', event);},handleClickPrevent() {console.log('do nothing');},},
}; </script>
<style lang="scss" scoped> .map_page_container {width: 100%;height: 100vh;position: relative;overflow: hidden;.map {width: 100%;height: 100%;}.float_container {width: 100%;height: 500rpx;position: fixed;bottom: 0;left: 0;z-index: 1;opacity: 0.5;// 方便看marker的位置background-color: red;}
} </style> 

表现

// 下面圈表示点击区域,数字对应列表的序号

1.单独点击地图空白区域,会输出 “click map”
2.点击marker,会输出"click marker"

3.移动marker到button下面,点击button,会输出"click marker; click button; do nothing;"

4.移动marker到div的下面,点击marker区域,会输出"click marker; do nothing;"

5.移动marker到input的位置,选中input,会输出"do nothing"

从上面的表现可以看出,marker的层级是最高的,marker的事件不会穿透到map上,其他的dom会按冒泡的顺序执行事件(这里的事件指click或者tap,其他的还没有测试)。选中input不会触发marker的事件。

解决方案

思路

为了避免事件穿透,首先想到的肯定是preventDefault或者stopPropagation去阻止事件冒泡或者捕获(或者vue中的修饰符.stop之类的,道理一样就不说了)。但是从上面的表现也可以看出来,marker本身并不在冒泡或者捕获过程中,它就是最先触发的。经过验证也确实不能阻止它的tap事件触发。

起初我是想通过target来判定执行事件(事件代理),在通过给dom添加data-来判断执行顺序。但是发现marker与常规dom并不同。最后,我就想通过JS逻辑去限制事件函数的执行。注意,这里并不是阻止事件执行,我们没有办法阻止tap事件的执行。

基本逻辑就是通过执行优先级来决定执行谁,只要执行了某一个,其他的就不执行了。具体的我通过代码来表示吧。

demo

<template><view class="map_page_container"><mapclass="map"@tap="handleClickMap"@markertap="handleClickMarker"latitude="30.654228"longitude="104.066558":markers="markersList"></map><!-- Weex在view组件中增加了eventPenetrationEnabled属性,当值为true(默认为false)时,View的子View仍能正常响应事件,但View自身将不会响应事件。 ——摘自uniapp文档 --><div class="float_container" @tap="handleClickPrevent" eventPenetrationEnabled="true"><input /><!--.stop只是处理dom的事件捕获 和map本身没有关系 --><button @tap.stop="handleClickButton">测试按钮</button></div></view>
</template>
<script> export default {data() {return {clickEventList: [],// 记录点击事件timer: null,// 定时器markersList: [{id: '1',latitude: '30.654228',longitude: '104.066558',iconPath: '/static/images/ge/guid.png',},],};},watch: {clickEventList(c, p) {if (c.length > 0) {const temp = [...c];// 根据level进行排序,执行优先级最高的const cur = temp.sort((a, b) => a.level - b.level)[0];this.timerGenerator(cur.func);}},},methods: {// 处理定时器timerGenerator(func) {// 存在则清空定时器this.timer && clearTimeout(this.timer);// 存在func则100ms后执行if (func) {const TIMEOUT = 100; // 延迟100msthis.timer = setTimeout(func, TIMEOUT);}},handleClickMap() {// 点击事件push进数组this.clickEventList.push({func: () => {// 实际的执行函数console.log('click map');// 执行完成后清空点击事件列表// TODO 这个部分可以封装起来对所有事件函数进行处理this.clickEventList = [];},level: 3, // 优先级 越小则优先级越高});},handleClickButton() {this.clickEventList.push({func: () => {console.log('click button');this.clickEventList = [];},level: 1,});},handleClickMarker(event) {this.clickEventList.push({func: () => {console.log('click marker', event);this.clickEventList = [];},level: 2,// TODO marker的level应该为尽可能大的n比如10000,然后map的level则为n+1});},handleClickPrevent() {// 用于一些div view之类的没有实际执行逻辑的dom上 主要是阻止遮盖marker的时候的事件this.clickEventList.push({func: () => {console.log('do nothing');this.clickEventList = [];},level: 0,});},},destroyed() {// 清空定时器this.timerGenerator();},
}; </script>
// ...css一样就不写了 

经过验证目前可以满足当前业务需求,也就是阻止了map的事件穿透。

TODO

对地图进行封装,入参一个clickEventList,达到逻辑复用的目的。我理解正常情况下,map的tap层级应该是最低的,其次是marker的tap层级(也就是上面注释里的内容)。不过还没有验证,只是有个大概的思路,等验证了再来更新。

写在最后

这里只提供一下我的思路,目前来讲虽然实现了效果,但是不太好用。第一个问题就是需要人为设定执行优先级,就不太友好,最好是能通过dom层级来生成这个优先级,但是在uniapp或者说小程序环境中获取dom一直都不太好用。第二个问题就是这里是通过定时器进行延时,多少还是有点不稳定,比如两个事件触发超过100ms了就拉胯了,虽然正常情况下这种点击事件的穿透不会超过100ms,但是依然是有风险的。

本身是React栈,业务需要才来维护这个uniapp的项目,并不是很熟悉uniapp的生态。主要目的是为了跟大家讨论一下,我这个比较粗暴如果有优雅一点的方式欢迎提出。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:23:00  更:2022-09-04 01:23:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:27:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码