引言
多年开发工作都是在 H5 的世界里遨游。总结了 H5 开发经验总结了一些常用技巧,记录下来,防止自己忘记,如有疑问,欢迎大家留言一起讨论
HTML
meta 标签
- meta 元素可提供有关页面的元信息。下面是一些与移动端 H5 相关比较特殊的 meta 标签
<meta name="screen-orientation" content="portrait" /> // 禁止屏幕旋转
<meta name="full-screen" content="yes" /> // 全屏显示
<meta name="browsermode" content="application" /> // UC应用模式,页面默认全屏
<meta name="x5-orientation" content="portrait" /> // QQ强制竖屏
<meta name="x5-fullscreen" content="true" /> // QQ强制全屏
<meta name="x5-page-mode" content="app" /> // QQ应用模式
<meta name="renderer" content="webkit" /> // 启用360浏览器的极速模式
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> // 优先使用 IE 最新版本和 Chrome
语义化标签
- HTML5 语义化标签能够让页面的结构更清晰,有利于 SEO,并且能够帮助辅助技术更好的阅读和转译你的网页。推荐使用语义化标签编写 HTML
<header></header> // 定义文档的头部区域
<nav></nav> // 定义导航链接的部分
<main></main> // 定义文档的主体部分
<section></section> // 定义文档中的节
<footer></footer> // 定义页面的页脚
<aside></aside> // 定义页面的侧边栏内容
<article></article> // 定义一个文章区域
电话号码拨号与识别
- 使用 a 标签实现点击手机号码唤起拨号页面
<a href="tel:4008001010" class="phone">400-800-1010</a>
在 iOS Safari 浏览器上会将类似为电话号码的数字识别为电话链接,比如:7 位数字,形如:1234567 带括号及加号的数字,形如:(+86)123456789 双连接线的数字,形如:00-00-00111 11 位数字,形如:13800138000
关闭识别
<meta name="format-detection" content="telephone=no" />
邮箱识别
使用 a 标签实现点击邮箱地址弹出邮件发送的功能
<a href="mailto:434387059@qq.com" class="email">434387059@qq.com</a>
Android手机上会对符合邮箱格式的字符串进行识别,我们可以通过如下的 meta 来关闭邮箱的自动识别
<meta name="format-detection" content="email=no" />
CSS
animation动画
animation: name duration timing-function delay iteration-count direction;
| 值 | 描述 |
|---|---|
| animation-name | 规定需要绑定到选择器的 keyframe 名称。 |
| animation-duration | 规定完成动画所花费的时间,以秒或毫秒计。 |
| animation-timing-function | 规定动画的速度曲线。 |
| animation-delay | 规定在动画开始之前的延迟。 |
| animation-iteration-count | 规定动画应该播放的次数。 |
| animation-direction | 规定是否应该轮流反向播放动画。 |
| animation-fill-mode | 规定动画在执行时间之外应用的值。 |
| animation-play-state | 规定动画是正在运行还是暂停。 |
单行文本、多行文本省略
- 单行文本省略
white-space:nowrap;(不换行,强制不换行)注:默认值为normal
overflow:hidden;(溢出部分隐藏)
text-overflow:ellipsis;(溢出部分用...代替)
- 多行文本省略
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box; //使用自适应布局
-webkit-line-clamp: 2; //设置超出行数,要设置超出几行显示省略号就把这里改成几
-webkit-box-orient: vertical;
1px 边框
移动端 H5 项目越来越多,设计师对于 UI 的要求也越来越高,比如 1px 的边框。在高清屏下,移动端的 1px 会很粗。
那么为什么会产生这个问题呢?主要是跟一个东西有关,DPR(devicePixelRatio) 设备像素比,它是默认缩放为 100%的情况下,设备像素和 CSS 像素的比值。目前主流的屏幕 DPR=2(iPhone 8),或者 3(iPhone 8 Plus)。拿 2 倍屏来说,设备的物理像素要实现 1 像素,而 DPR=2,所以 css 像素只能是 0.5。 常用方案
/* 底边框 */
.b-border {
position: relative;
}
.b-border:before {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #d9d9d9;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
/* 上边框 */
.t-border {
position: relative;
}
.t-border:before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
background: #d9d9d9;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
/* 右边框 */
.r-border {
position: relative;
}
.r-border:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
width: 1px;
height: 100%;
background: #d9d9d9;
-webkit-transform: scaleX(0.5);
transform: scaleX(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
/* 左边框 */
.l-border {
position: relative;
}
.l-border:before {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 1px;
height: 100%;
background: #d9d9d9;
-webkit-transform: scaleX(0.5);
transform: scaleX(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
/* 四条边 */
.setBorderAll {
position: relative;
&:after {
content: ' ';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border: 1px solid #e5e5e5;
border-radius: 4px;
}
}
css 中的 1px 并不等于移动设备的 1px,是因为不同的手机有不同的像素密度。在 window 对象中有一个 devicePixelRatio 属性,他可以反应 css 中的像素与设备的像素比
devicePixelRatio 的官方的定义为:设备物理像素和设备独立像素的比例
完美实现 1px 的方法如下,兼容 DPR(设备像素比) 1-4 的设备
@line-color: #ccc;
[retina] {
position: relative;
}
[retina]::before {
position: absolute;
box-sizing: border-box;
width: 100%;
height: 100%;
font-size: 20px;
content: ' ';
pointer-events: none;
}
/* 设备像素比为 1 */
@media only screen and (-webkit-min-device-pixel-ratio: 1), only screen and (min-device-pixel-ratio: 1) {
[retina]::before {
width: 100%;
height: 100%;
transform: scale(1);
transform-origin: 0 0;
}
[retina='line-bottom']::before {
height: 1px;
}
[retina='line-top']::before {
height: 1px;
}
[retina='line-left']::before {
width: 1px;
}
[retina='rect-input']::before {
border-width: 1px;
}
}
/* 设备像素比为 1.5 */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
[retina]::before {
width: 150%;
height: 150%;
font-size: 30px;
transform: scale(0.666667);
transform-origin: 0 0;
}
[retina='line-bottom']::before {
height: 1px;
}
[retina='line-top']::before {
height: 1px;
}
[retina='line-left']::before {
width: 1px;
}
[retina='rect-input']::before {
border-width: 1px;
}
[retina='rect-input']::before {
border-width: 1px;
}
}
/* 设备像素比为 2 */
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
[retina]::before {
width: 200%;
height: 200%;
font-size: 40px;
transform: scale(0.5);
transform-origin: 0 0;
}
[retina='line-bottom']::before {
height: 1px;
}
[retina='line-top']::before {
height: 1px;
}
[retina='line-left']::before {
width: 1px;
}
[retina='rect-input']::before {
border-width: 1px;
}
}
/* 设备像素比为 3 */
@media only screen and (-webkit-min-device-pixel-ratio: 3), only screen and (min-device-pixel-ratio: 3) {
[retina]::before {
width: 300%;
height: 300%;
font-size: 60px;
transform: scale(0.333333);
transform-origin: 0 0;
}
[retina='line-bottom']::before {
height: 2px;
}
[retina='line-top']::before {
height: 2px;
}
[retina='line-left']::before {
width: 2px;
}
[retina='rect-input']::before {
border-width: 2px;
}
}
/* 设备像素比为 4 */
@media only screen and (-webkit-min-device-pixel-ratio: 4), only screen and (min-device-pixel-ratio: 4) {
[retina]::before {
width: 400%;
height: 400%;
font-size: 80px;
transform: scale(0.25);
transform-origin: 0 0;
}
[retina='line-bottom']::before {
height: 2px;
}
[retina='line-top']::before {
height: 2px;
}
[retina='line-left']::before {
width: 2px;
}
[retina='rect-input']::before {
border-width: 2px;
}
}
[retina='line-bottom']::before {
bottom: 0;
left: 0;
background-color: @line-color;
transform-origin: 0 100%;
}
[retina='line-top']::before {
top: 0;
left: 0;
background-color: @line-color;
}
[retina='line-left']::before {
top: 0;
left: 0;
background-color: @line-color;
}
[retina='rect-input']::before {
top: 0;
left: 0;
box-sizing: border-box;
border-color: @input-border-color;
border-style: solid;
border-radius: 1em;
}
屏蔽用户选择
禁止用户选择页面中的文本
.container {
user-select: none;
}
清除输入框内阴影
在 safari 浏览器中,输入框默认有内部阴影,可以这样关闭:
input { -webkit-appearance: none; }
禁止保存或拷贝图像
img {
-webkit-touch-callout: none;
}
iOS 滑动不流畅
ios 手机上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。 iOS 5.0 以及之后的版本,滑动有定义有两个值 auto 和 touch,默认值为 auto
在滚动容器上增加滚动 touch 方法
body{
-webkit-overflow-scrolling: touch;
}
用户设置字号放大或者缩小导致页面布局错误
设置字体禁止缩放
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
}
android系统中元素被点击时产生边框
部分android系统点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样。去除代码如下
a,button,input,textarea{
-webkit-tap-highlight-color: rgba(0,0,0,0)
-webkit-user-modify:read-write-plaintext-only;
}
JS
移动端click屏幕产生200-300 ms的延迟响应
移动设备上的web网页是有300ms延迟的,往往会造成按钮点击延迟甚至是点击失效。解决方案:
- fastclick可以解决在手机上点击事件的300ms延迟
- zepto的touch模块,tap事件也是为了解决在click的延迟问题
触摸事件的响应顺序
- ontouchstart
- ontouchmove
- ontouchend
- onclick
var attachFastClick = require('fastclick');
attachFastClick(document.body);
safari 浏览器日期转换出现 NaN 的问题
将日期字符串的格式符号替换成'/'
'yyyy-MM-dd'.replace(/-/g, '/')
audio 和 video 在 ios 和 andriod 中自动播放
这个不是bug,由于自动播放网页中的音频或视频,会给用户带来一些困扰或者不必要的流量消耗,所以苹果系统和安卓系统通常都会禁止自动播放和使用 JS 的触发播放,必须由用户来触发才可以播放。加入自动触发播放的代码
$('html').one('touchstart', function() {
audio.play()
})
视频自动播放
由于安卓和微信版本不同 自动全屏,不能全屏,不能自动播放,Android,IOS,巴拉巴拉……多到令人发指
播放自动视频的两个前提条件
- 设置自动播放 autoplay
- 设置静音 muted
如果视频是静音播放 并且视频小 就把视频导出每张jpg 然后序列帧播放 本文序列化图片视频方案参考链接:www.zhangxinxu.com/wordpress/2…
##### CSS代码:
.container { width: 256px; height: 464px; margin: auto; background-color: #000; position: relative; } .container > img { position: absolute; width: 100%; height: 100%; } .loading { position: absolute; height: 8px; width: 150px; border: 1px solid #eee; background: linear-gradient(to top, #eee, #eee); background-size: 0 100%; transition: background-size .1s; left: 0; top: 0; right: 0; bottom: 0; margin: auto; } .loading::before { content: attr(data-percent)'%'; position: absolute; left: 0; top: -1.5em; font-size: 12px; color: #eee; }
##### HTML代码:
JS代码:
var urlRoot = './thumbs/';
var indexRange = [1, 47];
var maxLength = indexRange[1] - indexRange[0] + 1;
// loading
var eleContainer = document.getElementById('container');
var eleLoading = document.getElementById('loading');
// 存储预加载的DOM对象和长度信息
var store = {
length: 0
};
// 图片序列预加载
for ( var start = indexRange[0]; start <= indexRange[1]; start++) {
(function (index) {
var img = new Image();
img.onload = function () {
store.length++;
// 存储预加载的图片对象
store[index] = this;
play();
};
img.onerror = function () {
store.length++;
play();
};
img.src = urlRoot + index + '.jpg';
})(start);
}
var play = function () {
// loading进度
var percent = Math.round(100 * store.length / maxLength);
eleLoading.setAttribute('data-percent', percent);
eleLoading.style.backgroundSize = percent + '% 100%';
// 全部加载完毕,无论成功还是失败
if (percent == 100) {
var index = indexRange[0];
eleContainer.innerHTML = '';
// 依次append图片对象
var step = function () {
if (store[index - 1]) {
eleContainer.removeChild(store[index - 1]);
}
eleContainer.appendChild(store[index]);
// 序列增加
index++;
// 如果超过最大限制
if (index <= indexRange[1]) {
setTimeout(step, 42);
} else {
// 本段播放结束回调
// 我这里就放一个重新播放的按钮
eleContainer.insertAdjacentHTML('beforeEnd', '<button onclick="play()">再看一遍英姿</button>');
}
};
// 等100%动画结束后执行播放
setTimeout(step, 100);
}
};
<video // 设置后,音频会初始化为静音,注意浏览器只有设置静音,才能自动播放 muted // 视频会马上自动开始播放,不会停下来等着数据载入结束。 autoplay="autoplay" // 布尔属性;指定后,会在视频结尾的地方,自动返回视频开始的地方 loop="loop" // 一个布尔属性,标志视频将被“inline”播放,即在元素的播放区域内。 x5-playsinline="true" playsinline="true" webkit-playsinline="true" // 一个布尔属性,用于禁用使用有线连接的设备(HDMI、DVI等)的远程播放功能。 x-webkit-airplay="allow" // 这个视频优先加载 preload="auto" // 启用同层H5播放器,就是在视频全屏的时候,div可以呈现在视频层上,也是WeChat安卓版特有的属性。同层播放别名也叫做沉浸式播放 x5-video-player-type="h5" // :全屏设置。它又两个属性值,ture和false,true支持全屏播放 x5-video-player-fullscreen="true"
// 标签为媒介元素(比如 和 )定义媒介资源。
以上代码pc端能自动播放,如果需要自动播放视频 需要添加如下代码
function doPlay(){ WeixinJSBridge.invoke('getNetworkType', {}, function (e) { var ("#video1") var ("#video2") video2[0].play() }) }
if (window.WeixinJSBridge) { doPlay() } else { document.addEventListener("WeixinJSBridgeReady", function(){ doPlay() }, false); }
需要注意的是,监听WeixinJSBridgeReady事件后,回调函数里需要调用一下invoke,在invoke中操作视频才可以生效。另外,页面不需要引入jweixin-1.0.0.js,微信浏览器会自带api。但是如此 有的安卓机还是不能自动播放 需要使用[JSMpeg](https://github.com/phoboslab/jsmpeg),记得mp4格式要转换成ts格式
1. 区分开iOS和安卓,iOS用原生video标签,安卓用JSMpeg,绘制画面到canvas上。
1. iOS用mp4或者其他原生video标签支持的视频格式,安卓用ts格式。(其中ts格式需要用ffmpeg来转一下,下文会提及)
1. 监听WeixinJSBridgeReady事件,调用视频播放方法,实现自动播放。
var isAndroid = window.navigator.userAgent.match(/android/ig) if (isAndroid) { // 安卓 var src = "static.shikehuyu.com/vincent/wx-…" player = new JSMpeg.Player(src, { canvas: canvas, autoplay: true, progressive: false, loop: true, onVideoDecode: function() { canvas.style.display = 'block' canvas.style.height = 80 / (canvas.width / canvas.height) + "vw" } }) }else{ // ios 自动播放 }
以上视频自动播放总结:iOS用video、Android用JSMpeg,然后依靠监听WeixinJSBridgeReady事件,实现微信H5视频行内自动播放,
## H5常用的插件
### dom生成图片
**[dom-to-image](https://github.com/tsayen/dom-to-image)**
**[html2canvas](http://html2canvas.hertzen.com/)**
常规案例: 生成海报、长按保存图
如果生成图片是另一种的话 用img标签 z-index层级设置最上面 opacity设置0 H5用户可长按页面调起actionSheet识别或保存图片
如果图片是网络图片的话 需要转成base64
// 确保引入html2canvas 下面代码是jquery function imageUrlToBase64(url) { return fetch(url) .then(response => response.blob()) .then(blob => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); }); } const imageUrl = '线上网址图片' imageUrlToBase64(imageUrl).then(base64 => { // console.log(base64); // 在此处可以使用转换后的 Base64 数据 newdetail_image=base64 newhtml+= '
'+ ''+ 'setTimeout(()=>{
html2canvas(document.getElementById("poster-content"), {
scale: 1,
width: 750,
height: 1413,
useCORS: true,
allowTaint: true,
backgroundColor: null,
logging: false
}).then(function (canvas1) {
// img.setAttribute("crossOrigin",'Anonymous')
// console.log(canvas1,'canvas1')
var dataURL = canvas1.toDataURL("image/jpeg", 1)
// console.log(dataURL,'dataURL')
$("#userpic").attr("src",dataURL);
});
},1000)
})
.catch(error => {
console.error('转换出错:', error);
});
### 视差引擎
- **[Parallax.js](https://github.com/wagerfield/parallax)**是一个**简单的,轻量级的**视差引擎。你可以将它作为作为`jQuery`或`Zepto`插件来使用,也可以以**纯JS的方式**来使用。最-最-最厉害的是它可以对智能设备的**方向作出反应**,即使在没有陀螺仪或运动检测硬件可用的时候,也可使用光标的位置来代替。
- Vue.js组件用于视差图像滚动效果
**[vue-parallax](https://github.com/apertureless/vue-parallax)**
### 移动端全屏播放视频[JSMpeg](<https://github.com/phoboslab/jsmpeg>)
移动端全屏播放视频主要规避了原生video标签存在的如下兼容问题:1.原生UI不一致2.国产浏览器对播放器劫持(点名:QQ浏览器、百度浏览器、UC浏览器......)3.内联播放且视频层级无法覆盖
- 一个用 JavaScript 编写的视频播放器
- 由 MPEG-TS 解复用器、MPEG1 视频和 MP2 音频解码器、WebGL 和 Canvas2D 渲染器和 WebAudio 声音输出组成
- 可以通过 Ajax 加载静态视频,并允许通过 WebSockets 进行低延迟流式传输(约 50 毫秒)
- 可以在 iPhone 5S 上以 30fps 的速度解码 720p 视频
- 可以在任何现代浏览器(Chrome、Firefox、Safari、Edge)中使用,并且压缩后仅 20kb
### [canvas-confetti](https://www.kirilv.com/canvas-confetti/) 是一个使用 canvas 的五彩纸屑特效 JS 插件