使用 Javascript 检测抖动事件,适用于所有主要浏览器/设备(iOS、Android)
Posted
技术标签:
【中文标题】使用 Javascript 检测抖动事件,适用于所有主要浏览器/设备(iOS、Android)【英文标题】:Detect shake event with Javascript, with all major browsers/devices (iOS, Android) 【发布时间】:2021-12-31 18:28:59 【问题描述】:我已经阅读了javascript. Listen for iPhone shake event? 和Detecting shaking in html5 mobile,它们提供了一个很好的解决方案来检测手机“摇晃”事件:
<script src="shake.js"></script>
<script>
var myShakeEvent = new Shake(threshold: 15, timeout: 1000);
myShakeEvent.start();
window.addEventListener('shake', function() alert('shake!'); , false);
</script>
不幸的是,这似乎不适用于最新的 ios 设备,this issue 表明应该为最新的 iOS 版本授予特殊权限。请注意,代码from here 在库shake.js 中不易使用。
问题:截至 2022 年,在主要浏览器(Firefox、Chrome、Safari)和移动设备(iOS、Android)上,哪种方法可以使用 Javascript 检测“摇晃”事件?强>
如果有一个弹出窗口首先请求许可就可以了(比如弹出请求请求地理定位的许可)。
【问题讨论】:
Apple 向您收取在其操作系统上开发应用程序的费用。他们的操作系统被锁定,他们的浏览器可以支持他们想要的。换句话说,您需要联系摇动开发人员以获得最新的 shim 支持,或者继续在 iOS 中构建您的应用程序,您将获得所有支持。 :) "crossbrowser and crossdevice":我不知道这是什么意思。您能否提供您想要支持的特定浏览器和特定设备的列表? @jsejcksn 我的意思是:在主要浏览器(Firefox、Chrome、Safari)和主要移动设备(iOS、android)上工作。我编辑了问题和标题以包含此内容。 @Basj (1) 什么是“等”在浏览器中,您希望支持每个列出的浏览器的哪些版本? (2) 什么是“等”在设备中,您希望支持每个列出的操作系统的哪些版本? @Basj ????这缩小了可能的组合相当! 【参考方案1】:没有shake
事件:存在的最接近的事件是devicemotion
。
根据您的问题内容,我推断您只想订阅在设备加速度超过某个阈值时触发的事件,并且可能的触发器之间存在去抖动延迟(超时)。
使用您链接到的“shake.js”库作为参考,我编写了一个 TypeScript 模块,您可以使用它来完成基本相同的事情。它包括在开始时获得用户权限批准,但请记住,您必须调用 ShakeInstance.start()
方法来响应用户发起的事件(例如按钮单击)。
注意:根据 MDN 相关文档页面上的兼容性数据,您列出的环境支持模块中使用的方法。 (值得注意的是,桌面 Safari 根本不支持 DeviceMotionEvent。)但是,我无权访问您列出的所有环境组合以便自己执行测试,所以我将把它留给您。
TS Playground
function createEvent <Type extends string, Detail>(
type: Type,
detail: Detail,
): CustomEvent<Detail> & type: Type
return new CustomEvent(type, detail) as CustomEvent<Detail> & type: Type;
function getMaxAcceleration (event: DeviceMotionEvent): number
let max = 0;
if (event.acceleration)
for (const key of ['x', 'y', 'z'] as const)
const value = Math.abs(event.acceleration[key] ?? 0);
if (value > max) max = value;
return max;
export type ShakeEventData = DeviceMotionEvent;
export type ShakeEvent = CustomEvent<ShakeEventData> & type: 'shake';
export type ShakeEventListener = (event: ShakeEvent) => void;
export type ShakeOptions =
/**
* Minimum acceleration needed to dispatch an event:
* meters per second squared (m/s²).
*
* https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/acceleration
*/
threshold: number;
/**
* After a shake event is dispatched, subsequent events will not be dispatched
* until after a duration greater than or equal to this value (milliseconds).
*/
timeout: number;
;
export class Shake extends EventTarget
#approved?: boolean;
#threshold: ShakeOptions['threshold'];
#timeout: ShakeOptions['timeout'];
#timeStamp: number;
constructor (options?: Partial<ShakeOptions>)
super();
const
threshold = 15,
timeout = 1000,
= options ?? ;
this.#threshold = threshold;
this.#timeout = timeout;
this.#timeStamp = timeout * -1;
// @ts-ignore
addEventListener (
type: 'shake',
listener: ShakeEventListener | null,
options?: boolean | AddEventListenerOptions
): void
type Arg1 = Parameters<EventTarget['addEventListener']>[1];
super.addEventListener(type, listener as Arg1, options);
dispatchEvent (event: ShakeEvent): boolean
return super.dispatchEvent(event);
// @ts-ignore
removeEventListener (
type: 'shake',
callback: ShakeEventListener | null,
options?: EventListenerOptions | boolean
): void
type Arg1 = Parameters<EventTarget['removeEventListener']>[1];
super.removeEventListener(type, callback as Arg1, options);
async approve (): Promise<boolean>
if (typeof this.#approved === 'undefined')
if (!('DeviceMotionEvent' in window)) return this.#approved = false;
try
type PermissionRequestFn = () => Promise<PermissionState>;
type DME = typeof DeviceMotionEvent & requestPermission: PermissionRequestFn ;
if (typeof (DeviceMotionEvent as DME).requestPermission === 'function')
const permissionState = await (DeviceMotionEvent as DME).requestPermission();
this.#approved = permissionState === 'granted';
else this.#approved = true;
catch
this.#approved = false;
return this.#approved;
#handleDeviceMotion = (event: DeviceMotionEvent): void =>
const diff = event.timeStamp - this.#timeStamp;
if (diff < this.#timeout) return;
const accel = getMaxAcceleration(event);
if (accel < this.#threshold) return;
this.#timeStamp = event.timeStamp;
this.dispatchEvent(createEvent('shake', event));
;
async start (): Promise<boolean>
const approved = await this.approve();
if (!approved) return false;
window.addEventListener('devicemotion', this.#handleDeviceMotion);
return true;
stop (): void
window.removeEventListener('devicemotion', this.#handleDeviceMotion);
这样使用:
const shake = new Shake(threshold: 15, timeout: 1000);
shake.addEventListener('shake', ev =>
console.log('Shake!', ev.detail.timeStamp, ev.detail.acceleration);
);
// Then, in response to a user-initiated event:
const approved = await shake.start();
我不确定 SO sn-p 环境是否会导致演示问题,但我已经包含了来自 TS Playground 链接的已编译 JS,以防万一:
"use strict";
function createEvent(type, detail)
return new CustomEvent(type, detail );
function getMaxAcceleration(event)
let max = 0;
if (event.acceleration)
for (const key of ['x', 'y', 'z'])
const value = Math.abs(event.acceleration[key] ?? 0);
if (value > max)
max = value;
return max;
class Shake extends EventTarget
constructor(options)
super();
this.#handleDeviceMotion = (event) =>
const diff = event.timeStamp - this.#timeStamp;
if (diff < this.#timeout)
return;
const accel = getMaxAcceleration(event);
if (accel < this.#threshold)
return;
this.#timeStamp = event.timeStamp;
this.dispatchEvent(createEvent('shake', event));
;
const threshold = 15, timeout = 1000, = options ?? ;
this.#threshold = threshold;
this.#timeout = timeout;
this.#timeStamp = timeout * -1;
#approved;
#threshold;
#timeout;
#timeStamp;
// @ts-ignore
addEventListener(type, listener, options)
super.addEventListener(type, listener, options);
dispatchEvent(event)
return super.dispatchEvent(event);
// @ts-ignore
removeEventListener(type, callback, options)
super.removeEventListener(type, callback, options);
async approve()
if (typeof this.#approved === 'undefined')
if (!('DeviceMotionEvent' in window))
return this.#approved = false;
try
if (typeof DeviceMotionEvent.requestPermission === 'function')
const permissionState = await DeviceMotionEvent.requestPermission();
this.#approved = permissionState === 'granted';
else
this.#approved = true;
catch
this.#approved = false;
return this.#approved;
#handleDeviceMotion;
async start()
const approved = await this.approve();
if (!approved)
return false;
window.addEventListener('devicemotion', this.#handleDeviceMotion);
return true;
stop()
window.removeEventListener('devicemotion', this.#handleDeviceMotion);
////////////////////////////////////////////////////////////////////////////////
// Use:
const shake = new Shake( threshold: 15, timeout: 1000 );
shake.addEventListener('shake', ev =>
console.log('Shake!', ev.detail.timeStamp, ev.detail.acceleration);
);
const button = document.getElementById('start');
if (button)
button.addEventListener('click', async () =>
const approved = await shake.start();
const div = document.body.appendChild(document.createElement('div'));
div.textContent = `Approved: $String(approved)`;
button.remove();
, once: true );
<button id="start">Approve</button>
【讨论】:
【参考方案2】:查看这些链接:
Detect a shake in iOS Safari with Javascript?
https://github.com/lhagan/jquery.ios-shake
这第二个库基本上用作:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
<title>Detect shake in phone using Jquery</title>
<script type="text/javascript" src="jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="jquery.ios-shake.js"></script>
<script type="text/javascript">
$(document).ready(function()
$.shake(
callback: function()
alert("Please upvote my answer!");
);
);
</script>
</head>
<body>
<div id="content">
<h1>Detecting Phone shake using jQuery</h1>
</div>
<div id="welcome">Shake your phone to get the alert</div>
</body>
</html>
【讨论】:
谢谢,但我正在寻找一个没有 jquery 的纯 Javascript 解决方案。 而且这些库已经有 8 年以上的历史了,此时,iOS 没有什么特别的要求。如今,从 iOS 13 开始,需要特殊权限才能访问加速度计。这是问题中应该解决的问题。谢谢!以上是关于使用 Javascript 检测抖动事件,适用于所有主要浏览器/设备(iOS、Android)的主要内容,如果未能解决你的问题,请参考以下文章
使用 react-native-sensors 检测抖动设备事件?