javascript 陀螺仪加摄像头可以玩出AR效果

重要事情说三遍

此文章中的API接口,必须放在 https 协议下测试!浏览器APP必须开启摄像头权限!此文章代码仅在 chrome 手机浏览器及微信中测试通过。

此处省略两遍。

搞事

陀螺仪 API 及 摄像头API 可以搞太多事,比如:

  • 摄像头API
  1. 扫码识别

  2. 录像拍照

  3. 类似 APP 的身份识别效果等等

  • 陀螺仪 API
  1. AR 效果

  2. 指南针

  3. 海拔高度等等

示例

本文仅使用陀螺仪模拟 AR 效果,完整 AR 逃不掉一整套算法,本菜鸟还做不到啊。

DEMO 演示效果:

example-ar.html

手机扫码查看效果:

如图,旋转手机会看到不同偏移:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>陀螺仪模拟AR效果</title>
<style>
#video {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100vh;
background-color: #141414;
}
#address {
position: fixed;
left: 50%;
top: 50%;
width: 40px;
height: 60px;
border-radius: 100%;
background-color: #009900;
z-index: 4;
color: #fff;
}
</style>
</head>
<body>
<div id="address"></div>
<video id="video" autoplay="autoplay"></video>
<script>
var video = document.getElementById('video');
//默认使用前摄像头,强制使用后置摄像头如下设置
var constraints = {video: { facingMode: { exact: "environment" } }};
// let constraints = { video: true };
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
// 旧的浏览器可能没有srcObject
window.stream = stream;
if ("srcObject" in video) {
video.srcObject = stream;
} else {
// 防止在新的浏览器里使用它,应为它已经不再支持了
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function() {
video.play();
};
}).catch(function(err) {
console.error(err.name + ": " + err.message);
});
// http://stackoverflow.com/questions/18112729/calculate-compass-heading-from-deviceorientation-event-api/21829819#21829819
// todo 算法来源 https://my.oschina.net/u/2324376/blog/790939
function compassHeading(alpha, beta, gamma) {
// Convert degrees to radians
var alphaRad = alpha * (Math.PI / 180);
var betaRad = beta * (Math.PI / 180);
var gammaRad = gamma * (Math.PI / 180);

// Calculate equation components
var cA = Math.cos(alphaRad);
var sA = Math.sin(alphaRad);
// var cB = Math.cos(betaRad);
var sB = Math.sin(betaRad);
var cG = Math.cos(gammaRad);
var sG = Math.sin(gammaRad);

// Calculate A, B, C rotation components
var rA = - cA * sG - sA * sB * cG;
var rB = - sA * sG + cA * sB * cG;
// var rC = - cB * cG;

// Calculate compass heading
var compassHeading = Math.atan(rA / rB);

// Convert from half unit circle to whole unit circle
if(rB < 0) {
compassHeading += Math.PI;
}else if(rA < 0) {
compassHeading += 2 * Math.PI;
}

// Convert radians to degrees
compassHeading *= 180 / Math.PI;

return compassHeading;
}
function updateGravity (event) {
const that = this;
// !根据陀螺仪获取旋转角度,设置人物位置
const winWidth = document.documentElement.clientWidth;
const itemWidth = parseInt(window.getComputedStyle(address).width);
const heading = 360 - compassHeading(event.alpha, event.beta, event.gamma);
const angle = 0;

address.style.transform = 'rotate('+(heading - 90 + angle)+'deg)';
// `${(winWidth - itemWidth) * Math.min(180, Math.max(0, (heading + 360 - angle) % 360)) / 180}px` 移动区间 0 - 180
address.style.left = `${(winWidth - itemWidth) * ((heading + 360 - angle) % 360) / 180}px`;
}
window.addEventListener('deviceorientation', updateGravity, false);
</script>
</body>
</html>
本文由 linx(544819896@qq.com) 创作,采用 CC BY 4.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。本文链接为: https://blog.jijian.link/2020-09-08/js-ar/

如果您觉得文章不错,可以请我喝一杯咖啡!