#星光计划2.0# HarmonyOS 自定义组件之可拖拽圆形进度条 原创 精华

中软国际AIoT开发者社区
发布于 2021-12-29 08:53
浏览
4收藏

作者:彭为杰

【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】

简介

在项目开发中,我们经常会用到自定义组件,此处分享一下HarmonyOS中JS如何利用canvas实现自定义组件之可拖拽圆形进度条

效果演示

#星光计划2.0# HarmonyOS 自定义组件之可拖拽圆形进度条-鸿蒙开发者社区

实现思路

官方文档:JS API参考-HarmonyOS应用开发

思路参考: 可拖拽圆形进度条组件(支持移动端))

这里并未采用官方文档说通过element引入到宿主页面的方式;

#星光计划2.0# HarmonyOS 自定义组件之可拖拽圆形进度条-鸿蒙开发者社区

采用上述过程发了bug:canvas首次渲染绘制的不显示;

解决方案: 在页面生命周期onShow的时候,通过js让canvas绘制一次;

onShow() {
   // Todo 绘制
}

1.项目结构

#星光计划2.0# HarmonyOS 自定义组件之可拖拽圆形进度条-鸿蒙开发者社区

2.DragAcr初始化

定义构造函数,声明绘制圆的参数;

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组件-画布组件

根据当前进度 分段式的绘制需要的各个小控件;

// 绘图
 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); // 绘制滑块内侧
        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手势监听

手势按下,手势移动,手势抬起的事件处理

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) { //判断鼠标是否在滑块上
        this.isDown = true;
    } else {
        this.isDown = false;
    }
    console.log(TAG + 'OnMouseDown end...');
}

OnMouseUp() { //鼠标释放
    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}`)
                }
            })
        }
    }
}

总结

1,目前在API6及一下手机,canvas绘制时会是屏幕闪烁(API7远程模式无此现象);

2,无论什么语言,思路都是大体相同,绘制,手势,事件分发等;

代码地址

自定义组件之可拖拽圆形进度条

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-12-29 08:53:28修改
4
收藏 4
回复
举报
回复
    相关推荐