cover
2022年9月10日

中秋快乐,快来摇出你的本命月饼吧

中秋节到了,吃月饼必不可少,为了让大家能够更愉快的过中秋,我花了一晚的功夫开发了一个摇一摇的页面,通过页面摇一摇,可以摇出各种各样的月饼,快来试试你的本命月饼是什么吧。

由于使用摇一摇来抽取月饼,所以需要使用手机打开才行,可手机打开我的网站地址尝试:https://shake-your-mooncake.heyfe.org/

该网站源码地址:https://github.com/ZxBing0066/shake-your-mooncake

注意玩耍时如果摇不出可尝试加大摇一摇幅度哦。不过请小心练出麒麟臂 💪,并在无人环境下进行,避免被误以为发病(🐶)。

注意一共收集了 25 种月饼,越少见的月饼越难摇出,看看你能收集到哪些月饼吧。

技术实现

下面稍微讲一下技术实现的内容。

摇一摇

首先是关于摇一摇部分,摇一摇的实现主要使用的是浏览器中的 devicemotion 事件,该事件可以获取到设备的速度变化、位置、方向等信息。

window.addEventListener('devicemotion', e => {
    const devicemotion = {
        accelerationIncludingGravityX: e.accelerationIncludingGravity.x!,
        accelerationIncludingGravityY: e.accelerationIncludingGravity.y!,
        accelerationIncludingGravityZ: e.accelerationIncludingGravity.z!
    };

    if (latestDevicemotion) {
        // 摇一摇的幅度
        const thresholdCount = Math.max(
            Math.abs(latestDevicemotion.accelerationIncludingGravityX - devicemotion.accelerationIncludingGravityX),
            Math.abs(latestDevicemotion.accelerationIncludingGravityY - devicemotion.accelerationIncludingGravityY),
            Math.abs(latestDevicemotion.accelerationIncludingGravityZ - devicemotion.accelerationIncludingGravityZ)
        );

        // 大于一定幅度才计算
        if (thresholdCount > 20) {
            shakeCount += 8;
        }

        shakeCount = Math.max(0, shakeCount - 2);
    }

    latestDevicemotion = devicemotion;
});

代码如上,通过 devicemotion 事件数据,可以获取到三个方向的加速度,然后将其和之前的数值进行比对即可得出摇一摇的幅度,然后通过 shakeCount 来模拟记录持续摇一摇的时长,从而实现模拟摇一摇触发的效果。

需要注意的是,在安卓设备中,需要在 https 环境下才能正常触发 devicemotion 事件,所以本地开发时会有些麻烦,必须对本地开发工具启用 https。而在 iOS 中,除了必须是在 https 环境外,还需要使用 DeviceMotionEvent.requestPermission 获取到权限后才能监听到事件,所以我将事件绑定抽取出来,根据环境来进行调用,具体相关代码如下:

if (typeof (DeviceMotionEvent as any).requestPermission === 'function') {
    (DeviceMotionEvent as any)
        .requestPermission()
        .then((permissionState: string) => {
            if (permissionState === 'granted') {
                bindEvent();
            }
        })
        .catch(() => {
            alert('获取权限失败');
        });
} else {
    bindEvent();
}

震动与音效

当摇动幅度和摇动时间达到一定阈值后,摇一摇事件触发,会从月饼列表中抽取随机抽取一个月饼,并且会调用手机震动和播放音效来提醒摇一摇的用户出货了。🐶

if (thresholdCount > 80 && shakeCount > 100) {
    window.navigator.vibrate?.(200);

    const audio = document.createElement('audio');
    audio.src = '/success.mp3';
    document.body.appendChild(audio);
    audio.onended = () => document.body.removeChild(audio);
    audio.play();
}

不过实际月饼抽取逻辑略有些出入,并不是摇一摇后触发,而是预先选定,有兴趣的可以看下源码。

需要注意的是,无论是震动、还是播放音效,在 iOS 中由于兼容问题和系统限制,均无法实现。🤦‍♂️ 真不是我的锅。所以各位 iOS 用户在玩耍时还得看好手机,出货了就停止,不然。。。手臂真的会很累的。

总结

本文主要是就着中秋节这个档口,正好试试浏览器的 devicemotion 事件的使用,实际过程中坑还是挺多的,因为浏览器不触发事件也没有报错,都不知道 httpsiOS 中申请权限的问题,导致浪费了一些时间去查资料。

不过该事件还是很有趣,有创意可以玩出很多花样,依稀记得很久以前 Google 有个绝地武士将手机当光剑的游戏来着。