关于微信客户端ios版本内置浏览器使用MUI的picker组件出现遮挡的问题
【问题描述】 在ios版本的微信中的公众号中打开我们自研的项目,在执行选择器操作时出现了数据被遮挡以及部分数据无法被选中的问题。 【系统框架】 操作系统:iOS 系统版本:16.4.1 手机型号:iPhone 13 Pro 微信版本:8.0.37 前端相关:HTML + MUI + JQuery 【问题复现】 在页面中点击“选择商品”,在底部弹出picker(选择器)控件,这时候出现了列表数据被遮挡的情况,上下滑动也是存在选择问题,附上图片如下: (1)点击“选择商品”后: [图片] (2)在选择器中上下滑动后: [图片] (3)相对应的页面代码: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
pageContext.setAttribute("path", request.getContextPath());
%>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>补货</title>
<link rel="shortcut icon" type="image/x-icon" href="${path}/static/img/logo.png" media="screen">
<link href="${path}/static/css/mui.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="${path}/static/css/app.css" />
<link rel="stylesheet" type="text/css" href="${path}/static/css/mui.picker.css" />
<link rel="stylesheet" type="text/css" href="${path}/static/css/mui.poppicker.css" />
<link rel="stylesheet" type="text/css" href="${path}/static/css/style.css" />
</head>
<body style="background-color: #fff !important;padding-bottom: 60px !important;">
<header class="mui-bar mui-bar-nav login-header message-header">
<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
<h1 class="mui-title">补货</h1>
</header>
<div class="mui-content" style="position:fixed;width:100%">
<div class="rep-goods clearfix" >
<div class="rep-img mui-pull-left">
<a id="btn-detail" href="${path }/replenishment/detail.htm?cupboardId=${cupboard.id}">
<c:choose>
<c:when test="${cupboard.type == 3 }"><img src="${path }/static/img/jg_ic.png" /></c:when>
<c:when test="${cupboard.type == 0 }"><img src="${path }/static/img/hg_ic.png" /></c:when>
</c:choose>
</a>
</div>
<div class="replenishment-con mui-pull-left">
<p>机器编号:${machine.mid }</p>
<p>机器编码:${machine.code }</p>
<p class="add"><span class="mui-icon mui-icon-location"></span>${machine.location }</p>
</div>
</div>
<input type="hidden" id="mid" value="${mid }">
<input type="hidden" id="cupboardId" value="${cupboard.id }">
<input type="hidden" id="cupboardNo" value="${cupboard.cupboardNo }">
<div class="mui-slider floor-lis" style="background-color: #44A0FC;">
<div class="mui-slider-group">
<c:forEach items="${sliderMap }" var="map" varStatus="status">
<div class="mui-slider-item ${status.index == 0 ? 'active' : ''}">${map.key }层</div>
</c:forEach>
</div>
</div>
<div id="rep-goods-lis" class="rep-goods-lis" style="background-color:#FFF;overflow:scroll">
<c:forEach items="${sliderMap }" var="map" varStatus="status">
<ul class="mui-table-view ${status.index > 0 ? 'hid' : '' }">
<c:forEach items="${map.value }" var="slider">
<li>
<div class="top">
<input type="hidden" class="sliderId" value="${slider.sliderId }">
<input type="hidden" class="machineGoodsId" name="machineGoodsId" value="${slider.machineGoodsId }">
<input type="hidden" class="goodsPartId" name="goodsPartId" value="${slider.goodsPartId }">
<span class="hd">${slider.sliderNo } 货道</span>
<span class="goodsNameSpan">${slider.goodsName}<c:if test="${not empty slider.goodsPartName }"> - ${slider.goodsPartName }</c:if> </span>
</div>
<div class="rep-goods-con clearfix">
<div class="mui-col-xs-2 mui-pull-left">
<img src="${slider.goodsPic }" />
</div>
<div class="mui-col-xs-5 mui-pull-left flex-box">
<button id="goodsPopover" class="mui-btn btn-select-goods" type="button">选择商品</button>
</div>
<div class="flex-box">
<div class="mui-numbox mui-pull-left mui-col-xs-5" data-numbox-min='0' data-numbox-max='${slider.loadingQuantity }'>
<button class="mui-btn mui-btn-numbox-minus" type="button" disabled="">-</button>
<input name="stock" class="mui-input-numbox stock" type="number" value="${slider.stock }">
<button class="mui-btn mui-btn-numbox-plus" type="button">+</button>
</div>
</div>
</div>
</li>
</c:forEach>
</ul>
</c:forEach>
</div>
</div>
<input type="button" id="done" value="确定补货" class="bh-btn" />
<script src="${path}/static/js/jquery-3.2.1.min.js"></script>
<script src="${path}/static/js/mui.min.js"></script>
<script src="${path}/static/js/mui.picker.js"></script>
<script src="${path}/static/js/mui.poppicker.js"></script>
<script src="${path}/static/js/vue.min.js"></script>
<script>
function loadGoodsPartData() {
var goodsPartData = new Array();
$.ajax({
url: '${path}/replenishment/queryGoodsPartList.do?mid=${mid}',
async: false,
success: function(result) {
$.each(result, function(index, item) {
var obj = new Object();
obj.text = item.goodsPartName;
obj.machineGoodsId = item.machineGoodsId;
obj.goodsPartId = item.goodsPartId;
obj.goodsPartName = item.goodsPartName;
obj.goodsPic = item.goodsPic;
goodsPartData.push(obj);
});
}
});
var obj = new Object();
obj.text = ' ';
obj.machineGoodsId = 0;
obj.goodsPartName = '';
obj.goodsPic = '${path}/static/img/goods-default.png';
goodsPartData.push(obj);
return goodsPartData;
}
function addStock() {
$('.stock').blur();
var mid = $('#mid').val();
var cupboardId = $('#cupboardId').val();
var cupboardNo = $('#cupboardNo').val();
var sliderIdList = $('.sliderId');
var machineGoodsIdList = $('.machineGoodsId');
var goodsPartIdList = $('.goodsPartId');
var stockList = $('.stock');
var sliderGoodsList = new Array();
var len = sliderIdList.length;
for(var i = 0; i < len; i++) {
var machineGoodsId = machineGoodsIdList[i].value;
if(machineGoodsId && machineGoodsId != 0) {
var obj = new Object();
obj.sliderId = sliderIdList[i].value;
obj.machineGoodsId = machineGoodsId;
obj.goodsPartId = goodsPartIdList[i].value;
obj.stock = stockList[i].value;
sliderGoodsList.push(obj);
}
}
var data = {
mid : mid,
cupboardId : cupboardId,
cupboardNo : cupboardNo,
sliderGoodsList : sliderGoodsList
};
$.ajax({
type: 'post',
url: '${path}/replenishment/save.do',
data: JSON.stringify(data),
contentType: 'application/json',
success: function(result) {
if(result.resultCode == 200) {
mui.alert('补货成功', '提示', function() {
location.reload();
});
} else {
mui.toast(result.msg);
}
},
error : function() {
mui.alert('补货失败', '错误', function() {
location.reload();
});
}
});
}
mui.init();
(function($, doc, $$) {
$.init();
$.ready(function() {
var userPicker = new $.PopPicker();
userPicker.setData(loadGoodsPartData());
mui('body').on('tap', '.btn-select-goods', function(event) {
var elem = this;
var elemParent = elem.parentNode.parentNode.parentNode;
userPicker.show(function(items) {
var item = items[0];
elemParent.querySelector('.machineGoodsId').value = item.machineGoodsId;
elemParent.querySelector('.goodsPartId').value = item.goodsPartId;
elemParent.querySelector('.goodsNameSpan').innerText = item.goodsPartName;
elemParent.querySelector('img').setAttribute('src', item.goodsPic);
});
});
var done = document.getElementById('done');
done.addEventListener('tap', function() {
addStock();
});
});
})(mui, document, jQuery);
$(document).on('tap', '.floor-lis .mui-slider-item', function() {
var _num = $(this).index();
$(this).addClass('active').siblings().removeClass('active');
$('.rep-goods-lis .mui-table-view').eq(_num).show().siblings().hide();
});
mui(document).on('tap', 'a', function() {
var a = document.createElement('a');
a = this.cloneNode(true);
a.click();
});
$('#rep-goods-lis').height($(window).height()-210);
</script>
</body>
</html>
(4)附上所引用到的picker(选择器)的css代码以及js代码: mui.picker.css: .mui-picker {
background-color: #ddd;
position: relative;
height: 200px;
overflow: hidden;
border: solid 1px rgba(0, 0, 0, 0.1);
-webkit-user-select: none;
user-select: none;
box-sizing: border-box;
}
.mui-picker-inner {
box-sizing: border-box;
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
/*-webkit-mask-box-image: -webkit-linear-gradient(bottom, transparent, transparent 5%, #fff 20%, #fff 80%, transparent 95%, transparent);*/
/*-webkit-mask-box-image: linear-gradient(top, transparent, transparent 5%, #fff 20%, #fff 80%, transparent 95%, transparent);*/
}
.mui-pciker-list,
.mui-pciker-rule {
box-sizing: border-box;
padding: 0px;
margin: 0px;
width: 100%;
height: 36px;
line-height: 36px;
position: absolute;
left: 0px;
top: 50%;
margin-top: -18px;
}
.mui-pciker-rule-bg {
z-index: 0;
/*background-color: #cfd5da;*/
}
.mui-pciker-rule-ft {
z-index: 2;
border-top: solid 1px rgba(0, 0, 0, 0.1);
border-bottom: solid 1px rgba(0, 0, 0, 0.1);
/*-webkit-box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);*/
/*box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);*/
}
.mui-pciker-list {
z-index: 1;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transform: perspective(1000px) rotateY(0deg) rotateX(0deg);
transform: perspective(1000px) rotateY(0deg) rotateX(0deg);
}
.mui-pciker-list li {
width: 100%;
height: 100%;
position: absolute;
text-align: center;
vertical-align: middle;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
overflow: hidden;
box-sizing: border-box;
font-size: 16px;
font-family: "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
color: #888;
padding: 0px 8px;
white-space: nowrap;
-webkit-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
cursor: default;
visibility: hidden;
}
.mui-pciker-list li.highlight,
.mui-pciker-list li.visible {
visibility: visible;
}
.mui-pciker-list li.highlight {
color: #222;
}
mui.picker.js: (function($, window, document, undefined) {
var MAX_EXCEED = 30;
var VISIBLE_RANGE = 90;
var DEFAULT_ITEM_HEIGHT = 40;
var BLUR_WIDTH = 10;
var rad2deg = $.rad2deg = function(rad) {
return rad / (Math.PI / 180);
};
var deg2rad = $.deg2rad = function(deg) {
return deg * (Math.PI / 180);
};
var platform = navigator.platform.toLowerCase();
var userAgent = navigator.userAgent.toLowerCase();
var isIos = (userAgent.indexOf('iphone') > -1 ||
userAgent.indexOf('ipad') > -1 ||
userAgent.indexOf('ipod') > -1) &&
(platform.indexOf('iphone') > -1 ||
platform.indexOf('ipad') > -1 ||
platform.indexOf('ipod') > -1);
var isHighIos = (mui.os.ios) && (mui.os.version >= "16.2");
var Picker = $.Picker = function(holder, options) {
var self = this;
self.holder = holder;
self.options = options || {};
self.init();
self.initInertiaParams();
self.calcElementItemPostion(true);
self.bindEvent();
};
Picker.prototype.findElementItems = function() {
var self = this;
self.elementItems = [].slice.call(self.holder.querySelectorAll('li'));
return self.elementItems;
};
Picker.prototype.init = function() {
var self = this;
self.list = self.holder.querySelector('ul');
self.findElementItems();
self.height = self.holder.offsetHeight;
self.r = self.height / 2 - BLUR_WIDTH;
self.d = self.r * 2;
self.itemHeight = self.elementItems.length > 0 ? self.elementItems[0].offsetHeight : DEFAULT_ITEM_HEIGHT;
self.itemAngle = parseInt(self.calcAngle(self.itemHeight * 0.8));
self.hightlightRange = self.itemAngle / 2;
self.visibleRange = VISIBLE_RANGE;
self.beginAngle = 0;
self.beginExceed = self.beginAngle - MAX_EXCEED;
self.list.angle = self.beginAngle;
if (isIos) {
self.list.style.webkitTransformOrigin = "center center " + self.r + "px";
}
if (isHighIos) {
self.list.style.webkitTransformOrigin = "center center";
}
};
Picker.prototype.calcElementItemPostion = function(andGenerateItms) {
var self = this;
if (andGenerateItms) {
self.items = [];
}
self.elementItems.forEach(function(item) {
var index = self.elementItems.indexOf(item);
self.endAngle = self.itemAngle * index;
item.angle = self.endAngle;
item.style.webkitTransformOrigin = "center center -" + self.r + "px";
item.style.webkitTransform = "translateZ(" + self.r + "px) rotateX(" + (-self.endAngle) + "deg)";
if (andGenerateItms) {
var dataItem = {};
dataItem.text = item.innerHTML || '';
dataItem.value = item.getAttribute('data-value') || dataItem.text;
self.items.push(dataItem);
}
});
self.endExceed = self.endAngle + MAX_EXCEED;
self.calcElementItemVisibility(self.beginAngle);
};
Picker.prototype.calcAngle = function(c) {
var self = this;
var a = b = parseFloat(self.r);
//直径的整倍数部分直接乘以 180
c = Math.abs(c); //只算角度不关心正否值
var intDeg = parseInt(c / self.d) * 180;
c = c % self.d;
//余弦
var cosC = (a * a + b * b - c * c) / (2 * a * b);
var angleC = intDeg + rad2deg(Math.acos(cosC));
return angleC;
};
Picker.prototype.calcElementItemVisibility = function(angle) {
var self = this;
self.elementItems.forEach(function(item) {
var difference = Math.abs(item.angle - angle);
if (difference < self.hightlightRange) {
item.classList.add('highlight');
} else if (difference < self.visibleRange) {
item.classList.add('visible');
item.classList.remove('highlight');
} else {
item.classList.remove('highlight');
item.classList.remove('visible');
}
});
};
Picker.prototype.setAngle = function(angle) {
var self = this;
self.list.angle = angle;
self.list.style.webkitTransform = "perspective(1000px) rotateY(0deg) rotateX(" + angle + "deg)";
self.calcElementItemVisibility(angle);
};
Picker.prototype.bindEvent = function() {
var self = this;
var lastAngle = 0;
var startY = null;
var isPicking = false;
self.holder.addEventListener($.EVENT_START, function(event) {
isPicking = true;
event.preventDefault();
self.list.style.webkitTransition = '';
startY = (event.changedTouches ? event.changedTouches[0] : event).pageY;
lastAngle = self.list.angle;
self.updateInertiaParams(event, true);
}, false);
self.holder.addEventListener($.EVENT_END, function(event) {
isPicking = false;
event.preventDefault();
self.startInertiaScroll(event);
}, false);
self.holder.addEventListener($.EVENT_CANCEL, function(event) {
isPicking = false;
event.preventDefault();
self.startInertiaScroll(event);
}, false);
self.holder.addEventListener($.EVENT_MOVE, function(event) {
if (!isPicking) {
return;
}
event.preventDefault();
var endY = (event.changedTouches ? event.changedTouches[0] : event).pageY;
var dragRange = endY - startY;
var dragAngle = self.calcAngle(dragRange);
var newAngle = dragRange > 0 ? lastAngle - dragAngle : lastAngle + dragAngle;
if (newAngle > self.endExceed) {
newAngle = self.endExceed
}
if (newAngle < self.beginExceed) {
newAngle = self.beginExceed
}
self.setAngle(newAngle);
self.updateInertiaParams(event);
}, false);
//--
self.list.addEventListener('tap', function(event) {
elementItem = event.target;
if (elementItem.tagName == 'LI') {
self.setSelectedIndex(self.elementItems.indexOf(elementItem), 200);
}
}, false);
};
Picker.prototype.initInertiaParams = function() {
var self = this;
self.lastMoveTime = 0;
self.lastMoveStart = 0;
self.stopInertiaMove = false;
};
Picker.prototype.updateInertiaParams = function(event, isStart) {
var self = this;
var point = event.changedTouches ? event.changedTouches[0] : event;
if (isStart) {
self.lastMoveStart = point.pageY;
self.lastMoveTime = event.timeStamp || Date.now();
self.startAngle = self.list.angle;
} else {
var nowTime = event.timeStamp || Date.now();
if (nowTime - self.lastMoveTime > 300) {
self.lastMoveTime = nowTime;
self.lastMoveStart = point.pageY;
}
}
self.stopInertiaMove = true;
};
Picker.prototype.startInertiaScroll = function(event) {
var self = this;
var point = event.changedTouches ? event.changedTouches[0] : event;
/**
* 缓动代码
*/
var nowTime = event.timeStamp || Date.now();
var v = (point.pageY - self.lastMoveStart) / (nowTime - self.lastMoveTime); //最后一段时间手指划动速度
var dir = v > 0 ? -1 : 1; //加速度方向
var deceleration = dir * 0.0006 * -1;
var duration = Math.abs(v / deceleration); // 速度消减至0所需时间
var dist = v * duration / 2; //最终移动多少
var startAngle = self.list.angle;
var distAngle = self.calcAngle(dist) * dir;
//----
var srcDistAngle = distAngle;
if (startAngle + distAngle < self.beginExceed) {
distAngle = self.beginExceed - startAngle;
duration = duration * (distAngle / srcDistAngle) * 0.6;
}
if (startAngle + distAngle > self.endExceed) {
distAngle = self.endExceed - startAngle;
duration = duration * (distAngle / srcDistAngle) * 0.6;
}
//----
if (distAngle == 0) {
self.endScroll();
return;
}
self.scrollDistAngle(nowTime, startAngle, distAngle, duration);
};
Picker.prototype.scrollDistAngle = function(nowTime, startAngle, distAngle, duration) {
var self = this;
self.stopInertiaMove = false;
(function(nowTime, startAngle, distAngle, duration) {
var frameInterval = 13;
var stepCount = duration / frameInterval;
var stepIndex = 0;
(function inertiaMove() {
if (self.stopInertiaMove) return;
var newAngle = self.quartEaseOut(stepIndex, startAngle, distAngle, stepCount);
self.setAngle(newAngle);
stepIndex++;
if (stepIndex > stepCount - 1 || newAngle < self.beginExceed || newAngle > self.endExceed) {
self.endScroll();
return;
}
setTimeout(inertiaMove, frameInterval);
})();
})(nowTime, startAngle, distAngle, duration);
};
Picker.prototype.quartEaseOut = function(t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
};
Picker.prototype.endScroll = function() {
var self = this;
if (self.list.angle < self.beginAngle) {
self.list.style.webkitTransition = "150ms ease-out";
self.setAngle(self.beginAngle);
} else if (self.list.angle > self.endAngle) {
self.list.style.webkitTransition = "150ms ease-out";
self.setAngle(self.endAngle);
} else {
var index = parseInt((self.list.angle / self.itemAngle).toFixed(0));
self.list.style.webkitTransition = "100ms ease-out";
self.setAngle(self.itemAngle * index);
}
self.triggerChange();
};
Picker.prototype.triggerChange = function(force) {
var self = this;
setTimeout(function() {
var index = self.getSelectedIndex();
var item = self.items[index];
if ($.trigger && (index != self.lastIndex || force === true)) {
$.trigger(self.holder, 'change', {
"index": index,
"item": item
});
//console.log('change:' + index);
}
self.lastIndex = index;
typeof force === 'function' && force();
}, 0);
};
Picker.prototype.correctAngle = function(angle) {
var self = this;
if (angle < self.beginAngle) {
return self.beginAngle;
} else if (angle > self.endAngle) {
return self.endAngle;
} else {
return angle;
}
};
Picker.prototype.setItems = function(items) {
var self = this;
self.items = items || [];
var buffer = [];
self.items.forEach(function(item) {
if (item !== null && item !== undefined) {
buffer.push('<li>' + (item.text || item) + '</li>');
}
});
self.list.innerHTML = buffer.join('');
self.findElementItems();
self.calcElementItemPostion();
self.setAngle(self.correctAngle(self.list.angle));
self.triggerChange(true);
};
Picker.prototype.getItems = function() {
var self = this;
return self.items;
};
Picker.prototype.getSelectedIndex = function() {
var self = this;
return parseInt((self.list.angle / self.itemAngle).toFixed(0));
};
Picker.prototype.setSelectedIndex = function(index, duration, callback) {
var self = this;
self.list.style.webkitTransition = '';
var angle = self.correctAngle(self.itemAngle * index);
if (duration && duration > 0) {
var distAngle = angle - self.list.angle;
self.scrollDistAngle(Date.now(), self.list.angle, distAngle, duration);
} else {
self.setAngle(angle);
}
self.triggerChange(callback);
};
Picker.prototype.getSelectedItem = function() {
var self = this;
return self.items[self.getSelectedIndex()];
};
Picker.prototype.getSelectedValue = function() {
var self = this;
return (self.items[self.getSelectedIndex()] || {}).value;
};
Picker.prototype.getSelectedText = function() {
var self = this;
return (self.items[self.getSelectedIndex()] || {}).text;
};
Picker.prototype.setSelectedValue = function(value, duration, callback) {
var self = this;
for (var index in self.items) {
var item = self.items[index];
if (item.value == value) {
self.setSelectedIndex(index, duration, callback);
return;
}
}
};
if ($.fn) {
$.fn.picker = function(options) {
//遍历选择的元素
this.each(function(i, element) {
if (element.picker) return;
if (options) {
element.picker = new Picker(element, options);
} else {
var optionsText = element.getAttribute('data-picker-options');
var _options = optionsText ? JSON.parse(optionsText) : {};
element.picker = new Picker(element, _options);
}
});
return this[0] ? this[0].picker : null;
};
//自动初始化
$.ready(function() {
$('.mui-picker').picker();
});
}
})(window.mui || window, window, document, undefined);
//end
【预期效果】 点击“选择商品”按钮后弹出picker(选择器),列表数据能够正常显示,且上下滑动没有问题,附上图片如下: [图片] [图片] 【其他相关】 一、我从MUI的官方接口提供的示例中发现,用ios16.4以上版本的手机打开均存在这个问题,MUI的示例地址如下(用手机打开): https://www.dcloud.io/hellomui/examples/picker.html (1)以下分别是一级选择示例、二级选择示例、三级选择示例: [图片] [图片] [图片] (2)从官方github得知修复的方法: 将mui.picker.css文件中的.mui-picker-inner中的两行代码注释即能解决问题:[图片] 按照这个方法,我已经在此文件中注释掉了,并且在mui.picker.js中加上了对ios16.2版本以上的判断: var isHighIos = (mui.os.ios) && (mui.os.version >= "16.2");
if (isHighIos) {
self.list.style.webkitTransformOrigin = "center center";
}
这样子的操作后,我的项目(这个html页面)在手机浏览器中打开picker(选择器)显示的数据是正常不被遮挡的,如下图表示: [图片] 但是,在微信里面打开还是存在这个问题,我想这个是不是和手机微信内置浏览器有关,所以想请教一下你们官方团队,能不能给我个解决方案,希望能够在这次的反馈中获取到你们的帮助,万分感谢谢!