加速度计是一种测量设备正确加速度的设备。
使用 Javascript 检测摇动事件,适用于所有主要浏览器/设备(iOS、Android)
我读过Javascript。监听 iPhone 震动事件?检测 html5 mobile 中的晃动,这为检测手机“晃动”事件提供了一个很好的解决方案: <p>我已经阅读了<a href="https://stackoverflow.com/questions/2202216/javascript-listen-for-iphone-shake-event">Javascript。监听 iPhone 震动事件?</a> 和 <a href="https://stackoverflow.com/questions/12334160/detecting-shaking-in-html5-mobile">Detecting shake in html5 mobile</a> 这为检测手机“震动”事件提供了一个很好的解决方案:</p> <pre><code><script src="shake.js"></script> <script> var myShakeEvent = new Shake({threshold: 15, timeout: 1000}); myShakeEvent.start(); window.addEventListener('shake', function() { alert('shake!'); }, false); </script> </code></pre> <p>不幸的是,这似乎不适用于最新的 iOS 设备,并且 <a href="https://github.com/alexgibson/shake.js/issues/53" rel="nofollow noreferrer">此问题</a> 表明应为最新的 iOS 版本授予特殊权限。请注意,<a href="https://dev.to/li/how-to-requestpermission-for-devicemotion-and-deviceorientation-events-in-ios-13-46g2" rel="nofollow noreferrer">此处的代码</a>在 shake.js 库中不容易使用。</p> <p><strong>问题:截至 2022 年,有哪种方法可以使用 Javascript 检测“摇动”事件,适用于主要浏览器(Firefox、Chrome、Safari)和移动设备(iOS、Android)?</strong></p> <p>如果首先出现一个请求许可的弹出窗口(例如请求地理定位请求许可的弹出窗口),那就可以了。</p> </question> <answer tick="true" vote="6"> <p>不存在 <pre><code>shake</code></pre> 事件:存在的最接近的事件是 <a href="https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent" rel="noreferrer"><pre><code>devicemotion</code></pre></a>。</p> <p>根据您问题的内容,我推断您只想订阅当设备加速度超过特定阈值时触发的事件,并在可能的触发器之间有一个去抖延迟(超时)。</p> <p>使用您链接到的“shake.js”库作为参考,我编写了一个 TypeScript 模块,您可以使用它来完成基本相同的事情。它包括在启动时获得用户权限批准,但请记住,您必须调用 <pre><code>ShakeInstance.start()</code></pre> 方法来响应用户启动的事件(例如单击按钮)。</p> <blockquote> <p>注意:您根据 MDN 相关文档页面上的兼容性数据列出的环境支持模块中使用的方法。 (值得注意的是,桌面版 Safari 根本不支持 DeviceMotionEvent。)但是,我无法访问您列出的所有环境组合以便自己执行测试,因此我将其留给您。</p> </blockquote> <p><a href="https://www.typescriptlang.org/play?noUncheckedIndexedAccess=true&target=99&useUnknownInCatchVariables=true&exactOptionalPropertyTypes=true#code/GYVwdgxgLglg9mABBATgUwIZTQUQG5phSIA8AKgJ4AOaiaAHtmACYDOirUKMYA5gDSIAImigYYAGwB8ACgCwAKESIo1NAC5ElGv0XLmo8RM0ixk3QoCUmgMIhOcALb5CUEqaNTEAMkQBvVRpNbTQAX389RHQoEBQkMDQAd0Q7B2cCIhlAtEE-AzMJUMtEDHZUqCcXIndDSS9fALVgtVCAbkVQxUVQSFgERF5RAFkMegBBCAg0CTQULHgkGTQMqBNlmCmhuD6wKqhrRDAQRwAjWYilRBniR1HEAF5EAAZ2y5hgRCWVgDoMSenZvMEMU-JFlMA4ChPhAEJxEABrNAURBwD4AbQA5PQMYIMRQcYgMQAvDEAXRK7BhYE4ILBymQsOIeAwEhAtEeIygAAtfidWF9XL9-jM5js0YiKOSAPxS56WV70+nvT7M1m0Ly3ejFTUPRCqtkK+mdS7G5TRWJITWvY2KBhUSHEbKIADKXIwiL2QiwGF1IjwGzQWx2e1edodKjULrdHpWuvKlRWJFd7twKy9Ynq-mymgxrGjaAxbVt9HtKEdkeTMdcABkYJxCOdHgKiJpK6nXMV7l48HAYMxXsXS+WaFGUwB5Kg7diPUGXAD0ACoF2CF4ghjwYI5jiVhYCdoc0GgDMwVHBEMw61QsBAuSUkMtXOoV4hHKJZuwaFDWGgqSfWABHEAMHQE8ZEcOdWAAJssb4V2fLkoCgKhWHUOc5wMAgJDgT9vkcOAiUkCQMG+SFeDnQgAFoAFVnXQuAIFYOcAHU0BOOcxgABQASTnP0AyDBY9jnP4phFIEwBXOdIm5dA8zgCRmE0I5TlmQ1F2XS5EFXMZgGwKEfTzFM6FjOtz0va8uSPQRWBAPk0EA1xjNcdhEkIw5tkQM4zNYK8oBvI9n3AWAJBKXTzh9ZhYnEgZ0Cwc5uQwJBIToQCWVPFQuVM-VaDAwi6x-BA2BgyTpM3NA4BAVZDmOM4UGtAcFDDMtkCI1h2DbOhGEINhED2MhgMGYhZ2UABiDAqCoFA4AIZgpU0E44HkzAwENEaZLQOSFNbfMJynTF1s25gyVW2BXwqqq212hZWH2srzuOyI1rK50xEcKglJq1SukuKlOBQEBoGSmRsKnObEA44DYBZJMdsna6pFpTSOBAT8ZHlOlfqGullAOrl5JPR4AEYAFYLEVHG7sq3VCaeWmyaNXUQeuxAZX8Iske5OtvjWrlZLxhTdVx-HDRxzLWG507yqpx5JfOkWMq5p7XxejA3sFyniFXCjCcNU1EEiNDEAAASgVgKJgXgwEhNBIgwZhmD2Wt6wSKF5A5ppCUMxEcTpCQ6yYWZtpTR3-YbKEAB9qokCR6eUJnYTBhalsSxBI7Ge2Q+d2YrthSIDh7PsLkVJ0xhQXhCd1CG5lfPTWBIPqBtETE7YdlYnYDlAySkNFCdJeWbJwlvM47rI1EEP2s-09hS-LwR4+pdGTW+-RzL8rk9k+B8W1HKsiAOJOZhT4b6XNOJkZwi8fIsvZm32XXl8QQ2TbNi2rfQSJ0Dwghh7Dz46WzT2+YfZIwgCyCQJw-jwiDrvKA7df6RyONHWOKI4YJ00D-F2OdqSp08otQ+ElLj517CeY+ONIwzwro8Kuqs3woDrg3MuTcMSf2mu2IgcCXZdx7n3OkA9ZjfBYd-NuocXajx0MgMBECIDwgpIgChc9UEL3vgoW2rAKCQBKBNKaBBPgHA4lNRw+USAH2Wl4UhiBlRiPKh8Tm4sxpaNYQTe4jwMTgAMMAHgR4MSI3JhYj4MgACEMgMR8U2NsQSKwMQWKQK5FgcBEiWGKKfJAtjubjUmo43UwAWTfnljjFAyJzHF0jBxWYhi2oLAAEr2TZJwAAYkgJsnYvD6KcEY0pKBymsAWCrbAUg8n0idEIIYOBBZqFRMIdYYTgyxgaFEGpG0oAdK6QsTQyy6zdIQNUhy9SkDs18coSx2QJkyFCYGcJCAN6lGECMmC6AdlLLKRshYDxnGEh6NABY3ii4HOUJjRAn4VkIF6eyEoiRxDEFOVM85MzHLXOGTgO5CzODrIqQgNGAzi5izSQ4maupAXPOBWIbAryXG8DmEQLxmLEB618dMb8Cs7HpO0UeQW-00ADNpfSUBa8fm+NSfYjJeLHjZIkLk7GNK6RcuSYynFQqjzKMem6FgMwzkCX6E2LeVU1UXN2CsIhhcux8oZNSYgF5gAfEeFq74ksVZqworKpWaA7VUHlpY81HwSCOtlpVJJogLTy3+SJaYupBojHGLuUUCxb6L0VJY4NIUvUCqFgpP1MQ4jy2Tc9V6VBdTWttTmzN2LL6+RvDfVAmBsA31zEAwQWrEm6wasoUo6iIAcDEM1NGayDFGJMYlMxGNGSaPlQTMFELZXMtYRiukliAmTpmmmi0iBRXiqRrE5g8TfgZ2EZPYJGEAx4R2ASAVyrmCquheqsAsaT7+rPlwA0kQbSXAcLmrtepiHGvXZuwRbDYEiNmHu6Fh6vmCBPYlM9aAdU7GvcaJ9314MqMQ4oNCKHUNofQxhzDWHsM4dwyh5Dc5EBUW-E+RD-yvagoSMkNsMgAi8w2vzRSiASagY1poGmtMigNQo1u1uNZ-0oGCRRgkywHgDp+rCJa3wsK8GCW2AJIm8DfHyEYG12bVZUDrUplTkghSiT3AsReXHvr-JOJVCojTzz0WOIKQaOAZiviIAAIQoFxZgQmO1QG8Q1SxZnELAh+X5izvGMEAYxBAP20iCQto0WjMTxqg24tZY8DA4KYDEB45wSG06QFDovHgXUG6IA2aIN8BazAKC-Amt1GwmUFIyCKyVqA3wK1xXs2gRzUA90wDwN469K8lPYEYDYBATBiCPAAAZjCS0xgAJH4F63A+AyHnUeSwoQJvyyCwgARHWp0wdyAgKYmh71hCM4oIAA" rel="noreferrer">TS游乐场</a></p> <pre><code>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); } } </code></pre> <p>像这样使用:</p> <pre><code>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(); </code></pre> <blockquote> <p>我不确定 SO 片段环境是否会导致演示出现问题,但我已经包含了 TS Playground 链接中已编译的 JS,以防万一:</p> </blockquote> <p></p><div data-babel="false" data-lang="js" data-hide="true" data-console="true"> <div> <pre><code>"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 }); }</code></pre> <pre><code><button id="start">Approve</button></code></pre> </div> </div> <p></p> </answer> <answer tick="false" vote="1"> <h2>使用纯 JS 进行设备震动检测,无需库</h2> <p>通用震动检测的尝试。</p> <p>对于非 iOS:第一次摇动会向用户显示权限提示,要求允许使用传感器。</p> <p>对于 iOS(或任何对 <pre><code>requestPermission</code></pre> API 严格的设备):在用户体验中需要一个额外的步骤。用户必须自己调用传感器权限提示,而不是在第一次摇动时自行出现权限提示。这是通过您在体验中的某个位置(可能是在工具栏或模式中)提供一个按钮来完成的,其中该按钮调用 <pre><code>requestPermission</code></pre> API。</p> <p>除了上述之外,您还需要将其托管在 HTTPS 服务器上(我使用了 <pre><code>github-pages</code></pre>)。我也让它在 <pre><code>localhost</code></pre>/local-wifi 上工作,但那是另一个线程。对于这个问题,我会避免在在线 IDE(如 Codepen)中测试它,即使它们是 https,<pre><code>requestPermission</code></pre> 可能不起作用。</p> <p><strong>建议:</strong>无论您做什么,在您的应用程序(或网站)中,如果您独立存储用户的状态(即他们是否允许许可),那就太好了。如果他们点击“取消”,那么您可以可靠地知道这一点,并可能定期告诉他们“嘿,您错过了这个很棒的功能!”根据您的经验,并再次提供权限提示(通过显式 UI 控制)。</p> <p><strong>HTML</strong></p> <pre><code><button id="btn_reqPermission" style="display: none;padding: 2em"> Hey! This will be much better with sensors. Allow? </button> <div id="output_message"></div> </code></pre> <p><strong>Javascript:</strong></p> <pre><code>// PERMISSION BUTTON var btn_reqPermission = document.getElementById("btn_reqPermission") btn_reqPermission.addEventListener("click", () => { this.checkMotionPermission() }) // ON PAGE LOAD this.checkMotionPermission() // FUNCTIONS async function checkMotionPermission() { // Any browser using requestPermission API if (typeof DeviceOrientationEvent.requestPermission === 'function') { // If previously granted, user will see no prompts and listeners get setup right away. // If error, we show special UI to the user. // FYI, "requestPermission" acts more like "check permission" on the device. await DeviceOrientationEvent.requestPermission() .then(permissionState => { if (permissionState == 'granted') { // Hide special UI; no longer needed btn_reqPermission.style.display = "none" this.setMotionListeners() } }) .catch( (error) => { console.log("Error getting sensor permission: %O", error) // Show special UI to user, suggesting they should allow motion sensors. The tap-or-click on the button will invoke the permission dialog. btn_reqPermission.style.display = "block" }) // All other browsers } else { this.setMotionListeners() } } async function setMotionListeners() { // ORIENTATION LISTENER await window.addEventListener('orientation', event => { console.log('Device orientation event: %O', event) }) // MOTION LISTENER await window.addEventListener('devicemotion', event => { console.log('Device motion event: %O', event) // SHAKE EVENT // Using rotationRate, which essentially is velocity, // we check each axis (alpha, beta, gamma) whether they cross a threshold (e.g. 256). // Lower = more sensitive, higher = less sensitive. 256 works nice, imho. if ((event.rotationRate.alpha > 256 || event.rotationRate.beta > 256 || event.rotationRate.gamma > 256)) { this.output_message.innerHTML = "SHAKEN!" setTimeout(() => { this.message.innerHTML = null }, "2000") } }) } </code></pre> </answer> </body></html>
我正在 Unity iPhone 应用程序上读取陀螺仪,但无论我移动手机,它都会返回相同的值,它返回的值始终是常量值 (0.00000, 0.00000, 0.70711, -0.70711)。 我正在读它
将来自 Arduino 的加速度计和陀螺仪数据转换为 iOS SCNNode 位置和旋转
上下文: 我有一个 Arduino Nano RP2040 Connect 设备,它有一个内置的 IMU。 我还编写了一个 iOS 应用程序,它利用 SceneKit 并可以渲染/动画化 SCNNode。 目标 我想移动我的 Arduino
在 python 中使用 tflite 模型从 arduino nano 33 ble 读取加速度计和陀螺仪数据并预测所做的手势
我已经为 5 个手势训练了一个使用加速度计,在下载转换后的 tflite 模型供使用后,我遇到了无法调整输入数据大小的错误消息...
iPhone 加速度计/磁力计可以用来检测物体何时反转方向吗?
我正在尝试为一个应用程序编写代码,当 iPhone 连接到设备时,它可以检测设备何时反转方向。 到目前为止,我一直专注于尝试提取速度
我正在尝试通过融合来自加速度计、陀螺仪和磁力计的数据来确定 R3 中刚体的姿态。 我采用了“Op ...
我想检测 iPad 的小动作,比如当它被拿起时。 这可能与未运行但刚刚安装的应用程序有关吗?就像它在后台运行一样......
有基于惯性测量单元(IMU)跟踪移动物体的数据,使用加速度计、陀螺仪、磁力计等传感器。从这些数据(通过传感器融合)生成
我有加速度计数据,每一行都标记为动物每秒显示的行为,简单示例: 时间 X_accel 行为 1个 0.01 常设 2个 0.01 常设 3个 0.01 常设 4个 ...
在此先感谢您的帮助。我是 Python 的新手,所以对我很陌生。 我有几个海豹的加速度计和潜水深度数据,我打算制作动画。这是数据外观的偷偷摸摸的高峰......
我正在尝试使用 MPU6050 IMU 来估计位置变化。我知道漂移是个问题,但我只是想在不到几秒钟的短时间内做到这一点。 我已经调整了重力
用户体验设计师,在这里学习AndroidStudio,我一直试图从展示应用中获取52hz的IMU9数据,不幸的是我一直没有成功。我按照文档中关于...
我正在做一个智能手表项目。我想让显示屏关闭,只有当用户将手伸到观看手表的位置时才会打开。我在NRF52单片机上运行我的应用程序...。
我想在我的网页上实现Android和iOS平台的移动设备 "摇动 "事件。所以,我马上找到了Alex Gibson写的一个唯一的脚本。经过一些测试,我发现...
import pandas as pd df = pd.DataFrame(['Time':['16:47:55.510','16:47:55.511','16:47:55.410']}) df output: 时间 0 16:47:55.510 1 16:47:55.511 2 16:47:55.410 如何 ...
有来自陀螺仪和加速度计的数据:gyro_xyz = [gyro_x,gyro_y,gyro_z] acc_xyz = [acc_x,acc_y,acc_z] 在Python上有一个麦吉威克过滤器的实现。Madgwick filter I...
我正在接收陀螺仪数据(g1,g2,g3),以度为单位。我也知道半径r的长度。我想从陀螺仪数据中获得x,y,z的每个长度以及p的度数。请参阅附件...
IMU MPU9250数据表中的满量程范围表示什么,并且哪个更好使用?
我有MPU9250 IMU,我正在努力从传感器输出数据中获取方向。在浏览MPU9250(以及其他传感器,例如BNO055)数据表时,我遇到了满量程范围...
很抱歉,是否已经在这里被部分询问,如何将I-CUBE-LRWAN库添加到新项目?但由于我最近才在该网站上注册,因此无法对此发表评论。如...
在写下问题之前,我要说的是,我确实访问了Internet上有关RPi4B,SPI,BMI088,bcm2835库和C的所有相关页面,但没有运气,很多挫败感。我...