From 3b051fc90d0917ea6b109874215d514f04e54704 Mon Sep 17 00:00:00 2001 From: 18369190717pan <270687594@qq.com> Date: Wed, 5 Jun 2019 20:53:03 +0800 Subject: [PATCH] feat: add mip-orientation-oberver --- components/mip-orientation-observer/README.md | 196 ++++++++++++++ .../example/mip-orientation-observer.html | 244 ++++++++++++++++++ .../mip-orientation-observer.js | 105 ++++++++ 3 files changed, 545 insertions(+) create mode 100644 components/mip-orientation-observer/README.md create mode 100644 components/mip-orientation-observer/example/mip-orientation-observer.html create mode 100644 components/mip-orientation-observer/mip-orientation-observer.js diff --git a/components/mip-orientation-observer/README.md b/components/mip-orientation-observer/README.md new file mode 100644 index 00000000..afe50359 --- /dev/null +++ b/components/mip-orientation-observer/README.md @@ -0,0 +1,196 @@ +# mip-orientation-observer 组件 + +用于监听设备移动的方向信息,发出 `alpha` `beta` `gamma` 事件,提供给其他 MIP 组件使用。 + +标题|内容 +----|---- +类型|通用 +支持布局|nodisplay +所需脚本| https://c.mipcdn.com/static/v2/mip-orientation-observer/mip-orientation-observer.js + +## 示例 + +### 基本使用 + +组件本身只会发出事件,不会做其他任何事情,所以需要结合 `mip-animation` 等组件使用。 + +截止到 2019 年 6 月,组件的兼容性如下: + +![deviceOrientationCompatibility190604](https://boscdn.baidu.com/v1/assets/mip/temp/deviceOrientation190604.png) + +可见,Safari 浏览器暂时不提供支持。同时,不同版本的手机操作系统和浏览器,以及不同的应用程序中内置的浏览器对 deviceorientation 事件的支持不尽相同。尤其在Android平台上,可能会出现有的设备正常工作,有的则毫无反应的情况。 + +所以,mip-orientation-observer 仅推荐结合 `mip-animation` 或 `mip-video` 等媒体组件使用。 + +## 属性 + +### alpha-range + +说明:指定仅对设备绕 z 轴旋转指定范围角度内触发事件。当设备逆时针旋转时,alpha 值增加,当设备顺时针旋转时,alpha 值由 360 开始减少。 + +>注意:alpha 增加是逆时针旋转 + +![orientationZ](https://boscdn.baidu.com/v1/assets/mip/temp/orientationZ-deg.png) + +必选项:若监听 `alpha` 事件必写,监听其他事件不必须 + +类型:2 个数值以空格连接,比如 alpha-range =“0 180” + +默认值:0 到 360 度 + +### beta-range + +说明:指定仅对设备绕 x 轴旋转指定范围角度内触发事件。当设备的顶部和底部与地球表面等距时 beta 值为 0°。当设备的顶部远离地球表面时,此值增加,当设备的顶部倾向地球表面时,此值减少,为负数。 + +![orientationX](https://boscdn.baidu.com/v1/assets/mip/temp/orientationX-deg.png) + +必选项:若监听 `beta` 事件必写,监听其他事件不必须 + +类型:2 个数值以空格连接,比如 alpha-range =“0 180” + +默认值:-180 到 180 度 + +### gamma-range + +说明:指定仅对设备绕 y 轴旋转指定范围角度内触发事件。当设备的左右边缘与地球表面等距时 gamma 值为 0°。 当设备的右侧倾向地球表面时,此值增加,当设备的左侧倾向地球表面时,此值减少,为负数。 + +![orientationY](https://boscdn.baidu.com/v1/assets/mip/temp/orientationY-deg.png) + +必选项:若监听 `gamma` 事件必写,监听其他事件不必须 + +类型:2 个数值以空格连接,比如 alpha-range =“0 180” + +默认值:-90 到 90 度 + +### duration + +说明:指明动画持续时间,与 `mip-animation` 的 `duration` 值相同 + +必选项:使用 `mip-animation` 的 `seekTo` 功能时,必须写明动画持续时间,用于组件计算并返回 `seekToTime` + +类型:数字 + +默认值:无,不写则 `seekTo` 不生效 + +## 注意 + +### 关于角度范围 + +组件顺时针取弧长,作为触发范围。比如: + +若 `alpha-range = "330 30"`,则取 330 度到 360 度和 0 度到 30 度的弧长作为事件触发范围,360 度与 0 度重合 + +若 `alpha-range = "90 30"`,则取 90 度到 360 度和 0 度到 30 度的弧长作为事件触发范围,即 30 度顺时针到 90 度的弧不触发事件 + +若 `beta-range = "-30 30"`,则取 -30 度到 0 度和 0 度到 30 度的弧长作为事件触发范围 + +若 `beta-range = "120 -120"`,则取 120 度到 180 度和 -180 度到 -120 度的弧长作为事件触发范围,180 度与 -180 度重合 + +若 `gamma-range = "-30 30"`,则取 -30 度到 0 度和 0 度到 30 度的弧长作为事件触发范围 + +`gamma-range` 仅支持 -90 度到 0 度和 0 度到 90 度的顺时针弧长监听 + +### 事件参数 + +`alpha` `beta` `gamma` 事件传出数据形如: + +```js +orientData = { + 'angle': 30, + 'percent': 0.2333, + 'seekToTime': 120 +} +``` + +可以在 `on` 事件中获取: + +```html + + +``` + +### 关于为什么角度设置感觉和正常想象的不一样 + +组件的时间和角度设置与原生 `deviceorientation` 事件保持一致,组件可以实现为更符合人操作的方向和角度。但是为了使开发者使用原生事件时不发生混淆,所以与原生事件保持一致。 + +## 示例 + +```html +

alpha-range="330 30",监听范围为手机顺时针和逆时针旋转各 30 度。手机放平,起始状态指向为 0 度,逆时针向左转 30 度到达 330,顺时针向右转 30 到达 30。动画初始状态为动画执行到一半时的状态,逆时针转动手机,alpha 值增大,方块向右移动;顺时针转动手机,alpha 值减小,方块向左。 +

+
+ + + + + + +
+ +

beta-range="-30 30",监听范围为手机顶部向下旋转和向上旋转各 30 度。手机放平,起始状态指向为 0 度,手机顶部向上 30 度到达 30,向下 30 到达 -30。动画初始状态为动画执行到一半时的状态,手机顶部向上转动,beta 值增大,方块向右移动;手机顶部向下转动,beta 值减小,方块向左。 +

+
+ + + + + + +
+ +

gamma-range="-30 30",监听范围为手机右侧向下旋转和向上旋转各 30 度。手机放平,起始状态指向为 0 度,手机右侧向下 30 度到达 30,向上 30 到达 -30。动画初始状态为动画执行到一半时的状态,手机右侧向下转动,gamma 值增大,方块向右移动;手机右侧向上转动,gamma 值减小,方块向左。 +

+
+ + + + + +``` diff --git a/components/mip-orientation-observer/example/mip-orientation-observer.html b/components/mip-orientation-observer/example/mip-orientation-observer.html new file mode 100644 index 00000000..b0f14b95 --- /dev/null +++ b/components/mip-orientation-observer/example/mip-orientation-observer.html @@ -0,0 +1,244 @@ + + + + + + + mip-orientation-observer + + + + + + +

以下所有描述中,0 度都是手机放平时的起始状态

+
+ +

alpha-range="330 30"

+

+ 监听范围: + 手机顺时针和逆时针旋转各 30 度,超过范围不触发事件,即动画无反应。 +

+

+ 操作: + 逆时针向左转 30 度到达 330,顺时针向右转 30 到达 30。手机初始状态为 0,是度数 [-30, 30] 的中间值,所以动画初始是执行到一半时的状态,逆时针转动手机,alpha 值增大,方块向右移动;顺时针转动手机,alpha 值减小,方块向左。 +

+
+ + + + + + +
+ +

beta-range="-30 30"

+

+ 监听范围: + 手机顶部向下旋转和向上旋转各 30 度 +

+

+ 操作: + 手机顶部向上 30 度到达 30,向下 30 到达 -30。手机初始状态为 0,所以动画出于执行到一半时的状态,手机顶部向上转动,beta 值增大,方块向右移动;手机顶部向下转动,beta 值减小,方块向左。 +

+
+ + + + + + +
+ +

gamma-range="-30 30"

+

+ 监听范围: + 手机右侧向下旋转和向上旋转各 30 度 +

+

+ 操作: + 手机右侧向下 30 度到达 30,向上 30 度到达 -30。动画初始状态为执行到一半时的状态,手机右侧向下转动,gamma 值增大,方块向右移动;手机右侧向上转动,gamma 值减小,方块向左。 +

+
+ + + + + + +
+ +

alpha-range="10 270"

+

+ 监听范围: + 手机逆时针旋转 10 度到 270 度 +

+

+ 操作: + 逆时针向左转 10 度到达 10,一直旋转直到到达 270。手机初始状态为 0,不在触发范围内,动画未触发,元素处于屏幕最左边。逆时针转动手机时,触发动画。alpha 值增大,方块向右移动;顺时针转动手机,alpha 值减小,方块向左。 +

+
+ + + + + + +
+ +

beta-range="30 -120"

+

+ 监听范围: + 手机顶部向上旋转 30 度到达 30,保持旋转可到达 -120 +

+

+ 操作: + 手机初始状态为 0,不在触发范围内,元素处于屏幕最左边。手机顶部向上转动,保持转动,beta 值增大,方块向右移动;手机顶部向下转动,beta 值减小,方块向左。 +

+

+ 解释: + 手机顶部向上旋转 30 度到达 30,一直保持方向再旋转到达 180 度(也是 -180 度,两点重合),接着旋转成负值 -180,由 -180 渐渐变大直到到达 -120。 +

+
+ + + + + + +
+ +

gamma-range="-60 30"

+

+ 监听范围: + 手机右侧向下旋转 30 度到和向上旋转 60度 +

+

+ 操作: + 手机顶部向上转动,保持转动,beta 值增大,方块向右移动;手机顶部向下转动,beta 值减小,方块向左。 +

+

+ 解释: + 手机右侧从 0 状态向下 30 度到达 30,从 0 状态向上 60 到达 -60。手机右侧向下转动,gamma 值增大,方块向右移动;手机右侧向上转动,gamma 值减小,方块向左。 +

+
+ + + + + + + + + + + diff --git a/components/mip-orientation-observer/mip-orientation-observer.js b/components/mip-orientation-observer/mip-orientation-observer.js new file mode 100644 index 00000000..ab1d22d9 --- /dev/null +++ b/components/mip-orientation-observer/mip-orientation-observer.js @@ -0,0 +1,105 @@ + +const {CustomElement, util, viewer} = MIP +const {warn} = util.log('mip-orientation-observer') +const {raf} = util.fn + +export default class MIPOrientationOberver extends CustomElement { + constructor (element) { + super(element) + this.alphaRange = undefined + this.betaRange = undefined + this.gammaRange = undefined + } + + firstInviewCallback () { + if (!window.DeviceOrientationEvent) { + warn('当前浏览器不支持 window.DeviceOrientationEvent') + return + } + + this.alphaRange = this.parseAttributes('alpha-range', this.alphaRange) + this.betaRange = this.parseAttributes('beta-range', this.betaRange) + this.gammaRange = this.parseAttributes('gamma-range', this.gammaRange) + this.duration = this.element.getAttribute('duration') || 0 + window.addEventListener('deviceorientation', event => { + raf(() => { + this.deviceOrientationHandler(event) + }) + }, true) + } + + parseAttributes (rangeName, originalRange) { + const providedRange = this.element.getAttribute(rangeName) + if (providedRange) { + const rangeArray = providedRange.trim().split(' ') + return [parseInt(rangeArray[0], 10), parseInt(rangeArray[1], 10)] + } + return originalRange + } + + deviceOrientationHandler (event) { + if (event instanceof DeviceOrientationEvent) { + const canTirggerAlpha = this.canTirgger('alpha', event.alpha) + if (canTirggerAlpha) { + this.triggerEvent('alpha', event.alpha, this.alphaRange) + } + const canTirggerBeta = this.canTirgger('beta', event.beta) + if (canTirggerBeta) { + this.triggerEvent('beta', event.beta, this.betaRange) + } + const canTirggerGamma = this.canTirgger('gamma', event.gamma) + if (canTirggerGamma) { + this.triggerEvent('gamma', event.gamma, this.gammaRange) + } + } + } + + canTirgger (name, value) { + let range = this[`${name}Range`] + return range && (range[0] < range[1] + ? value > range[0] && value < range[1] + : value > range[0] || value < range[1]) + } + + triggerEvent (eventName, eventValue, eventRange) { + // alpha 旋转角度取值范围为 [0, 360],beta 取值范围为 [-180, 180],gamma 旋转角度取值范围为 [-90, 90] + // 处理触发弧正常情况:alpha 不跨越 0 度和 360 重合点,beta 不跨越 -180 度和 180 度重合点 + // gamma 本身仅支持顺时针 [-90, 90] 的情况,不存在 0 或者 -180 度这样的跳跃点,不用特殊处理 + // 度数定义见 README 或者 html deviceorientation官方文档 + let arcLen = eventRange[1] - eventRange[0] + let percentValue = eventValue - eventRange[0] + let percent = percentValue / arcLen + let orientData = { + 'angle': Math.round(eventValue), + 'percent': percent, + 'seekToTime': Math.floor(percent * this.duration) + } + + // 处理触发弧包含跳跃点的情况,比如 + // alpha-range = "330 30" 或者 beta-range = "120 -120" + if (eventRange[0] > eventRange[1]) { + arcLen = eventRange[1] - eventRange[0] + 360 + if (eventName === 'alpha') { + percentValue = eventValue > eventRange[1] + // 当前角度在 330 度顺时针到 0 度时 + ? eventValue - eventRange[0] + // 当前角度在 0 度顺时针到 30 度时 + : eventValue + (360 - eventRange[0]) + } + if (eventName === 'beta') { + percentValue = eventValue < eventRange[1] + // 当前角度在 -180 度顺时针到 -120 度这样的范围时 + ? eventValue + (360 - eventRange[0]) + // 当前角度在 120 度顺时针到 -180 度时 + : eventValue - eventRange[0] + } + percent = percentValue / arcLen + orientData = { + 'angle': Math.round(eventValue), + 'percent': percent, + 'seekToTime': Math.floor(percent * this.duration) + } + } + viewer.eventAction.execute(eventName, this.element, orientData) + } +}