一开始听说开发H5,以为就是做适配现代浏览器的移动网页,心想不用管IE了,欧也。到今天,发现当初too young too simple,兼容IE和兼容安卓与IOS,后者让你更抓狂。接下来数一下踩过的坑。主要分UI展示,键盘,输入框等等。解决bug最苦恼的问题不是没有解决方案,而是你没有找到真正的原因。再就是现象难以重现,每次都要发布代码,然后到手机app中去测试,模拟。这些地方会耗费大量的精力。
fixed布局的作用之一就是在手机键盘弹起来的时候,可以自动把页面顶起来。如果不支持的话,换绝对定位也是可以的。但是绝对定位某些机型比如sm-n7508,华为m7上还是没有能顶起来。IOS没有这个问题。
部分安卓机器(比如小米)的分辨率低,如果border的宽度小于1px。安卓机出现一种边框消失了的现象。样式上有点奇怪,IOS没有这个问题。
一开始以为是别的元素挡住了,但是调了半天无解。最后突然意识到是不是计算出来的高度有小数导致的。然后马上取整:
$target.css("height", Math.ceil(maxline * lineHeight));
但是,华为的某些类型的是上面显示不正常,出来一排点点。
再修正一下:
$target.css("height", Math.ceil(maxline * lineHeight) - 1);
或者用floor。你好奇为什么会有小数高度呢,因为这个功能设计一个折叠,需要重新计算dom的高度。
这个发生的频率很高,中文输入法或者输入法切换的时候会遮挡。
解决的办法如下:
setInterval(function () { if (document.activeElement.className.indexOf('inputcontent') >= 0) { document.activeElement.scrollIntoViewIfNeeded(); } }, 300);
这是最管用的办法,inputcontent为输入框的样式。activeElement表示获得焦点的元素。但是这个方法只在app中有用,如果是在浏览器中还是会失效。
这个在部分安卓机上比较明显,当键盘在激活状态,突然来一个模态弹框,很明显的看到键盘收下去之后出现了一个短暂(不到1秒的样子)的空白,然后页面再收下去,让人感觉闪了下。比如华为P7。但是ios上没有关系,这个问题没招,系统不流畅。
web其实无法直接控制键盘,只有通过让输入框获focus来让手机的键盘弹出来,但是三星SM-N8507v 这款就是不听话。我也无解了。
只有number或者tel还是不够,只有加入正则,ios才会出现九宫格。
在ios中,会出现几秒的输入框没有反应,开始也怎么想不明白,各种尝试,推测,搜索发现原来是使用的轻框架中用到了fastClik引起的,解决的办法就是加上一个样式。
解决方法到是简单,但是当初为找到这个原因,费了好大劲,把框架的模块一个一个删,最后才定位到。如果不加这个,不单会锁住输入框,每次调用focus都会设置光标跑在最前面,无法移动到后面。这个体验很糟糕。
div作为输入框,本身加入placeholder是无效的。得借助于伪元素。
.tools .inputcontent:after { display: inline-block; width: 100%; color: #999; content: attr(placeholder); }
然后在获得焦点和失去焦点的时候对这个属性进行移除和添加操作。这样做的好处在于,分离用户输入的内容,如果把placeholder的值直接在写输入框中这样会多一些判断,让代码不太干净。
在div中触发换行,会得到一个div。这个就相当于是在编辑器中。而不是我们认为的
或者换行符。
所以,需要对div进行过滤替换。
var replace = /(.*?)<\/div>/g; txt = txt.replace(replace, function(a) { if (a != "
") { return "
" + delHtmlTag(a); } return delHtmlTag(a);}
在输入框中粘贴会以这样的形式出现:
粘贴内容
不清楚是系统还是app定义的。所以也得过滤。 用户还可以粘贴其他文章,容易搞乱输入框中的样式,需要加上:
.tools .inputcontent *{font-size: 0.50rem !important; color: #000 !important;line-height: 22px !important;font-weight: normal !important;}
因为如果从粘贴事件里面处理的话,容易搞乱焦点,让焦点在最前面。这样很不爽。所以还是样式控制,在发送的时候过滤掉所有的标签。
这是一个神奇的bug,当内容超过div的最大高度后,最后一行出现一个神奇的现象,头两个字显示了,后面的内容不见了(快快后面其实有内容),直到下一次换行才会出现。
我alert里面的内容,发现并没有其他的元素,我不断的尝试,发现div overflow: hidden;的时候字都会显示出来,但是为了让用户能够在内容框里面上下滚动。我得让y轴是可以滚动的:overflow-y: auto; 所以应该是滚动条引起的。但我确实不知道如何修改。后来试出一个hack的方法。只要有一个换行就不会出现这种情况。所以,我就在用户输入到特定行的时候就塞进一个1px的换行:
if ($("#content").height() == 88 && isIOS() && !haveAppendBr) { $("#content").append(""); haveAppendBr = true; }
当然这不是正规的解决办法,如果园友有遇到相似的剧情,可以赐教一下。
这个现象是消息发送成功之后,用户(小米)一来就是点换行,却无法换行,我怀疑是安卓系统阻止了这样的行为。但是在输入一个字之后,换行就是正常的了(哪怕再删掉这个字)。ios里面没有这个问题。开始我尝试去人为加一个换行,又发现焦点没了。想想这样问题不改也罢。
这个是translate3d属性引起,修改sm框架中的一段代码设置useTranslate为false。位于handleTouchMove方法中。
安卓很多时候都不能触发input type='file'的弹框,上传就得走原生的帮助。原生拦截到链接后就会弹出图片选择框。
window.location = 'xxapp:h5Upload({"openType":2,"needChop":false,"uploadUrl": '+fileUploadUrl+',"key":' + totalFiles + ',"callbackMethodName": "uploadComplete"})';
key是为了记住是第几张图片,便于在原生上传结束之后把地址和key一起传到uploadComplete方法中。这样界面上才可以做相应的修改。但这不是我说的重点。重点是Url不要带中文啊或者特殊符号之类的。一方面很多手机里面的图片命名很奇怪,一大堆,像在uc上面保存下来的图片后面跟了几个类型。这种名称没啥用,非常建议服务器端像微信一样处理上传图片,成功之后得到一个唯一的字符串就行。不然有的系统会自动解码你得区分的用上了encodeURI或者encodeURIComponent。测试妹子会说这个手机可以,那个不可以,然后是苦了前端。
安卓部分机型(小米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); }
这个问题在安卓上面会有,不是加载的问题。正确的效果图片应该是垂直居中的。但不知道为什么偶尔的会只出来个半截。而且我发现,给图片设置百分比,手机和pc不一样,手机图片的百分比并不是相对于其父类元素,而是它自己。
所以图片的宽度会超出其父类,即使
的宽度都是100%。overflow:hidden吧,图片可能显示不全。超出的部分会导致用户可以在图片上面左右滑动,这在ios中有个搞笑的现象,就是对弹出的图片不断的左右滑动,再恢复后居然能让原先绑定的点击事件失效,不确定是框架的原因还是系统的原因。当时是用一个模态框改造的。最后干脆自己再写一个:Client.modalImg = function (src) { if (!src) return; if ($(".img-overlay").length) { $(".img-overlay").remove(); }; var modal = '