H5嵌入原生开发小结----兼容安卓与ios的填坑之路

news2024/11/22 11:03:37

一开始听说开发H5,以为就是做适配现代浏览器的移动网页,心想不用管IE了,欧也。到今天,发现当初too young too simple,兼容IE和兼容安卓与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中有用,如果是在浏览器中还是会失效。

2.键盘收下留下空白阴影。

 这个在部分安卓机上比较明显,当键盘在激活状态,突然来一个模态弹框,很明显的看到键盘收下去之后出现了一个短暂(不到1秒的样子)的空白,然后页面再收下去,让人感觉闪了下。比如华为P7。但是ios上没有关系,这个问题没招,系统不流畅。

3.无法保持键盘在弹出状态。

web其实无法直接控制键盘,只有通过让输入框获focus来让手机的键盘弹出来,但是三星SM-N8507v 这款就是不听话。我也无解了。

4.确保弹出来的是数字键盘

<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都会设置光标跑在最前面,无法移动到后面。这个体验很糟糕。

2.模拟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的值直接在写输入框中这样会多一些判断,让代码不太干净。

3.div的换行是div

在div中触发换行,会得到一个div。这个就相当于是在编辑器中。而不是我们认为的<br>或者换行符。

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

     var replace = /<div.*?>(.*?)<\/div>/g;
        txt = txt.replace(replace, function(a) {
         if (a != "<br>") {
                return "<br>" + delHtmlTag(a);
            }
       return delHtmlTag(a);}
       

4.粘贴莫名奇妙带了样式

在输入框中粘贴会以这样的形式出现:

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

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

5.超出遮挡/换行遮挡

这是一个神奇的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;
   }

当然这不是正规的解决办法,如果园友有遇到相似的剧情,可以赐教一下。

6.安卓第一次不能换行

这个现象是消息发送成功之后,用户(小米)一来就是点换行,却无法换行,我怀疑是安卓系统阻止了这样的行为。但是在输入一个字之后,换行就是正常的了(哪怕再删掉这个字)。ios里面没有这个问题。开始我尝试去人为加一个换行,又发现焦点没了。想想这样问题不改也罢。

7.输入框光标闪动

这个是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。测试妹子会说这个手机可以,那个不可以,然后是苦了前端。

2.图片转了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);

        }

3.图片半截

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

 

所以图片的宽度会超出其父类,即使<div><img></div>的宽度都是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);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何借助AI,产品文案语音配图片一键生成视频

const name "AI生成视频";console.log(name); 以前我们做视频都是要找素材、剪辑、配音&#xff0c;太费时间了&#x1f629;&#xff0c;现在只需要通过AI&#xff0c;输入文字&#xff0c;它就能自动帮我们生成一段有声有色的视频。 我们来看看文本生成的视频效…

案例35:基于Springboot图书商城管理系统开题报告设计

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

面向流媒体的确定时延传输:从QUIC出发,走向未来

QUIC&#xff08;Quick UDP Internet Connections&#xff09;是Google设计的一套可靠UDP传输协议&#xff0c;旨在为HTTP提供一个安全、可靠、高效和低延时的通信基础。QUIC协议已被IETF采纳为标准&#xff0c;并且HTTP/3已选择使用QUIC来代替TCP作为其传输层协议。LiveVideoS…

python怎么退出执行/退出程序语句

python怎么退出执行/退出程序语句 文章目录 python怎么退出执行/退出程序语句sys.exit()函数 raise SystemExit()异常os._exit()函数CtrlC中断程序执行具体情况具体处理参考资料 在Python中&#xff0c;退出执行是一个常见的操作。退出方法介绍&#xff1a; sys.exit()函数 sy…

最近几年,国内好多家实体企业都开始用上低代码了,它有什么好?

前言&#xff1a; 裹挟大数据、云计算、人工智能等数字技术的第四次工业革命浪潮正加速来袭&#xff0c;全球经济已行至历史的十字路口。 站上技术浪潮潮头者澎湃生长&#xff0c;错过技术浪潮者黯然败退。那么&#xff0c;对于中国的普通制造企业来说&#xff0c;如何抓住向…

【媒体广告】的现状与未来发展趋势!

媒体广告是一种重要的市场推广手段&#xff0c;随着技术的不断发展和市场环境的变化&#xff0c;媒体广告也在不断地演变和发展。本文将从以下几个方面探讨媒体广告的发展趋势。 一、数字化、数据化和智能化趋势 随着互联网和移动互联网技术的发展&#xff0c;数字化、数据化…

2.1 初探MyBatis实现简单查询

一、创建数据库与表 1、创建数据库 在Navicat里创建MySQL数据库 - testdb&#xff0c;采用utf8mb4字符集 2、创建用户表 用户表 - t_user CREATE TABLE t_user (id int(11) NOT NULL AUTO_INCREMENT,name varchar(50) DEFAULT NULL,age int(11) DEFAULT NULL,address var…

LLM-Pruner: 剪枝+少量数据+少量训练 = 高效的LLM压缩

概要 大语言模型&#xff08;LLMs, Large Language Models&#xff09;在各种任务上展现出了惊人的能力&#xff0c;这些能力很大程度上来自于模型庞大的模型规模以及海量的训练语料。为了应对这些模型部署上存在的挑战&#xff0c;许多研究者开始关注大语言模型的轻量化问题。…

华为认证 | HCIE-存储 V3.0 即将发布!

华为认证HCIE-Storage V3.0&#xff08;中文版&#xff09;预计将于2023年6月30日正式对外发布。为了帮助您做好学习、培训和考试计划&#xff0c;现进行预发布通知&#xff0c;请您关注。 01 发布概述 基于“平台生态”战略&#xff0c;围绕“云-管-端”协同的新ICT技术架构&…

5周年更新 | OpenVINO™  2023.0,让AI部署和加速更容易

时光匆匆&#xff0c;岁月荏苒&#xff0c;OpenVINO™迎来了5岁生日。5岁&#xff0c;对于OpenVINO™来说还是个很年轻的年纪&#xff0c;一如正在茁壮成长的少年&#xff0c;每天都迸发着无穷的生命力。 在这5年里&#xff0c;OpenVINO™密切关注市场需求&#xff0c;着眼未来…

JavaScript拖动元素在一个范围内移动

基于 jQuery移动范围由 div 搭建(div 模仿表格)&#xff0c;卡片的移动不允许超出该范围移动卡片会有一个淡蓝色卡片的标记出将要放置的位置有禁止放置标记的位置&#xff0c;不允许卡片放置&#xff08;会放到前一个可放置的位置&#xff09;卡片放置会覆盖单元格中的文字卡片…

TSS半导体放电管八大属性总结

​之前在写关于GDT放电管与TSS放电管之间的差异时&#xff0c;其实有谈到TSS&#xff08;固体放电管&#xff09;它拥有的一些特性&#xff0c;今天优恩小编还是想重复一下&#xff0c;希望更多小伙伴能够记住。 TSS&#xff0c;有人叫它固体放电管、也有人叫它半导体放电管&am…

智能交通车路协同系统的应用场景和发展趋势

随着城市化进程的加速和汽车保有量的增加&#xff0c;城市交通拥堵、交通事故等问题日益突出。为了解决这些问题&#xff0c;智能交通车路协同系统应运而生。智能交通车路协同系统是一种基于车载终端、路侧设备和交通管理中心等多个组成部分构成的智能交通系统&#xff0c;可以…

io之netty

写在前面 netty当前是网络io框架的事实标准&#xff0c;基于nio实现&#xff0c;框架的作者是韩国一位姓李的朋友&#xff0c;开始我们这位行李的韩国朋友开发一个io框架mina&#xff0c;但后来其离职&#xff0c;mina也就和其没有关系了&#xff0c;所以后来其改进了mina的不…

Maxcompute数据上云一致性比对

我写过很多如何去对数、如何批量对数的技术文档&#xff0c;最近项目遇到这个问题&#xff0c;我才发现在官方博客上还没有发布过这个课题的文章。这就像灯下黑&#xff0c;太长用到的知识点&#xff0c;反而没有意识到其重要性。 注&#xff1a;这里对数的场景就是指在阿里云…

docker 装机/卸载 Mysql

1、首先&#xff0c;需要安装Docker。可以使用以下命令安装&#xff1a; > yum install docker 2、安装完成后&#xff0c;启动Docker服务&#xff1a; > systemctl start docker3、CentOS7环境下的Docker使用 docker快速部署mysql数据库并初始化 docker快速部署mysq…

Power BI API调用注意事项 (By Power Automate)

注&#xff1a;本文最初发布于https://d-bi.gitee.io和medium, 2023年6月迁移至CSDN 前述 本站关于实现Power BI REST API的博文已有许多&#xff0c;包括&#xff1a; Power BI REST API有多强大&#xff1f;PBI开发者必读Power BI REST API实战教程&#xff1a;PowerQuery为…

基于SSM的便利店系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 基于SSH的便利店系统是…

Java中方法的重载与重写

文章目录 前言方法重载方法重写 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 方法的重载与重写容易混&#xff0c;所以单独拿出来比较 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 方法重载 在同一个类中&#xff0c;允…

springcloud-alibaba (06)RocketMQ控制台安装与启动

RocketMQ控制台 ✨让你的消息传输更高效✨ 如果你是一名开发者&#xff0c;或者是对消息传输有需求的企业用户&#xff0c;那么你肯定不陌生于 RocketMQ&#xff0c;它是一个高可用、高可靠、高性能、分布式消息中间件。但是有时候&#xff0c;在 Windows 上安装和启动 Rocke…