H5 调用本地摄像头
getUserMediaAPI 方案
getUserMediaAPI 是 H5 提供的新的 API;
该 API 最早是直接挂在 navigator 对象上面的,但是目前做了调整,navigator.getUserMediaAPI 已经被废弃,取而代之的是 navigator.mediaDevices.getUserMedia;
-
MDN 上对此 API 的说明如下:
- MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型;
- 它返回一个 Promise 对象,成功后会 resolve 回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promise 会 reject 回调一个 PermissionDeniedError 或者 NotFoundError;
-
接下来看一个具体的示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>getUserMediaAPI 示例</title> <style> * { margin: 0; padding: 0; } button { width: 100px; height: 30px; font-size: 18px; margin: 20px; } </style> </head> <body> <button id="btn1">开启摄像头</button> <button id="btn2">关闭摄像头</button> <video id="video" controls width="100%" height="300px"></video> <script> // 获取 DOM 元素 var btn1 = document.getElementById("btn1"); var btn2 = document.getElementById("btn2"); var video = document.getElementById("video"); var constraints = { audio: true, video: true, }; // 开启摄像头 btn1.onclick = function () { navigator.mediaDevices .getUserMedia(constraints) .then(function (stream) { video.srcObject = stream; video.play(); // 关闭摄像头 btn2.onclick = function () { stream.getTracks().forEach(function (track) { track.stop(); }); }; }) .catch(function (err) { console.log("getUserMedia() error: " + err); }); }; </script> </body> </html>
-
上面的示例中,虽然在开发环境时,各种访问媒体设备都没有问题,但是当部署到服务器上,手机和电脑浏览器都无法调起摄像头,这是由于浏览器的安全策略导致的;有下面三种情况是可以调起设备的,也就是 navigator.mediaDevices 不为 undefined:
- 地址为 localhost://
- 地址为 https://
- 为文件访问 file:///
-
getUserMediaAPI 的兼容性:
input:file 方案
-
另一种主流的方案,就是通过传统的 input:file 来调用本地摄像头;
-
下面是一个示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>input-file示例</title> <style> html { font-size: 36px; } input { display: none; } label { display: block; } span { display: block; text-align: center; height: 50px; font-size: 18px; margin: 50px; border: 1px solid #ccc; padding: 10px; line-height: 50px; } </style> </head> <body> <label> <span>打开相册,可以选择一张图片</span> <input type="file" accept="image/*" /> </label> <label> <span>打开相册,可以选择多张图片</span> <input type="file" multiple accept="image/*" /> </label> <label> <span>打开照相机</span> <input type="file" accept="image/*" capture="camera" /> </label> <label> <span>打开录像机</span> <input type="file" accept="video/*" capture="camcorder" /> </label> <label> <span>打开录音机</span> <input type="file" accept="audio/*" capture="microphone" /> </label> </body> </html>
案例实战:选择头像
这里来看一个用户拍照或者从相册选择头像并且裁剪头像后上传的案例;
上拉加载和下拉刷新
在移动端应用中,经常会涉及到一个上拉加载和下拉刷新的需求:
- 所谓上拉加载,其实就是以前在 PC 端的分页显示,主要因为数据太多,无法一次性加载所有的数据,因此以前在 PC 端采用的是分页显示的方式;然而到了移动端,一般不会再见到分页,取而代之的是上拉加载下一页数据;
- 所谓下拉刷新,则指代的是下拉之后获取更新的数据:这种需求常见于信息展示类网页,诸如新闻、资讯一类的网页,因为新闻和资讯每隔一段时间会有内容更新,因此用户可以下拉来获取最新的内容;
本次的上拉加载和下拉刷新也会在 Swiper.js 的基础上进行展开;
在本地环境配置 https 证书
在使用 HTML5 的 API 时,很多 API 只能在 https 保证安全的情况下才能开启;
这就要求在本地开发环境也能够配置 https,否则需要每次部署到配有 https 的测试环境中才能看到预览效果,这对开发的敏捷度造成了极大的干扰;
使用 mkcert
mkcert 是一个用 GO 写的零配置专门用来本地环境 https 证书生成的工具;
macOS、Linux、Windows 均可安装;
-
要安装 mkcert,官方推荐的命令很简单,使用 Homebrew 来进行安装:
brew install mkcert
-
需要注意的是,目前由于国内的特殊环境,可以使用国内镜像安装:
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
-
安装完 Homebrew 之后,就可以执行刚才所提到的 brew install mkcert 来安装 mkcert;
-
安装完成后,执行
mkcert -install
-
该命令会在您的本机安装一个证书中心,用于颁发证书,示例如下:
-
通过 mkcert --CAROOT 可以查看证书中心的存放位置,如下图所示:
-
至此,证书中心就搞定了,之后可以在具体的项目中生成证书文件和私钥;
在具体项目中生成证书文件和私钥
-
通过上面的步骤,在本机上安装了 CA(刚才的证书中心),接下来 cd 到需要生成凭证的目录下,通过 mkcert 命令来生成证书文件和私钥,示例如下:
-
在上面的操作中,针对 localhost、127.0.0.1、192.168.101.26 这 3 个地址生成了证书文件和私钥,其中 ./localhost+2.pem 就是证书文件,./localhost+2-key.pem 就是私钥文件,并且这两个文件都有过期时间;
-
也可以打开生成证书的目录,可以看到在该目录下已经存在了这 2 个文件:
在 live server 中配置 https 环境
-
因为项目很多时候是以 live server 的形式打开,因此可以在 live server 中配置以 https 打开项目:
-
在扩展中找到 live server 对应的配置项:
-
将 enable 修改为 true,然后填写 cert 和 key 的地址即可,填写完成后最好重启一下 vscode:
-
配置完成后,之后使用 live server 重新打开项目时,就会以 https 的形式来打开项目:
其他环境中要配置 https,可以参阅:https://www.codingsky.com/m/doc/2021/8/24/116.html
重力加速度
html5 提供了几个新的 DOM 事件来获得设备物理(主要针对移动端)方向及运动的信息,包括:陀螺仪、罗盘及加速器;
- DeviceMotionEvent 简介;
- 摇一摇改变背景色;
DeviceMotionEvent 简介
ios13+ 限制了该事件
官方文档地址:https://w3c.github.io/deviceorientation/
-
我们所使用的移动设备,和 PC 有一个区别在于会内置陀螺仪、罗盘及加速器等硬件,而作为开发者则可以从各个内置传感器那里获取未经修改的传感数据,并观测或响应设备各种运动和角度变化,这些传感器包括陀螺仪、加速器和磁力仪(罗盘);
-
加速器和陀螺仪的数据都是描述沿着设备三个方向轴上的位置,对于一个竖屏摆放的设备来说,X 方向从设备的左边(负)到右边(正),Y 方向则是由设备的底部(-)到顶部(+),而 Z 方向为垂直于屏幕由设备的背面(-)到正面(+);
-
DeviceMotionEvent 会在设备发生有意义的摆动(或运动)时产生,事件对象封装有产生的间距值,旋转率,和设备加速度;
-
如果要使用 DeviceMotionEvent,开发者可以监听 devicemotion 事件从而能够监听手机加速度的变化,该事件的事件对象提供了 4 个只读属性:
- accelerationIncludingGravity:重力加速度(包括重心引力9.8)
- acceleration:加速度(需要设备陀螺仪支持)
- rotationRate(alpha,beta,gamma):旋转速度
- interval:获取的时间间隔
-
其中关于 rotationRate 的 3 个属性值 alpha、beta、gamma 说明如下:
- alpha 以设备坐标系 z 轴为轴的旋转速率,如下图所示:
- beta 以设备坐标系 x 轴为轴的旋转速率,如下图所示:
- gamma 以设备坐标系 y 轴为轴的旋转速率,如下图所示:
- alpha 以设备坐标系 z 轴为轴的旋转速率,如下图所示:
-
DeviceMotionEvent 基本使用示例:
<style> .box { width: 200px; height: 200px; background: green; color: #fff; } </style> <div class="box"></div> <script> /* devicemotion:重力加速度事件 acceleration:设备在X,Y,Z三个轴的方向上移动的距离, 不包含重力加速度 accelerationIncludingGravity:设备在X,Y,Z三个轴方向移动的距离, 包含重力加速度(重力加速度通常取值为9.8m/s的二次方) - x轴加速度以手机屏幕左右两侧为方向移动,往右为正,往左为负 - y轴加速度以手机屏幕前后两侧为方向移动,往前为正,往后为负 - z轴加速度以手机屏幕上下两侧为方向移动,往上为正,往下为负 rotationRate:设备在Alpha, Beta, Gamma三个方向旋转的速率(度/秒) - alpha 以设备坐标系z轴为轴的旋转速率 - beta 以设备坐标系x轴为轴的旋转速率 - gamma 以设备坐标系y轴为轴的旋转速率 interval:从设备获取数据的频率, 单位是毫秒 注意:ios与android里取到的属性值不一致(正负相反),因为它们各自处理坐标的方式不同 */ const box = document.querySelector(".box"); window.addEventListener("devicemotion", (ev) => { let motion = ev.acceleration; box.innerHTML = ` x:${motion.x}<br> y:${motion.y}<br> z:${motion.z}<br>`; }); </script>
摇一摇改变背景色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
手机摇一摇,改变手机屏幕颜色。
<p>x 方向</p>
<p id="x"></p>
<p>y 方向</p>
<p id="y"></p>
<p>z 方向</p>
<p id="z"></p>
<script>
/**window.DeviceMotionEvent:判斷浏览器是否支持此时间*/
if (window.DeviceMotionEvent) {
/**
* speed:速度,根据摇一摇的动作幅度可以适当增加或减小
* cx、cy、cz:分别是当前在 x,y,z 3个方向上的加速度
* lastX、lastY、lastZ:分别是上一次在 x,y,z 3个方向上的加速度
*/
var speed = 20;
var cx = 0;
var cy = 0;
var cz = 0;
var lastX = 0;
var lastY = 0;
var lastZ = 0;
/**注册devicemotion(设备运动)事件
* Window.prototype.addEventListener = function(type,listener,useCapture)
* type:事件类型,如 devicemotion、deviceorientation、compassneedscalibration 等
* listener:事件触发的回调函数,也可以提取出来单独写
* useCapture:是否捕获
* */
window.addEventListener("devicemotion", function (evenData) {
/**获取重力加速度
* x、y、z 三个属性,分别表示 3 个方向上的重力加速度
* */
var acceleration = evenData.accelerationIncludingGravity;
cx = acceleration.x.toFixed(2);
cy = acceleration.y.toFixed(2);
cz = acceleration.z.toFixed(2);
/**只要手机有稍微的抖动,就会进入此回调函数
* 当某一个方向上的加速度超过 speed 的值时,改变背景色
*/
if (
Math.abs(cx - lastX) > speed ||
Math.abs(cy - lastY) > speed ||
Math.abs(cz - lastZ) > speed
) {
// 设置随机的颜色
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
document.body.style.backgroundColor = `rgb(${r},${g},${b})`;
/** 将数据打印出来瞧一瞧*/
document.querySelector("#x").innerHTML = "cx:" + cx + "\r\n";
document.querySelector("#y").innerHTML = "cy:" + cy + "\r\n";
document.querySelector("#z").innerHTML = "cz:" + cz + "\r\n";
}
lastX = cx;
lastY = cy;
lastZ = cz;
},
true
);
} else {
alert("您的浏览器不支持摇一摇功能.");
}
</script>
</body>
</html>
横竖屏切换与方向变化事件
横竖屏切换
-
横竖屏切换也是移动端应用中一个非常常见的需求,在早期的时候,可以监听 orientationchange 事件来判断用户是横屏还是竖屏,但是目前这个 API 已经被完全废弃;
-
取而代之的是 ScreenOrientationAPI,ScreenOrientationAPI 为 Web 应用提供了读取设备当前屏幕方向、旋转角度、锁定旋转方向、获取方向改变事件的能力,使得特定应用在屏幕方向方面增强用户体验,如视频和游戏;
-
可以简单的测试浏览器是否支持:
if(window.ScreenOrientation){ alert("OK"); } else { alert("No"); }
读取屏幕方向
读取屏幕方向主要通过 type 和 angle 两个属性,前者返回旋转方向的描述,后者返回旋转的角度;
-
angle,angle 属性代表了以设备的自然位置开始,逆时针方向上所旋转的角度,如将手机逆时针旋转 90 度变为横屏,那么此时 angle 则返回 90;
-
type,type 属性主要通过描述来表达屏幕的旋转方向,type 的返回值总共有四个,对应着四个不同的旋转方向:
- portrait-primary:竖屏状态并且旋转角度为 0 度,也就是设备的自然位置
- portrait-secondary:竖屏状并且即旋转角度为 180 度,也就是倒着拿的位置
- landscape-primary:横屏状态并且旋转角度为 90 度
- landscape-secondary:横屏状态并且旋转角度为 270 度
锁定屏幕方向
出于一些安全方面的考虑,锁定方向时必须使页面处于全屏状态
-
锁定:锁定屏幕通过 lock 方法,调用 lock 方法需要传入锁定的方向描述字符串,随后该方法会返回一个 Promise;
-
解锁:解锁不需要额外参数,只需要调用 unlock 即可;
function unlock() { screen.orientation.unlock(); }
屏幕方向改变事件
通过为 onchange 赋值或通过 addEventListener 都可以添加事件监听:
function rotationChange() {
console.log('rotation changed to:', screen.orientation.type);
}
screen.orientation.addEventListener('change', rotationChange);
简单示例
<div>
<p>类型: <span id="type"></span></p>
<p>角度: <span id="angle"></span></p>
</div>
<div>
<select id="mode">
<option value="landscape">lanscape</option>
<option value="portrait">portrait</option>
</select>
<button id="lock">lock</button>
<button id="unlock">unlock</button>
</div>
<script>
const btn_lock = document.getElementById("lock");
const btn_unlock = document.getElementById("unlock");
const sel_mode = document.getElementById("mode");
const txt_type = document.getElementById("type");
const txt_angle = document.getElementById("angle");
btn_lock.addEventListener("click", function () {
document.documentElement.requestFullscreen().then(() => {
screen.orientation.lock(sel_mode.value).catch(function (e) {
alert(e.message);
});
});
});
btn_unlock.addEventListener("click", function () {
screen.orientation.unlock();
});
function rotateStatUpdate() {
txt_type.innerText = screen.orientation.type;
txt_angle.innerText = screen.orientation.angle;
}
screen.orientation.addEventListener("change", rotateStatUpdate);
window.addEventListener("load", rotateStatUpdate);
</script>
方向变化事件
-
方向变化事件:
<style> .box { width: 200px; height: 200px; background: green; color: #fff; } </style> <div class="box"></div> <script> /* deviceorientation 方向变化事件 alpha:设备围绕z轴方向旋转的度数,范围:0~360(顶部指向地球的北极,alpha此时为0) beta:设备围绕x轴方向旋转的度数,由前向后,范围:-180~180 gamma:设备围绕y轴方向旋转的度数,由左向右,范围:-90~90 */ const box = document.querySelector(".box"); window.addEventListener("deviceorientation", (ev) => { box.innerHTML = ` z轴偏移的度数为:${ev.alpha.toFixed(2)}<br> x轴偏移的度数为:${ev.beta.toFixed(2)}<br> y轴偏移的度数为:${ev.gamma.toFixed(2)}<br> `; }); </script>
-
该事件的兼容性不是太好,作为了解即可,下图是 caniuse 上的兼容性情况:
更多内容
拨打电话和发送短信
-
首先补充一个拨打电话和发送短信,看起来可能很复杂的需求,但是实际上使用 a 标签就能够实现,示例如下:
<a href="tel:13333333333">点击给我打电话</a> <a href="sms:10086?body=短信内容">发送短信</a>
-
如果要拨打电话,则 a 标签的 href 属性值为:
tel:电话号码
-
如果要发送短信,其中 body 部分可选,如果填写则会成为短信编辑栏内容的一部分,则 a 标签的 href 属性值为:
sms:电话号码?body=短信内容
JS-SDK
-
微信 JS-SDK 是微信公众平台面向网页开发者提供的基于 微信内的网页 开发工具包;
-
通过使用微信 JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验;
-
详细的文档可以参阅:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
HTML5+
-
HTML5+ 是 HTML5 中国产业联盟,是工信部下属单位,是为了更好的推进 HTML5 的商用、更好的为 HTML5 开发者服务而由产业链厂商共同组成的一个联盟;
-
HTML5+ 所开发的应用严格来讲并不是 WebApp,而是应该属于 HybridApp,在 uniapp 中内置了 HTML5+ 引擎,让 js 可以直接调用丰富的原生能力;因此也可以将 HTML5+ 看作是一个运行时,在此运行时下提供了一个名为 plus 的对象,扩展了 HTML5 的能力范围;
-
文档地址:https://www.html5plus.org/doc/h5p.html
JSBridge
-
之前介绍 WebView 的时候,实际上介绍过 JSBridge,还记得 WebView 么?它是原生 APP 中提供的一个组件,正因为有这个组件,所以原生应用也能很轻松的打开网页;
-
而在 WebView 中,又提供了一个 JSBridge 的组件,该组件可以作为 JS 和 Native 之间进行通信的一个桥梁;
📱 项目案例
上一篇