OpenHarmony开发者论坛

标题: 妙用Sensor Server Kit开发感知光影效果 [打印本页]

作者: hellokun    时间: 前天 17:57
标题: 妙用Sensor Server Kit开发感知光影效果
[md]# 1 简介

传感器在APP较为常用,例如开屏摇一摇广告是结合陀螺仪实现的。本篇文章将讲解Sensor Service Kit(传感器服务),通过本篇文章你将学到:

- 如何获取光线、加速度、陀螺仪数据。
- 如何实现丰富的震动效果
- 如何将UI基础组件与光线、陀螺仪传感器数据联动,实现感知光影效果
- 注:开发者手机不支持光线、陀螺仪传感器
  ![sensor2.gif](![](https://dl-harmonyos.51cto.com/i ... c32a48873140d16.gif)

)

# 2 环境搭建

我们首先需要完成OpenHarmony应用开发环境搭建,可参考([开发一个BLE低功耗蓝牙调试助手(一)连接蓝牙服务设备-华为开发者论坛]([https://developer.huawei.com/con ... 0109140870620153026](https://developer.huawei.com/con ... 0109140870620153026)))中的第二章进行操作。

# 3 代码结构解读

本篇文档只对\*\*核心代码\*\*进行讲解,全部代码可看仓库地址[HelloKun - Gitee.com]([https://gitee.com/ckunkun](https://gitee.com/ckunkun))。

\`\`\`c
. entry/src
|-- common // 常用工具库
|   |-- CommonConstants.ets
|   |-- Logger.ets
|   \`-- PermissionUtil.ets
|-- entryability
|   \`-- EntryAbility.ets // 入口,设置全屏
|-- pages
|   |-- ControlBox.ets   // 蓝牙体感手柄页面
|   |-- IconModel.ets    // 图标类
|   |-- Index.ets        // 首页
|   \`-- LingDongIcon.ets // 感知光影页面
\`-- services // 蓝牙服务
|-- BLEGattClientManager.ets
\`-- BLEScanManager.ets
\`\`\`

# 4 构建应用主界面

首页面使用Navigation组件实现布局,先将需要展示页面单独存放在首页列表中,页面的title与icon在IconModel.ets进行定义。

\`\`\`js
// IconModel.ets 定义需要的跳转页面title与icon
const SensorSources: IconItem[] = [
{ title: '灵动光影', image: \$r('app.media.calendar') },
{ title: '灵动手柄', image: \$r('app.media.xbox') },
// ...
]
\`\`\`

首页的布局使用Navigation组件实现,具体可参考该系列教程。只需要在PageMap中新增需要跳转的页面即可。

\`\`\`js
// Index.ets
// ...
// 1. 导航页面列表
PageMap(name: string) {
if (name === "LingDongIcon") {
LingDongIcon();
}
else if (name === "ControlBox") {
ControlBox();
}
// ...
}
\`\`\`

实现的效果如下:

![sensor1.png](![](https://dl-harmonyos.51cto.com/i ... 08e230cd11043e4.png)

?x-oss-process=image/resize,w\_307,h\_627)

# 5 使用Sensor服务

使用传感器主要步骤如下:

- 1)导入模块

\`\`\`js
import { sensor } from '@kit.SensorServiceKit';
\`\`\`

- 2)获取相应权限,本篇文章涉及的、需要向用户声请的权限有:

\`\`\`js
export const permissions: Array<Permissions> =
['ohos.permission.ACCESS\_BLUETOOTH',
"ohos.permission.ACCELEROMETER",
"ohos.permission.GYROSCOPE",
"ohos.permission.VIBRATE"];
\`\`\`

需要在module.json中声明的权限有:

\`\`\`json
"requestPermissions": [
{
"name": " ohos.permission.ACCESS\_BLUETOOTH",
"reason": "\$string:app\_name",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.ACCELEROMETER",
// ...
},
{
"name": "ohos.permission.GYROSCOPE",
// ...
},
{
"name": "ohos.permission.ORIENTATION",
// ...
},
{
"name": "ohos.permission.VIBRATE",
// ...
},
{
"name": "ohos.permission.AMBIENT\_LIGHT",
// ...
}
]
\`\`\`

- 3)了解传感器及其ID

每个传感器都有对应的ID,其定义在@ohos.sensor.d.ts中

\`\`\`c
enum SensorId {
ACCELEROMETER = 1,
GYROSCOPE = 2,
AMBIENT\_LIGHT = 5,
MAGNETIC\_FIELD = 6,
BAROMETER = 8,
HALL = 10,
PROXIMITY = 12,
HUMIDITY = 13,
ORIENTATION = 256,
GRAVITY = 257,
LINEAR\_ACCELEROMETER = 258,
ROTATION\_VECTOR = 259,
// ...
}
\`\`\`

设备并非具备全部传感器能力,在使用传感器前,可以先扫描确定当前设备拥有的传感器列表,实现如下:

\`\`\`js
sensor.getSensorList((error: BusinessError, data: Array<sensor.Sensor>) => {
if (error) {
console.info('getSensorList failed');
} else {
console.info('getSensorList success');
for (let i = 0; i < data.length; i++) {
console.info(JSON.stringify(data*));
}
}
});
\`\`\`

![](C:\\Users\\jingwen liang\\Downloads\\sensor2.png)

- 4)注册监听与结束监听。可以通过on()和once()两种接口监听传感器的调用结果。

通过on()接口,实现对传感器的持续监听,传感器上报周期interval设置为100000000纳秒。

\`\`\`typescript
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {     console.info("Succeeded in obtaining data. x: " + data.x + " y: " + data.y + " z: " + data.z);}, { interval: 100000000 });
\`\`\`

通过once()接口,实现对传感器的一次监听。

\`\`\`typescript
sensor.once(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {    console.info("Succeeded in obtaining data. x: " + data.x + " y: " + data.y + " z: " + data.z);});
\`\`\`

通过off()接口,实现取消持续监听传感器数据。适当使用传感器,能更好的控制设备功耗。

\`\`\`typescript
sensor.off(sensor.SensorId.ACCELEROMETER);
\`\`\`

## 5.1 获取加速度数据

加速度传感器数据有两类,分别是校准数据(ACCELEROMETER)和原始数据(ACCELEROMETER\_UNCALIBRATED)。示例中使用校准数据,数据说明如下:

| 传感器类型    | 描述         | 说明                                                                                | 主要用途       |
| ------------- | ------------ | ----------------------------------------------------------------------------------- | -------------- |
| ACCELEROMETER | 加速度传感器 | 测量三个物理轴(x、y 和 z)上,施加在设备上的加速度(包括重力加速度),单位 : m/s² | 检测运动状态。 |

设置了一个定时器,每隔一段时间就持续监听x轴的加速度(其他传感器同样处理)数据,并给赋值给变量angle\_X,后续进行使用。

\`\`\`js
@State accelerometer\_X:number = 0;
setInterval(()=>{
try {
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
this.accelerometer\_X = data.x; // data.y data.z包含其他两个轴的加速度
}, { interval: 100000000 });

```
  } catch (error) {
    let e: BusinessError = error as BusinessError;
    console.error(\`Failed to invoke on. Code: \${e.code}, message: \${e.message}\`);
  }
console.info(TAG,'accelerometer\_X:'+this.accelerometer\_X.toString())
},500)
```

\`\`\`

## 5.2 获取陀螺仪数据

### 1)角速度获取

陀螺仪传感器角速度数据分为是否校准两类,这里获取校准的GYROSCOPE数据,数据说明如下:

| 传感器类型 | 描述         | 说明                                                            | 主要用途           |
| ---------- | ------------ | --------------------------------------------------------------- | ------------------ |
| GYROSCOPE  | 陀螺仪传感器 | 测量三个物理轴(x、y 和 z)上,设备的旋转角速度,单位 : rad/s。 | 测量旋转的角速度。 |

继续在定时器中补充获取y轴角速度的监听程序:

\`\`\`js
@State gyroscope\_Y:number = 0;
setInterval(()=>{
try {
//...
sensor.on(sensor.SensorId.GYROSCOPE, (data: sensor.GyroscopeResponse) => {
this.gyroscope\_Y = data.y \* 10
}, { interval: 100000000 });

```
  } catch (error) {
    let e: BusinessError = error as BusinessError;
    console.error(\`Failed to invoke on. Code: \${e.code}, message: \${e.message}\`);
  }
  // ...
  console.info(TAG,'gyroscope\_Y:'+this.gyroscope\_Y.toString())
},500)
```

\`\`\`

### 2)获取角度

陀螺仪传感器可以计算出设备在三个轴的旋转角度,在应用层我们可以直接获取到旋转角度。

| 传感器类型  | 描述       | 说明                                                           | 主要用途                      |
| ----------- | ---------- | -------------------------------------------------------------- | ----------------------------- |
| ORIENTATION | 方向传感器 | 测量设备围绕所有三个物理轴(z、x、y)旋转的角度值,单位:rad。 | 用于测量屏幕旋转的3个角度值。 |

监听到的三个物理轴(z、x、y)旋转的角度值分别为alpha、beta、gamma,获取角度的程序如下:

\`\`\`js
@State orientation\_X:number = 0;
@State orientation\_Y:number = 0;
@State orientation\_Z:number = 0;
setInterval(()=>{
try {
//...
sensor.on(sensor.SensorId.ORIENTATION, (data: sensor.OrientationResponse) => {
this.orientation\_X = data.beta
this.orientation\_Y = data.gamma
this.orientation\_Z = data.alpha
}, { interval: 100000000 });

```
  } catch (error) {
    let e: BusinessError = error as BusinessError;
    console.error(\`Failed to invoke on. Code: \${e.code}, message: \${e.message}\`);
  }
  // ...
  console.info(TAG,'beta:'+this.orientation\_X.toString())
  console.info(TAG,'gamma:'+this.orientation\_Y.toString())
  console.info(TAG,'alpha:'+this.orientation\_Z.toString())
},500)
```

\`\`\`

## 5.4 获取光线传感器

光线传感器通常用来调节亮度,不过我们可以监听其数值,用于应用程序组件属性控制。

| 传感器类型     | 描述         | 说明                              | 主要用途                                   |
| -------------- | ------------ | --------------------------------- | ------------------------------------------ |
| AMBIENT\_LIGHT | 环境光传感器 | 测量设备周围光线强度,单位:lux。 | 自动调节屏幕亮度,检测屏幕上方是否有遮挡。 |

光线传感器返回的数据类型为LightResponse, 包含强读、色温、红外强度(可以用来做出行建议),完整定义如下:

\`\`\`js
interface LightResponse extends Response {
/\*\*
\* Indicates light intensity, in lux.
\* @type { number }
\* @syscap SystemCapability.Sensors.Sensor
\* @since 8
\*/
intensity: number;
/\*\*
\* Indicates color temperature, in kelvin.
\* @type { ?number }
\* @syscap SystemCapability.Sensors.Sensor
\* @since 12
\*/
colorTemperature?: number;
/\*\*
\* Indicates infrared luminance, in cd/m2.
\* @type { ?number }
\* @syscap SystemCapability.Sensors.Sensor
\* @since 12
\*/
infraredLuminance?: number;
}
\`\`\`

继续在定时器中补充获取环境光线强度的监听程序:

\`\`\`js
@State Light\_Lux:number = 0;
setInterval(()=>{
try {
// ...
sensor.on(sensor.SensorId.AMBIENT\_LIGHT, (data: sensor.LightResponse) => {
this.Light\_Lux = data.intensity/100;
}, { interval: 100000000 });

```
  } catch (error) {
    let e: BusinessError = error as BusinessError;
    console.error(\`Failed to invoke on. Code: \${e.code}, message: \${e.message}\`);
  }
  // ...
  console.info(TAG,'Light\_Lux:'+this.Light\_Lux.toString())
},500)
```

\`\`\`

## 5.5 震动控制

HarmonyOS NEXT提供了丰富的震动控制接口,不仅预设了丰富的预置震动效果,开发者还可以自定义震动。具体可参考:[振动开发指导-振动-Sensor Service Kit(传感器服务)]([https://developer.huawei.com/con ... rator-guidelines-V5](https://developer.huawei.com/con ... rator-guidelines-V5))。我们使用预置的震动效果,开发步骤如下:

- 1)引入模块

\`\`\`js
import { vibrator } from '@kit.SensorServiceKit';
\`\`\`

- 2)安装预置震动效果开启震动,可先查询振动效果是否被支持,再调用振动接口,下面是开启一次震动的实现:

\`\`\`js
OpenPreSetVibrator()
{
try {
// 查询是否支持'haptic.effect.soft'
vibrator.isSupportEffect('haptic.effect.soft', (err: BusinessError, state: boolean) => {
if (err) {
console.error(\`Failed to query effect. Code: \${err.code}, message: \${err.message}\`);
return;
}
console.info('Succeed in querying effect');
if (state) {
try {
// 触发马达振动
vibrator.startVibration({
type: 'preset',
effectId: 'haptic.effect.soft',
count: 1,
intensity: 50,
}, {
usage: 'unknown'
}, (error: BusinessError) => {
if (error) {
console.error(\`Failed to start vibration. Code: \${error.code}, message: \${error.message}\`);
} else {
console.info('Succeed in starting vibration');
}
});
} catch (error) {
let e: BusinessError = error as BusinessError;
console.error(\`An unexpected error occurred. Code: \${e.code}, message: \${e.message}\`);
}
}
})
} catch (error) {
let e: BusinessError = error as BusinessError;
console.error(\`An unexpected error occurred. Code: \${e.code}, message: \${e.message}\`);
}
}
\`\`\`

# 6 感知光影

上一章节获取到了加速度、角速度、角度、光强度数据,接下来结合常用组件,实现光影感知的效果。

1)首先使用.shadow属性将图标和文字添加阴影效果:

- 光的强度控制阴影的可变半径;
- X轴加速度控制X轴可变偏移范围;
- y轴转动角度控制Y轴可变偏移范围;

\`\`\`js
// LingDongIcon.ets
// 光影标题
Text(this.message)
.textShadow({radius:10-this.Light\_Lux/2,type:ShadowType.COLOR,color:'#000000',
offsetX:this.widthIcon/2+this.accelerometer\_X,offsetY:40+this.gyroscope\_Y})
// 光影图标
Image(service.image) .margin({top:'15%'})
.backgroundBlurStyle(BlurStyle.Thick)
.height(this.widthIcon)
.width(this.widthIcon)
.borderRadius(this.widthIcon / 2)
.borderWidth(0)
.borderColor('#A8D18D')
.shadow({
radius: 40 - this.Light\_Lux / 2,
type: ShadowType.COLOR,
color: '#000000',
offsetX: this.accelerometer\_X,
offsetY: 20 + this.gyroscope\_Y / 2
})
\`\`\`

2)使用Grid模拟桌面布局,更直观的查看感知光影的效果。实现如下:

\`\`\`js
// LingDongIcon.ets
@Component
struct ShowIcon{
// ...
build(){
Column() {
Column() {
Grid() {
ForEach(IconSources, (service: IconItem, index: number) => {
GridItem() {
Column({ space: 15 }) {
Image(service.image) .margin({top:'15%'}) // 光影图标
// ...
Text(service.title)
}
}
})
}.height('100%')
.width('100%')
.rowsTemplate(('1fr 1fr 1fr 1fr') as string)
.columnsTemplate(('1fr 1fr 1fr 1fr') as string)
.supportAnimation(true)
}.height('100%')
}.justifyContent(FlexAlign.Center)
}
}
\`\`\`

实现效果演示:

- 1)当手机静置时,图标和文字的阴影较大且位置固定
- 2)当手机被拿起时,图标和文字的阴影会随着握持的姿势变动

![sensor2.gif](![](https://dl-harmonyos.51cto.com/i ... 7ceb476f1ecf368.gif))

- 3)光线强度不同的环境中图标阴影会变淡或者变深

![light.gif](![](https://dl-harmonyos.51cto.com/i ... 919c4927ebe4182.gif))

# 总结

本篇综合介绍了如何使用光线、加速度、陀螺仪和震动等传感器,并实现感知光影的交互体验。我们可以发挥想象,如结合蓝牙、角度、震动、加速度可以将手机变为无线体感手柄!*
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5