# Compass **Repository Path**: chinasoft_ohos/compass ## Basic Information - **Project Name**: Compass - **Description**: 一个基于ACE框架canvas组件的指南针组件,它可以根据设备的罗盘传感器来显示设备的方向。 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-08-31 - **Last Updated**: 2023-06-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 指南针组件 ## 简介 一个基于ACE框架canvas组件的指南针组件,它可以根据设备的罗盘传感器来显示设备的方向,也可以将罗盘角度回调至用户。 ## 效果展示: ![compass](design-sketch/compass.jpg) ![compass](design-sketch/compass.gif) ## 使用示例 1. 下载项目代码,将compass文件夹添加至你的项目,如图所示: ![引用示例](design-sketch/引用示例.png) 2. 在要使用的位置通过element添加compass组件,设置组件宽高 ![hml代码](design-sketch/hml代码.png) ## 开发流程 ### 1. 绘制组件 1.1 canvas组件调用 官方文档中提及了,不支持在onInit和onReady中调用,所以我将调用方法放在自定义组件生命周期的onPageShow中,onPageShow方法会走多次,所以我添加了一个标志位,控制canvas初始化方法直走一次,避免性能消耗。画布默认0点位于屏幕左上角,在初始化画布时,调用canvas.translate方法,将画布0点移动至屏幕中心,方便我们后续绘制。 ![canvas获取](design-sketch/canvas获取.png) ``` onPageShow() { this.getWindowSize(); this.initCanvas(); }, initCanvas() { el = this.$element('compassCanvas'); ctx = el.getContext('2d', { antialias: true }); lin_el = this.$refs.linecanvas; lin_ctx = lin_el.getContext('2d', { antialias: true }); if (this.isFirst) { ctx.translate(this.width / 2, this.height / 2); lin_ctx.translate(this.width / 2, this.height / 2); this.drawCircle(this.outsideRadius, 'rgb(245,245,245)'); this.drawCircle(this.insideRadius, 'rgb(255,255,255)'); this.drawArcNumber(); this.drawArcText(); this.drawArcLine(); this.drawArrowLine(); } this.isFirst = false; }, getWindowSize() { this.outsideRadius = this.width / 2 - this.width / 10; this.insideRadius = this.outsideRadius - this.width / 6; }, ``` 1.2 绘制圆盘 罗盘组件分为两个圆,只是半径和填充颜色不同,所对应的参数分别为: X轴坐标,Y轴坐标,半径,开始角度,结束角度,是否逆时针方向绘制。 ```js drawCircle(radius, color) { ctx.beginPath(); ctx.fillStyle = color; ctx.strokeStyle = color; ctx.arc(0, 0, radius, 0, 6.28); ctx.fill(); } /** * Draws an arc on the canvas. * @param x X-coordinate of the center point of the arc. * @param y Y-coordinate of the center point of the arc. * @param radius Radius of the arc. * @param startAngle Start radian of the arc. * @param endAngel End radian of the arc. * @param anticlockwise Whether to draw the arc counterclockwise. * @devices tv, phone, tablet, wearable */ arc( x: number, y: number, radius: number, startAngle: number, endAngel: number, anticlockwise?: boolean ): void; ``` 1.3 绘制文字 四个文字,每旋转90°绘制一个,“北”是红色,在0°时,将fillStyle设置为红色,其他角度设置为黑色。因为ctx.fillText(x,y)方法的坐标是以字体左下角为准,所以我们用ctx.measureText(text).width方法得到字体的宽度,在绘制时将字体坐标X轴减去字体宽度的一半,字体就居中显示了。 ![image-compass](design-sketch/compass.png) ```js //文字列表 textArrs: ["北", "东", "南", "西"], //绘制文字 drawArcText() { ctx.beginPath(); this.fontSize = this.width / 20; ctx.font = this.fontSize + 'px' + ' sans-serif'; for (let i = 0; i < this.textArrs.length; i++) { if (i == 0) { ctx.fillStyle = 'rgb(255,0,0)'; this.drawTriangle(); } else { ctx.fillStyle = 'rgb(0,0,0)'; } let text = this.textArrs[i]; //测量字体宽度 let textWidth = ctx.measureText(text).width; ctx.fillText(text, -textWidth / 2, -this.width / 13); ctx.stroke(); ctx.rotate(90 * Math.PI / 180); } } ``` 1.4 绘制数字和分隔线 数字有12个,角度均分为30°,分隔线有24个,均分为15°,有四条分隔线较长,我们判断列表索引,绘制不同长度的分隔线。 ctx.moveTo(x,y)方法将画笔移动到某个点,以这个点为线的起点 ,ctx.lineTo(x,y)方法为线的终点绘制一条线。 ```js //数字列表 numberArrs: ['0', '30', '60', '90', '120', '150', '180', '210', '240', '270', '300', '330'], //绘制数字 drawArcNumber() { ctx.beginPath(); this.numberSize = this.width / 30; ctx.font = this.numberSize + 'px' + ' sans-serif'; for (var i = 0; i < this.numberArrs.length; i++) { if (i == 0) { ctx.fillStyle = 'red'; } else { ctx.fillStyle = '#AF000000'; ctx.fillStyle = 'rgb(0,0,0)'; } let number = this.numberArrs[i]; let textWidth = ctx.measureText(number).width; ctx.fillText(number, -textWidth / 2, -this.width / 4); ctx.rotate(30 * Math.PI / 180); } }, //绘制分隔线 drawArcLine() { ctx.strokeStyle = 'rgb(0,0,0)'; ctx.lineWidth = 0.3; for (var i = 0; i < 24; i++) { if (i == 0 || i == 6 || i == 12 || i == 18) { ctx.beginPath(); ctx.moveTo(0, this.insideRadius + (this.outsideRadius - this.insideRadius) / 2); ctx.lineTo(0, this.outsideRadius); } else { ctx.beginPath(); ctx.moveTo(0, this.insideRadius + (this.outsideRadius - this.insideRadius) / 2); ctx.lineTo(0, this.outsideRadius - this.width / 30); } ctx.stroke(); ctx.rotate(15 * Math.PI / 180); } } ``` 1.5 绘制箭头 用lineTo方法绘制出箭头,此方法在绘制文字“北”时调用即可。 ```js drawTriangle() { ctx.beginPath(); ctx.fillStyle = 'rgb(255,0,0)'; ctx.moveTo(0, -this.width / 5); ctx.lineTo(this.width / 25, -this.width / 5 + this.width / 15); ctx.lineTo(0, -this.width / 5 + this.width / 20); ctx.lineTo(-this.width / 25, -this.width / 5 + this.width / 15); ctx.closePath(); ctx.fill(); } ``` ## 2. 订阅罗盘数据 hml文件中为canvas组件添加style="transform : rotate({{ deg }}) , 罗盘数据改变时同步修改罗盘组件的旋转角度并将角度回调至用户。 ```js //引入传感器包 import sensor from '@system.sensor'; //订阅罗盘数据 subscribeCompassSensor() { let now = this; sensor.subscribeCompass({ success: function (ret) { //过滤角度小于1的变化 if (-now.angle - ret.direction > 1 || -now.angle - ret.direction < -1) { now.angle = -ret.direction; //将角度回调 now.$emit('compassAngle', { angle: now.angle }); } }, fail: function (data, code) { console.error('subscribe compass fail, code: ' + code + ', data: ' + data); }, }); }, //退出时解除罗盘订阅 onDestroy() { sensor.unsubscribeCompass(); } ``` ```html
```