一个基于ACE框架canvas组件的指南针组件,它可以根据设备的罗盘传感器来显示设备的方向,也可以将罗盘角度回调至用户。
1.1 canvas组件调用
官方文档中提及了,不支持在onInit和onReady中调用,所以我将调用方法放在自定义组件生命周期的onPageShow中,onPageShow方法会走多次,所以我添加了一个标志位,控制canvas初始化方法直走一次,避免性能消耗。画布默认0点位于屏幕左上角,在初始化画布时,调用canvas.translate方法,将画布0点移动至屏幕中心,方便我们后续绘制。
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轴坐标,半径,开始角度,结束角度,是否逆时针方向绘制。
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轴减去字体宽度的一半,字体就居中显示了。
//文字列表
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)方法为线的终点绘制一条线。
//数字列表
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方法绘制出箭头,此方法在绘制文字“北”时调用即可。
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();
}
hml文件中为canvas组件添加style="transform : rotate({{ deg }}) , 罗盘数据改变时同步修改罗盘组件的旋转角度并将角度回调至用户。
//引入传感器包
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();
}
<!-- xxx.hml -->
<div class="canvas-layout" style="width : {{ compWidth }}; height : {{ compHeight }};">
<canvas id="compassCanvas" style="transform : rotate({{ deg }});" class="compass-canvas" ref="mycanvas"></canvas>
<canvas class="line-canvas" ref="linecanvas"></canvas>
</div>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。