Bas*_*asj 3 javascript mobile accelerometer shake
我读过Javascript。监听 iPhone 震动事件?检测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>
Run Code Online (Sandbox Code Playgroud)
不幸的是,这似乎不适用于最新的 iOS 设备,并且此问题表明应为最新的 iOS 版本授予特殊权限。请注意,此处的代码不容易在 shake.js 库中使用。
问题:截至 2022 年,有哪种方法可以使用 Javascript 检测“摇动”事件,适用于主要浏览器(Firefox、Chrome、Safari)和移动设备(iOS、Android)?
如果首先出现一个请求许可的弹出窗口(例如请求地理定位请求许可的弹出窗口),那就可以了。
没有shake事件:存在的最接近的事件是devicemotion。
根据您问题的内容,我推断您只想订阅当设备加速超过某个阈值时触发的事件,并在可能的触发器之间有一个去抖延迟(超时)。
\n使用您链接到的“shake.js”库作为参考,我编写了一个 TypeScript 模块,您可以使用它来完成基本相同的事情。它包括在启动时获得用户权限批准,但请记住,您必须调用该ShakeInstance.start()方法来响应用户启动的事件(例如单击按钮)。
\n\n\n注意:您根据 MDN 相关文档页面上的兼容性数据列出的环境支持模块中使用的方法。(值得注意的是,桌面 Safari 根本不支持 DeviceMotionEvent。)但是,我无权访问您列出的所有环境组合以便自己执行测试,因此我将把它留给您。
\n
function createEvent <Type extends string, Detail>(\n type: Type,\n detail: Detail,\n): CustomEvent<Detail> & {type: Type} {\n return new CustomEvent(type, {detail}) as CustomEvent<Detail> & {type: Type};\n}\n\nfunction getMaxAcceleration (event: DeviceMotionEvent): number {\n let max = 0;\n if (event.acceleration) {\n for (const key of [\'x\', \'y\', \'z\'] as const) {\n const value = Math.abs(event.acceleration[key] ?? 0);\n if (value > max) max = value;\n }\n }\n return max;\n}\n\nexport type ShakeEventData = DeviceMotionEvent;\nexport type ShakeEvent = CustomEvent<ShakeEventData> & {type: \'shake\'};\nexport type ShakeEventListener = (event: ShakeEvent) => void;\n\nexport type ShakeOptions = {\n /**\n * Minimum acceleration needed to dispatch an event:\n * meters per second squared (m/s\xc2\xb2).\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/acceleration\n */\n threshold: number;\n /**\n * After a shake event is dispatched, subsequent events will not be dispatched\n * until after a duration greater than or equal to this value (milliseconds).\n */\n timeout: number;\n};\n\nexport class Shake extends EventTarget {\n #approved?: boolean;\n #threshold: ShakeOptions[\'threshold\'];\n #timeout: ShakeOptions[\'timeout\'];\n #timeStamp: number;\n\n constructor (options?: Partial<ShakeOptions>) {\n super();\n const {\n threshold = 15,\n timeout = 1000,\n } = options ?? {};\n this.#threshold = threshold;\n this.#timeout = timeout;\n this.#timeStamp = timeout * -1;\n }\n \n // @ts-ignore\n addEventListener (\n type: \'shake\',\n listener: ShakeEventListener | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n type Arg1 = Parameters<EventTarget[\'addEventListener\']>[1];\n super.addEventListener(type, listener as Arg1, options);\n }\n\n dispatchEvent (event: ShakeEvent): boolean {\n return super.dispatchEvent(event);\n }\n\n // @ts-ignore\n removeEventListener (\n type: \'shake\',\n callback: ShakeEventListener | null,\n options?: EventListenerOptions | boolean\n ): void {\n type Arg1 = Parameters<EventTarget[\'removeEventListener\']>[1];\n super.removeEventListener(type, callback as Arg1, options);\n }\n\n async approve (): Promise<boolean> {\n if (typeof this.#approved === \'undefined\') {\n if (!(\'DeviceMotionEvent\' in window)) return this.#approved = false;\n try {\n type PermissionRequestFn = () => Promise<PermissionState>;\n type DME = typeof DeviceMotionEvent & { requestPermission: PermissionRequestFn };\n if (typeof (DeviceMotionEvent as DME).requestPermission === \'function\') {\n const permissionState = await (DeviceMotionEvent as DME).requestPermission();\n this.#approved = permissionState === \'granted\';\n }\n else this.#approved = true;\n }\n catch {\n this.#approved = false;\n }\n }\n return this.#approved;\n }\n\n #handleDeviceMotion = (event: DeviceMotionEvent): void => {\n const diff = event.timeStamp - this.#timeStamp;\n if (diff < this.#timeout) return;\n const accel = getMaxAcceleration(event);\n if (accel < this.#threshold) return;\n this.#timeStamp = event.timeStamp;\n this.dispatchEvent(createEvent(\'shake\', event));\n };\n\n async start (): Promise<boolean> {\n const approved = await this.approve();\n if (!approved) return false;\n window.addEventListener(\'devicemotion\', this.#handleDeviceMotion);\n return true;\n }\n\n stop (): void {\n window.removeEventListener(\'devicemotion\', this.#handleDeviceMotion);\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n像这样使用:
\nconst shake = new Shake({threshold: 15, timeout: 1000});\n\nshake.addEventListener(\'shake\', ev => {\n console.log(\'Shake!\', ev.detail.timeStamp, ev.detail.acceleration);\n});\n\n// Then, in response to a user-initiated event:\nconst approved = await shake.start();\nRun Code Online (Sandbox Code Playgroud)\n\n\n我不确定 SO 片段环境是否会导致演示出现问题,但我已经包含了 TS Playground 链接中已编译的 JS,以防万一:
\n
"use strict";\nfunction createEvent(type, detail) {\n return new CustomEvent(type, { detail });\n}\nfunction getMaxAcceleration(event) {\n let max = 0;\n if (event.acceleration) {\n for (const key of [\'x\', \'y\', \'z\']) {\n const value = Math.abs(event.acceleration[key] ?? 0);\n if (value > max)\n max = value;\n }\n }\n return max;\n}\nclass Shake extends EventTarget {\n constructor(options) {\n super();\n this.#handleDeviceMotion = (event) => {\n const diff = event.timeStamp - this.#timeStamp;\n if (diff < this.#timeout)\n return;\n const accel = getMaxAcceleration(event);\n if (accel < this.#threshold)\n return;\n this.#timeStamp = event.timeStamp;\n this.dispatchEvent(createEvent(\'shake\', event));\n };\n const { threshold = 15, timeout = 1000, } = options ?? {};\n this.#threshold = threshold;\n this.#timeout = timeout;\n this.#timeStamp = timeout * -1;\n }\n #approved;\n #threshold;\n #timeout;\n #timeStamp;\n // @ts-ignore\n addEventListener(type, listener, options) {\n super.addEventListener(type, listener, options);\n }\n dispatchEvent(event) {\n return super.dispatchEvent(event);\n }\n // @ts-ignore\n removeEventListener(type, callback, options) {\n super.removeEventListener(type, callback, options);\n }\n async approve() {\n if (typeof this.#approved === \'undefined\') {\n if (!(\'DeviceMotionEvent\' in window))\n return this.#approved = false;\n try {\n if (typeof DeviceMotionEvent.requestPermission === \'function\') {\n const permissionState = await DeviceMotionEvent.requestPermission();\n this.#approved = permissionState === \'granted\';\n }\n else\n this.#approved = true;\n }\n catch {\n this.#approved = false;\n }\n }\n return this.#approved;\n }\n #handleDeviceMotion;\n async start() {\n const approved = await this.approve();\n if (!approved)\n return false;\n window.addEventListener(\'devicemotion\', this.#handleDeviceMotion);\n return true;\n }\n stop() {\n window.removeEventListener(\'devicemotion\', this.#handleDeviceMotion);\n }\n}\n////////////////////////////////////////////////////////////////////////////////\n// Use:\nconst shake = new Shake({ threshold: 15, timeout: 1000 });\nshake.addEventListener(\'shake\', ev => {\n console.log(\'Shake!\', ev.detail.timeStamp, ev.detail.acceleration);\n});\nconst button = document.getElementById(\'start\');\nif (button) {\n button.addEventListener(\'click\', async () => {\n const approved = await shake.start();\n const div = document.body.appendChild(document.createElement(\'div\'));\n div.textContent = `Approved: ${String(approved)}`;\n button.remove();\n }, { once: true });\n}Run Code Online (Sandbox Code Playgroud)\r\n<button id="start">Approve</button>Run Code Online (Sandbox Code Playgroud)\r\n| 归档时间: |
|
| 查看次数: |
6079 次 |
| 最近记录: |