#528·mui

关于微信客户端内置浏览器使用MUI的picker组件出现遮挡的问题

Author: yxnoooCreated May 19, 2023Updated Jun 30, 2023

问题描述

在ios版本的微信中的公众号中打开我们自研的项目,在执行选择器操作时出现了数据被遮挡以及部分数据无法被选中的问题。

系统框架

  • 操作系统:iOS
  • 系统版本:16.4.1
  • 手机型号:iPhone 13 Pro
  • 微信版本:8.0.37
  • 前端相关:HTML + MUI + JQuery

问题复现

在页面中点击“选择商品”,在底部弹出picker(选择器)控件,这时候出现了列表数据被遮挡的情况,上下滑动也是存在选择问题,附上图片如下:

  1. 点击“选择商品”后:

QQ图片20230519152601

2.在选择器中上下滑动后:

OK~}SFL63Z0{BG 7$9VWDPN

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;
    };