H5嵌入原生----兼容安卓与ios

主要分UI展示,键盘,输入框等等。解决bug最苦恼的问题不是没有解决方案,而是你没有找到真正的原因。再就是现象难以重现,每次都要发布代码,然后到手机app中去测试,模拟。这些地方会耗费大量的精力。

一、UI相关

  1. 安卓4.4以下不支持fixed布局。
    fixed布局的作用之一就是在手机键盘弹起来的时候,可以自动把页面顶起来。如果不支持的话,换绝对定位也是可以的。但是绝对定位某些机型比如sm-n7508,华为m7上还是没有能顶起来。IOS没有这个问题。
  2. 小于1px显示问题
    部分安卓机器(比如小米)的分辨率低,如果border的宽度小于1px。安卓机出现一种边框消失了的现象。样式上有点奇怪,IOS没有这个问题。
    在这里插入图片描述

一开始以为是别的元素挡住了,但是调了半天无解。最后突然意识到是不是计算出来的高度有小数导致的。然后马上取整:

$target.css(“height”, Math.ceil(maxline * lineHeight));

但是,华为的某些类型的是上面显示不正常,出来一排点点。
在这里插入图片描述

再修正一下:

$target.css(“height”, Math.ceil(maxline * lineHeight) - 1);

或者用floor。你好奇为什么会有小数高度呢,因为这个功能设计一个折叠,需要重新计算dom的高度。

二、键盘相关

  1. ios键盘挡住输入框。
    这个发生的频率很高,中文输入法或者输入法切换的时候会遮挡。
    在这里插入图片描述

解决的办法如下:

setInterval(function () { 
     if (document.activeElement.className.indexOf('inputcontent') >= 0) {
         document.activeElement.scrollIntoViewIfNeeded();     }
 }, 300);

这是最管用的办法,inputcontent为输入框的样式。activeElement表示获得焦点的元素。但是这个方法只在app中有用,如果是在浏览器中还是会失效。

  1. 键盘收下留下空白阴影。
    这个在部分安卓机上比较明显,当键盘在激活状态,突然来一个模态弹框,很明显的看到键盘收下去之后出现了一个短暂(不到1秒的样子)的空白,然后页面再收下去,让人感觉闪了下。比如华为P7。但是ios上没有关系,这个问题没招,系统不流畅。
  2. 无法保持键盘在弹出状态。
    web其实无法直接控制键盘,只有通过让输入框获focus来让手机的键盘弹出来,但是三星SM-N8507v 这款就是不听话。我也无解了。
  3. 确保弹出来的是数字键盘
<input type="number" pattern="[0-9]*" />
<input type="password" pattern="[0-9]*" />

只有number或者tel还是不够,只有加入正则,ios才会出现九宫格。

三、输入框相关

  1. fastClick 锁住输入框。
    在ios中,会出现几秒的输入框没有反应,开始也怎么想不明白,各种尝试,推测,搜索发现原来是使用的轻框架中用到了fastClik引起的,解决的办法就是加上一个样式。
  <div id="content" class="inputcontent needsclick" ></div>

解决方法到是简单,但是当初为找到这个原因,费了好大劲,把框架的模块一个一个删,最后才定位到。如果不加这个,不单会锁住输入框,每次调用focus都会设置光标跑在最前面,无法移动到后面。这个体验很糟糕。

  1. 模拟placeholder。
    div作为输入框,本身加入placeholder是无效的。得借助于伪元素。
<div id="content" class="inputcontent needsclick" placeholder="有问题就尽管提问吧" contenteditable="true"></div>

.tools .inputcontent:after {
display: inline-block;
width: 100%;
color: #999;
content: attr(placeholder);
}

然后在获得焦点和失去焦点的时候对这个属性进行移除和添加操作。这样做的好处在于,分离用户输入的内容,如果把placeholder的值直接在写输入框中这样会多一些判断,让代码不太干净。

  1. div的换行是div
    在div中触发换行,会得到一个div。这个就相当于是在编辑器中。而不是我们认为的
    或者换行符。
    在这里插入图片描述

所以,需要对div进行过滤替换。

 var replace = /<div.*?>(.*?)<\/div>/g;
    txt = txt.replace(replace, function(a) {
     if (a != "<br>") {
            return "<br>" + delHtmlTag(a);
        }
   return delHtmlTag(a);}
  1. 粘贴莫名奇妙带了样式
    在输入框中粘贴会以这样的形式出现:
<font style='font-size:30px;color:#999'><span>粘贴内容</span></font>

不清楚是系统还是app定义的。所以也得过滤。 用户还可以粘贴其他文章,容易搞乱输入框中的样式,需要加上:

.tools .inputcontent *{
	font-size: 0.50rem !important; 
	color: #000 !important;
	line-height: 22px !important;
	font-weight: normal !important;
}

因为如果从粘贴事件里面处理的话,容易搞乱焦点,让焦点在最前面。这样很不爽。所以还是样式控制,在发送的时候过滤掉所有的标签。

  1. 超出遮挡/换行遮挡
    这是一个神奇的bug,当内容超过div的最大高度后,最后一行出现一个神奇的现象,头两个字显示了,后面的内容不见了(快快后面其实有内容),直到下一次换行才会出现。
    在这里插入图片描述

我alert里面的内容,发现并没有其他的元素,不断尝试,发现div overflow: hidden;的时候字都会显示出来,但是为了让用户能够在内容框里面上下滚动。我得让y轴是可以滚动的:overflow-y: auto; 所以应该是滚动条引起的。但不知道如何修改。后来试出一个hack的方法。只要有一个换行就不会出现这种情况。所以,我就在用户输入到特定行的时候就塞进一个1px的换行:

  if ($("#content").height() == 88 && isIOS() && !haveAppendBr) {
                $("#content").append("<div style='height:1px;border-top:1px solid #fff' ><br></div>");
                haveAppendBr = true;
   }
  1. 安卓第一次不能换行
    这个现象是消息发送成功之后,用户(小米)一来就是点换行,却无法换行,怀疑是安卓系统阻止了这样的行为。但是在输入一个字之后,换行就是正常的了(哪怕再删掉这个字)。ios里面没有这个问题。开始我尝试去人为加一个换行,又发现焦点没了。想想这样问题不改也罢。

  2. 输入框光标闪动
    这个是translate3d属性引起,修改sm框架中的一段代码设置useTranslate为false。位于handleTouchMove方法中。

四、图片相关

  1. 安卓不能上传。
    安卓很多时候都不能触发input type='file’的弹框,上传就得走原生的帮助。原生拦截到链接后就会弹出图片选择框。
window.location = 'xxapp:h5Upload({"openType":2,"needChop":false,"uploadUrl": '+fileUploadUrl+',"key":' + totalFiles + ',"callbackMethodName": "uploadComplete"})';

key是为了记住是第几张图片,便于在原生上传结束之后把地址和key一起传到uploadComplete方法中。这样界面上才可以做相应的修改。但这不是我说的重点。重点是Url不要带中文啊或者特殊符号之类的。一方面很多手机里面的图片命名很奇怪,一大堆,像在uc上面保存下来的图片后面跟了几个类型。这种名称没啥用,非常建议服务器端像微信一样处理上传图片,成功之后得到一个唯一的字符串就行。不然有的系统会自动解码你得区分的用上了encodeURI或者encodeURIComponent。测试会说这个手机可以,那个不可以。

  1. 图片转了90度。
    安卓部分机型(小米2A,三星N7,三星G9)对于拍照的图片上传之后居然左转了90度。
    拍摄的图片本身会带有一个exifdata,这个对象里面包含了拍摄的时间、方向、曝光时间等一些信息。用exif.js可以看出来。也有网站可以直接查看。
 function getdata(id) {
            EXIF.getData(document.getElementById(id), function () {
                var data = EXIF.getTag(this, 'Orientation');
                console.log(data);
            });
        }

思路就是通过这个方向来判断是否给图片来个再旋转。
this.style.transform = ‘rotate(’ + current + ‘deg)’;
实际没有使用exif.js,因为exif还需要再请求一次,而且这个图片有验证,居然还读不到方向信息。最后让后端对上传图片进行方向旋转纠正

public static Bitmap RotateImage(Stream sm)
       {
           Image img = Image.FromStream(sm);
           var exif = img.PropertyItems;
           byte orien = 0;
           var item = exif.Where(m => m.Id == 274).ToArray();
           if (item.Length > 0)
               orien = item[0].Value[0];
           switch (orien)
           {
               case 2:
                   img.RotateFlip(RotateFlipType.RotateNoneFlipX);//horizontal flip  
                   break;
               case 3:
                   img.RotateFlip(RotateFlipType.Rotate180FlipNone);//right-top  
                   break;
               case 4:
                   img.RotateFlip(RotateFlipType.RotateNoneFlipY);//vertical flip  
                   break;
               case 5:
                   img.RotateFlip(RotateFlipType.Rotate90FlipX);
                   break;
               case 6:
                   img.RotateFlip(RotateFlipType.Rotate90FlipNone);//right-top  
                   break;
               case 7:
                   img.RotateFlip(RotateFlipType.Rotate270FlipX);
                   break;
               case 8:
                   img.RotateFlip(RotateFlipType.Rotate270FlipNone);//left-bottom  
                   break;
               default:
                   break;
           }
           return (Bitmap)img;
       }


 [HttpPost]
       public ActionResult UploadImg(HttpPostedFileBase file, string dir = "UserImg")
       {
           if (CheckImg(file) != "ok") return Json(new { Success = false, Message = "文件格式不对!" }, JsonRequestBehavior.AllowGet);

           if (file != null)
           {
               var path = "~/Content/UploadFiles/" + dir + "/";
               var uploadpath = Server.MapPath(path);
               if (!Directory.Exists(uploadpath))
               {
                   Directory.CreateDirectory(uploadpath);
               }
               string fileName = Path.GetFileName(file.FileName);// 原始文件名称
               string fileExtension = Path.GetExtension(fileName); // 文件扩展名
               //string saveName = Guid.NewGuid() + fileExtension; // 保存文件名称 这是个好方法。
               string saveName = Encrypt.GenerateOrderNumber() + fileExtension; // 保存文件名称 这是个好方法。
               var saveUrl = uploadpath + saveName;
              // file.SaveAs(saveUrl);
               System.Drawing.Image image = ImageManageHelper.RotateImage(file.InputStream);
               image.Save(saveUrl);
               if (file.ContentLength / 1024 > 500)//大于0.5M
               {
                   var _saveName = Encrypt.GenerateOrderNumber() + "_thumbnailUrl" + fileExtension;
                   var thumbnailUrl = uploadpath + _saveName;
                   var maxh = 400;
                   var maxw = 400;
                   
                   if (image.Width>maxw)
                   {
                       maxh = 400*image.Height/image.Width;
                   }

                   ImageManageHelper.GetPicThumbnail(saveUrl, thumbnailUrl, maxh, maxw, 90);
                   return Json(new { Success = true, SaveName = "/Content/UploadFiles/Mobile/"  +_saveName });
               }
               return Json(new { Success = true, SaveName = "/Content/UploadFiles/Mobile/" + saveName });
           }
           return Json(new { Success = false, Message = "请选择要上传的文件!" }, JsonRequestBehavior.AllowGet);

       }
  1. 图片半截
    这个问题在安卓上面会有,不是加载的问题。正确的效果图片应该是垂直居中的。但不知道为什么偶尔的会只出来个半截。而且我发现,给图片设置百分比,手机和pc不一样,手机图片的百分比并不是相对于其父类元素,而是它自己。
    在这里插入图片描述

所以图片的宽度会超出其父类,即使div img的宽度都是100%。overflow:hidden吧,图片可能显示不全。超出的部分会导致用户可以在图片上面左右滑动,这在ios中有个搞笑的现象,就是对弹出的图片不断的左右滑动,再恢复后居然能让原先绑定的点击事件失效,不确定是框架的原因还是系统的原因。当时是用一个模态框改造的

Client.modalImg = function (src) {
   if (!src) return;
   if ($(".img-overlay").length) {
       $(".img-overlay").remove();
   };
   var modal = '<div class="img-overlay"> <div class="imgheader"></div> <div class="imgbox"><img οnlοad="reCalcuImg();" src="' + src + '" /></div>';
   $("body").append(modal);
};

//校准位置
function reCalcuImg() {
   var headerH = $(".imgheader").height();
   var boxH = $(".imgbox").height();
   var imgH = $(".imgbox img").height();
   var realMaxH = boxH - headerH;
   // console.log("headerH", headerH, "boxH", boxH, "imgH", imgH, "realMaxH", realMaxH);
   if (imgH > realMaxH) {
       $(".imgbox img").css("height", realMaxH + "px");
       console.log("大于最大高度,realMaxH", realMaxH);

   } else {
       var gap = (realMaxH - imgH) / 2;
       // console.log("小于最大高度,margintop",(realMaxH - imgH), gap);
       $(".imgbox img").css("margin-top", gap + "px");
   }
}

写在onload事件结束后是确保图片已经加载完成。这样才能计算,如果直接在modalimg中计算,图片的高度可能为0。然后如果图片的高度大于最大高度则设置为最大高度,否则的话在进行margin,让其垂直居中。
现在使用的是photoSwipe插件。需要结合图片的onload事件先存下图片的原始宽高。

//图片加载完成后调用
function imgloading(img,srcoll) {
console.log("imgloading", img.height);
 var cached = {
       height: img.naturalHeight,
       width: img.naturalWidth,
       src: img.src
   };

   imClient.imgCache[img.src] = cached;
   srcoll&&imClient.scroll();
}

获取原始宽高是为了显示时候的比例自然:

//图片放大
   imClient.imgCache = {};
      var ispop = false;
   function getaimg(src) {
          if (ispop) return;

       function loadaction(w,h) {
           imClient.debugSay("图片加载:w" + w + " h:" + h);
           loadimg(src, w, h);
           ispop = false;
       }

       var cached = imClient.imgCache[src];
       if (cached) {
           ispop = true;
           loadaction(cached.height, cached.width);
           return;
       }
       console.log("未加载");
   }
   $(document).on("click", ".bubbleimgright img,.bubbleimgleft img", function (e) {
       if ($(this).hasClass("msgfailimg")) return;
       var url = $(this).attr("src");//就放大缩略图
        getaimg(url);

   });
   function loadimg(url, hei, width) {
       var pswpElement = document.querySelectorAll('.pswp')[0];
       var maxH = $("#historylist").height();
       var maxW = $("#historylist").width();
       var fH = 400;
       var fW = 400;
       //如果都比默认的小
       if (hei <= maxH && width <= maxW) {
           fH = hei;
           fW = width;
       }
       if (hei > maxH && width < maxW) {
           fH = maxH;
           fW = Math.ceil((maxH * width) / hei);
       }
       if (width > maxW) {
           fW = maxW;
           fH = Math.ceil((maxW * hei) / width);
       }
       // build items array
       var items = [
           {
               src: url,
               w: fW,
               h: fH
           }
       ];
       // define options (if needed)
       var options = {
           index: 0 // start at first slide
       };
       var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
       gallery.init();
   }

五、消息

  1. websocket是脆弱但又顽强的。
    websocket很容易受到网络的影响而中断,但网络一恢复能自动重连。而手机会有切到后台运行的这种情况,比如小米系统会在手机黑屏之后把网络断掉,用户进入应用的时候,你得有重连的机制,确保消息没有遗漏。
	socket.on("reconnect", function () {
	                isConneted = true;
	                eventManger.trigger("reconnect");
	                listenChannel();
	   });

在listenChannel中通过 socket.emit 告之node后端重连了,拿消息的姿势调整下。
but红米手机有一款socketio老是重连,所以手机上也要准备轮询的机制。重连三次关掉socket,直接轮询。
2. 消息先发后到。
先发的消息后到,这是很有可能的,但是用户就会奇怪。比如一条长消息分成几次发,前端可以在前一条发完了再发第二条。

       var limit = 1000;
       function loop(i) {
           if (i < num) {
               send(content.substr(i * limit, limit), function () {
                   i++;
                   loop(i);
               });
           }
       };
       loop(0);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/625984.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ C# 贝塞尔曲线

二阶贝塞尔曲线公式 三阶贝塞尔曲线公式 C 三维坐标点 二阶到N阶源码 //二阶公式&#xff1a; FVector BezierUtils::CalculateBezierPoint(float t, FVector startPoint, FVector controlPoint, FVector endPoint) {float t1 (1 - t) * (1 - t);float t2 2 * t * (1 - t);…

MQ消息积压,把我整吐血了

我之前在一家餐饮公司待过两年&#xff0c;每天中午和晚上用餐高峰期&#xff0c;系统的并发量不容小觑。为了保险起见&#xff0c;公司规定各部门都要在吃饭的时间轮流值班&#xff0c;防止出现线上问题时能够及时处理。 我当时在后厨显示系统团队&#xff0c;该系统属于订单…

图像归一化处理

归一化 归一化是一种简化计算的方式&#xff0c;即将有量纲的表达式&#xff0c;经过变换&#xff0c;化为无量纲的表达式&#xff0c;成为标量。 在多种计算中都经常用到这种方法。 简单介绍 归一化是一种无量纲处理手段&#xff0c;使物理系统数值的绝对值变成某种相对值关…

一台linux通过另一台linux访问互联网-TinyProxy

参考&#xff1a; https://blog.csdn.net/weixin_41831919/article/details/113061317https://www.yuncongz.com/archives/1.htmlhttps://blog.csdn.net/aoc68397/article/details/101893369 环境&#xff1a;ubuntu 18.04 机器1: IP 219.216.65.252 (可以访问外网) 机器2: IP…

文本换列新手教程:一键两列对换操作,提升文本编辑效率的实用技巧

在文本编辑过程中&#xff0c;有时我们需要对文本进行列与列之间的对换操作&#xff0c;以满足特定的排版需求或信息呈现方式。这种操作虽然看似简单&#xff0c;但对于新手来说&#xff0c;可能会因为不熟悉相关技巧而感到困惑。今天&#xff0c;我们将为大家介绍一种简单而高…

研究生学习---找工作

规划 研一~研二上学期完成小论文&#xff0c;实习&#xff0c;秋招 竞赛&#xff1a;kaggle&#xff1f; 面试题一般简单且为原题&#xff0c;笔试题目很难&#xff0c;不会出原题 项目 找工作软件

微软必应bing国内官方代理商,广告账户如何开户?

微软必应Bing作为全球知名的搜索引擎之一&#xff0c;其广告平台为众多企业提供了广阔的市场空间和精准的推广机会。对于中国内地的企业而言&#xff0c;通过必应Bing开展国内广告推广不仅能够触及更广泛的潜在客户群体&#xff0c;还能有效提升品牌影响力。通过微软必应Bing国…

大数据可视化实验(五):Tableau数据可视化

目录 一、实验目的... 1 二、实验环境... 1 三、实验内容... 1 1&#xff09;打开数据源... 1 2&#xff09;进入工作簿... 2 3&#xff09;字段设置... 2 4&#xff09;数据筛选... 3 5&#xff09;绘制条形图... 3 四、思考问题... 4 五、总结与心得体会... 4 一、…

面对.halo勒索病毒,如何有效防范与应对?

导言&#xff1a; 随着网络技术的不断发展&#xff0c;网络安全问题也日益凸显。其中&#xff0c;勒索病毒作为一种极具破坏性的网络攻击手段&#xff0c;近年来在全球范围内频发。其中&#xff0c;.halo勒索病毒作为勒索病毒家族中的一员&#xff0c;其危害性和传播性不容忽视…

Intel HDSLB 高性能四层负载均衡器 — 快速入门和应用场景

目录 文章目录 目录前言与背景传统 LB 技术的局限性HDSLB 的特点和优势HDSLB 的性能参数基准性能数据对标竞品 HDSLB 的应用场景HDSLB 的发展前景参考文档 前言与背景 在云计算、SDN、NFV 高速发展并普遍落地的今天&#xff0c;随着上云业务的用户数量越来越多、数据中心的规模…

pve clusterk8s cluster重建

背景 dev环境新买了一块固态&#xff0c;插到pve主板的第二个M.2槽位不能识别&#xff0c;通过其他机器排查后确定是这台pve的槽位问题。 同时这台pve还有一些其他的lvm历史遗留问题&#xff0c;具体表现是每次开机很长&#xff0c;因为要扫描那块机械硬盘做的lvm 所以决定将这…

ubuntu编译pcl时报错

报错如下 cc1plus: warning: -Wabi wont warn about anything [-Wabi] cc1plus: note: -Wabi warns about differences from the most up-to-date ABI, which is also used by default cc1plus: note: use e.g. -Wabi11 to warn about changes from GCC 7 在网上找到了一封邮件…

【C++】string类的使用①(默认成员函数 || 迭代器接口begin,end,rbegin和rend)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f308;关于string类&#x1f308;string类的成员函数&#x1f525;默认成员函数string类对象的构造(constructor)string类对象的析构string类对象的赋值运算符…

MySQL数据库核心面试题

数据库中的引擎 常用的引擎有InnoDB、MyIsam、Memory三种。 MyIsam&#xff1a;组织形式分为三种&#xff1a; frm文件存储表结构、MyData文件存储表中的数据、MyIndex文件存储表的索引数据。是分开存储的。 Memory&#xff1a;基于内存的&#xff0c;访问速度快&#xff0…

【C++】 C++ 编写 鸡兔同笼程序

文章目录 “鸡兔同笼”问题是一个经典的数学问题&#xff0c;要求根据总头数和总腿数来计算鸡和兔的数量。假设鸡有 2 条腿&#xff0c;兔有 4 条腿。可以通过以下步骤求解这个问题&#xff1a; 1 .设鸡的数量为 x&#xff0c;兔的数量为 y。2.根据题意&#xff0c;我们有以下…

#私密朋友圈被吐槽有bug?官方致歉!网友:尴尬了......

1月7日&#xff0c;话题#微信私密朋友圈被吐槽有bug#&#xff0c;冲上微博热搜第一。 据了解&#xff0c;近日有网友发现&#xff0c;即使自己发布私密朋友圈&#xff0c;好友也可以在朋友圈页面看到自己的头像红点&#xff0c;随即此话题引发热议。 有网友表示&#xff0c;怪…

STL----resize

resize的作用 设置容器元素个数和初始值。 resize和reserve resize即改变容器元素个数&#xff0c;也改变容器容量。 reserve只改变容器容量&#xff0c;不改变容器元素个数。 reserve有什么用 reserve---存储&#xff0c;容量&#xff0c;保留。 1&#xff0c;设置容器容…

分布式系统的一致性与共识算法(二)

Consitency 背景 如买最后一张车票&#xff0c;两个售票处分别通过某种方式确认过这张票的存在。这时&#xff0c;两家售票处几乎同时分别来了一个乘客要买这张票&#xff0c;从各自"观察"看来&#xff0c;自己一方的乘客都是先到的&#xff0c;这种情况下&#xf…

原子学习笔记7——FrameBuffer 应用编程

Frame 是帧的意思&#xff0c;buffer 是缓冲的意思&#xff0c;所以 Framebuffer 就是帧缓冲&#xff0c;这意味着 Framebuffer 就是一块内存&#xff0c;里面保存着一帧图像。 应用程序通过对 LCD 设备节点/dev/fb0&#xff08;假设 LCD 对应的设备节点是/dev/fb0&#xff09;…

【时隙ALOHA,CSMA(载波侦听多路访问)carrier sense mltiple access,无线局域网: CSMA/CA】

文章目录 时隙ALOHA时隙ALOHA的效率( Efficiency )纯ALOHA(非时隙)----效率低CSMA(载波侦听多路访问)carrier sense mltiple accessCSMA冲突CSMA/CD(冲突检测)边说边听&#xff08;提高了信道利用率&#xff09;以太网就是用的这个无线局域网: CSMA/CA无线局域网中的 MAC&#…