HarmonyOS 自定義組件之可拖拽圓形進(jìn)度條
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
簡(jiǎn)介
在項(xiàng)目開(kāi)發(fā)中,我們經(jīng)常會(huì)用到自定義組件,此處分享一下HarmonyOS中JS如何利用canvas實(shí)現(xiàn)自定義組件之可拖拽圓形進(jìn)度條。
效果演示

實(shí)現(xiàn)思路
官方文檔:JS API參考-HarmonyOS應(yīng)用開(kāi)發(fā)
思路參考: 可拖拽圓形進(jìn)度條組件(支持移動(dòng)端))
這里并未采用官方文檔說(shuō)通過(guò)element引入到宿主頁(yè)面的方式;

采用上述過(guò)程發(fā)了bug:canvas首次渲染繪制的不顯示;
解決方案: 在頁(yè)面生命周期onShow的時(shí)候,通過(guò)js讓canvas繪制一次;
- onShow() {
- // Todo 繪制
- }
1.項(xiàng)目結(jié)構(gòu)

2.DragAcr初始化
定義構(gòu)造函數(shù),聲明繪制圓的參數(shù);
- export default class DragAcr {
- constructor(param) {
- this.initParam(param)
- this.draw(this.value)
- }
- initParam(param) {
- const {
- el,
- startAngle = 0,
- endAngle = 2,
- width = 252,
- innerColor = "red",
- outColor = "#08000000",
- innerLineWidth = 1,
- outLineWidth = 24,
- counterclockwise = false,
- slider = 8,
- color = ["#ffffff", "#0F75F3", "#54C8A5", "#FEDB00", "red"],
- sliderColor = "#ffffff",
- sliderBorderColor = "blue",
- value = 0,
- change = (v) => {
- console.log(v)
- },
- textShow = true,
- } = param;
- this.el = el;
- this.width = width;
- this.height = width;
- this.center = this.width / 2
- this.outLineWidth = outLineWidth;
- this.radius = this.width / 2 - this.outLineWidth / 2;
- // this.ctx = el.getContext("2d");
- this.ctx = this.el.getContext('2d', {
- antialias: true
- });
- this.startAngle = startAngle;
- this.endAngle = endAngle;
- this.innerColor = innerColor;
- this.outColor = outColor;
- this.innerLineWidth = innerLineWidth;
- this.counterclockwise = counterclockwise;
- this.slider = slider;
- this.color = color;
- this.sliderColor = sliderColor;
- this.sliderBorderColor = sliderBorderColor;
- this.value = value;
- this.textShow = textShow;
- this.change = change;
- this.isDown = false;
- }
3.DragAcr繪制
canvas的API參考:canvas組件-畫布組件
根據(jù)當(dāng)前進(jìn)度 分段式的繪制需要的各個(gè)小控件;
- // 繪圖
- draw(value) {
- console.log(TAG + ';draw value:' + value);
- if (value == undefined) {
- value = this.value;
- } else {
- this.value = value;
- }
- this.ctx.clearRect(0, 0, this.width, this.width);
- this.ctx.save();
- let startDeg = this.counterclockwise ? Math.PI * (2 - this.startAngle) : Math.PI * this.startAngle
- let endDeg = this.counterclockwise ? Math.PI * (2 - this.endAngle) : Math.PI * this.endAngle
- // 繪制背景圓
- this.ctx.beginPath();
- this.ctx.arc(this.center, this.center, this.radius, startDeg,
- endDeg, this.counterclockwise);
- this.ctx.strokeStyle = this.outColor;
- this.ctx.lineCap = "round";
- this.ctx.lineWidth = this.outLineWidth;
- this.ctx.stroke();
- let Deg = this.valToDeg(value)
- this.drawOne(startDeg, value);
- if (value > 25) {
- // 繪制可變圓弧
- this.drawTwo(value);
- }
- if (value > 50) {
- // 繪制可變圓弧
- this.drawThree(value);
- }
- if (value > 75) {
- this.drawFour(value)
- }
- // 繪制滑塊bar
- this.P = this.DegToXY(Deg)
- this.ctx.beginPath();
- this.ctx.moveTo(this.center, this.center,);
- this.ctx.arc(this.P.x, this.P.y, this.outLineWidth / 2, 0, Math.PI * 2, false); // 繪制滑塊內(nèi)側(cè)
- this.ctx.fillStyle = this.sliderBorderColor;
- this.ctx.fill();
- this.ctx.beginPath();
- this.ctx.moveTo(this.center, this.center);
- this.ctx.arc(this.P.x, this.P.y, this.slider, 0, Math.PI * 2, false); // 繪制滑塊
- this.ctx.fillStyle = this.sliderColor;
- this.ctx.fill();
- // 文字
- if (this.textShow) {
- this.ctx.font = '60px HarmonyHeiTi, HarmonyHeiTi-Medium';
- this.ctx.fillStyle = "#000000";
- this.ctx.textAlign = "center"
- this.ctx.textBaseline = "middle";
- this.ctx.fillText(this.value + "", this.center, this.center);
- }
- // this.drawLine();
- }
4.DragAcr手勢(shì)監(jiān)聽(tīng)
手勢(shì)按下,手勢(shì)移動(dòng),手勢(shì)抬起的事件處理
- OnMouseMove(evt) {
- if (!this.isDown) return;
- let evpoint = {};
- evpoint.x = this.getx(evt);
- evpoint.y = this.gety(evt);
- let point = this.spotchangeXY(evpoint);
- let deg = this.XYToDeg(point.x, point.y);
- // console.log(TAG + '; OnMouseMove deg XYToDeg ...' + deg);
- deg = this.counterclockwise ? deg : Math.PI * 2 - deg;
- // console.log(TAG + '; OnMouseMove deg...' + deg);
- let val = (deg / Math.PI - this.startAngle) / (this.endAngle - this.startAngle) * 100
- val = Math.round(val)
- // console.log(TAG + '; OnMouseMove val:' + val);
- if (val < 0) val = 100 + val;
- if (val <= 0) val = 0;
- if (val > 100) {
- if (this.value > 75) {
- val = 100;
- } else {
- val = val - 100;
- }
- }
- if (val > 75) {
- if (this.value < 25) {
- val = 0;
- }
- }
- // console.log(TAG + '; OnMouseMove val2:' + val);
- if (Math.abs(val - this.value) > 10) return;
- // console.log(TAG + '; OnMouseMove val:' + val);
- this.animate = requestAnimationFrame(this.draw.bind(this, val));
- if (this.value != Math.round(val)) {
- this.value = Math.round(val);
- this.change(this.value)
- }
- // console.log(TAG + '; OnMouseMove end...');
- }
- OnMouseDown(evt) {
- let range = 10;
- let X = this.getx(evt);
- let Y = this.gety(evt);
- let P = this.P
- let minX = P.x - this.slider - range;
- let maxX = P.x + this.slider + range;
- let minY = P.y - this.slider - range;
- let maxY = P.y + this.slider + range;
- if (minX < X && X < maxX && minY < Y && Y < maxY) { //判斷鼠標(biāo)是否在滑塊上
- this.isDown = true;
- } else {
- this.isDown = false;
- }
- console.log(TAG + 'OnMouseDown end...');
- }
- OnMouseUp() { //鼠標(biāo)釋放
- const _this = this
- cancelAnimationFrame(_this.animate);
- this.isDown = false
- console.log(TAG + 'OnMouseUp end...');
- }
5.使用方法
- index.hml文件
- <div class="container">
- <canvas ref="canvas2"
- style="width : 252px; height : 252px;"
- @touchstart="canvasTouchStart"
- on:touchmove="canvasTouchMove"
- on:touchend="canvasTouchEnd"></canvas>
- </div>
- index.js文件
- import DragAcr2 from './dragAcr2.js'
- export default {
- dragAcr2: undefined
- data: {
- // 出事化值
- reverb2: 30,
- }
- onShow() {
- // 首次繪制
- this.initDragAcr2();
- },
- // 觸摸事件
- canvasTouchStart(msg) {
- //console.log('dragAcr canvasTouchStart msg:' + msg);
- this.dragAcr2.OnMouseDown(msg);
- },
- canvasTouchMove(msg) {
- //console.log('dragAcr OnMouseMove msg:' + msg);
- this.dragAcr2.OnMouseMove(msg);
- },
- canvasTouchEnd(msg) {
- // console.log('dragAcr canvasTouchEnd msg:' + msg);
- this.dragAcr2.OnMouseUp(msg);
- },
- initDragAcr2() {
- const el = this.$refs.canvas2;
- if (this.dragAcr2 == undefined) {
- this.dragAcr2 = new DragAcr2({
- el: el,
- value: this.reverb2,
- change: (v) => {
- console.log(`value:${v}`)
- }
- })
- }
- }
- }
總結(jié)
1,目前在API6及一下手機(jī),canvas繪制時(shí)會(huì)是屏幕閃爍(API7遠(yuǎn)程模式無(wú)此現(xiàn)象);
2,無(wú)論什么語(yǔ)言,思路都是大體相同,繪制,手勢(shì),事件分發(fā)等。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):