应用开发

HarmonyOS自定义控件之JS进度条

时间:2010-12-5 17:23:32  作者:IT科技   来源:IT科技  查看:  评论:0
内容摘要:想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com前言在我们日常开发的里面,很多场景经常会用到进度条,而系统提供的进度条样式又无法满足我们的

想了解更多内容,自之请访问:

和华为官方合作共建的定义度条鸿蒙技术社区

https://harmonyos.51cto.com

前言

在我们日常开发的里面,很多场景经常会用到进度条,控件而系统提供的自之进度条样式又无法满足我们的使用,这时候我们就需要自定义一个进度条,定义度条自定义JS进度条主要涉及以下知识点:

如何自定义组件及引用 如何自定义绘制图形(draw) 如何创建并执行动画(animation) 如何设置自定义组件的控件参数(setter) 如何监听自定义组件的参数(getter)

效果演示

代码实现

如何自定义组件及引用

1.Js自定义组件,只需要新创建一个包,自之直接在里面编写界面,定义度条样式,控件逻辑代码即可。自之如果需要使用该组件,定义度条将其完整拷贝到自己的控件项目结构下进行引用即可。我们自定义一个Progress进度条控件,自之项目结构如下图:

2.使用的定义度条时候,我们需要给自定义组件进行标签声明,控件然后就可以使用该标签了:

<index.hml>

// src表示我们引用的自定义组件的文件,name表示我们给该自定义组件声明一个标签名 <element src="../progress/progress.hml" name="progress-bar"></element> <div class="container">     // 声明之后,就可以使用这个自定义的标签了     <progress-bar></progress-bar>     ... </div> 

如何自定义绘制图形

说到自定义绘制,自然离不开canvas,首先我们给自定义组件增加一个<canvas>标签,并在JS文件中描述绘制:

<progress.hml>

<stack class="frame-layout">     <canvas id="progress-bar" class="progress-bar" ontouchmove="onTouchEvent"></canvas>     <text if="{ {  display }}" class="progress-bar">{ {  progressText }}</text> </stack> 

在JS中定义一个draw方法,并且传入一个CanvasRenderingContext2D参数,这个参数我们可以理解为canvas + paint,源码下载所有绘制都是通过它进行调用:

<progress.js>

draw(ctx) {       this.display = true      ctx.lineWidth = this.circleWidth      ctx.lineCap = round      // ctx可以理解为canvas + paint      ctx.clearRect(0, 0, this.width, this.height) // 会闪屏,系统渲染问题      ctx.save() // save1      ctx.translate(this.width / 2, this.height / 2)      // draw background      ctx.beginPath()      ctx.strokeStyle = this.backgroundColor      ctx.arc(0, 0, 100, 0, 2 * Math.PI) // r = 100, 参数是弧度,角度 = 弧度 / PI * 180      ctx.stroke() // 绘制      ctx.closePath()      // draw progress      ctx.save()      ctx.rotate(-90 / 180 * Math.PI)      ctx.beginPath()      ctx.strokeStyle = this.progressColor      ctx.arc(0, 0, 100, 0, this.angle / 180 * Math.PI) // r = 100, 参数是弧度,角度 = 弧度 / PI * 180      ctx.stroke() // 绘制      ctx.closePath()      ctx.restore()      ctx.restore() // save1      this.notifyChanged()  } 

这部分逻辑并不复杂,都有注释,就是绘制一个圆环背景,然后根据进度参数在圆环上绘制一个圆弧进度,相信做过自定义控件的同学都能够非常熟悉。

如何创建并执行动画

1.首先我们需要在init的时候创建一个动画对象,并且设置好初始的动画参数:

<progress.js>

onInit() {        // 动画参数(具体参数类型和参数说明参考官方文档)       var options = {            duration: this.animDuration, // 动画时长           direction: normal, // 播放模式           easing: linear, // 差值器           fill: forwards, // 动画结束后状态           iterations: 1, // 执行次数           begin: 0, // 起始值           end: 360.0 // 终止值       };       var _this = this       this.animator = Animator.createAnimator(options)       this.animator.onframe = function (value) {            // 动画每一帧回调,类似我们熟悉的onAnimateUpdate回调           _this.angle = value           // 刷新绘制           _this.draw(_this.ctx)       }       ...   },  

2.接着我们需要在特定的时候开启动画,例如我们在接收到外部传进来的进度参数后,我们需要更新动画的起始值和终止值,并且开始执行动画:

<progress.js>

onProgressChanged(oldV, newV) {        console.log("onProgressChanged from:" + oldV + " to: " + newV)      this.initWidget()      // 进度值范围限定为[0, 1]      if (oldV >= 1) {           oldV = 1      }      if (newV >= 1) {           newV = 1      }      // 更新动画的起始和终止参数      var options = {           duration: this.animDuration,          direction: alternate-reverse,          easing: linear,          fill: forwards,          iterations: 1,          begin: oldV * 360,          end: newV * 360      };      this.animator.update(options)      // 开始执行动画      this.animator.play()  }, 

如何设置自定义组件的参数

1.我们自定义组件,并不能像之前一样简单的暴露个公开方法给外部调用。由于其数据驱动的设计,我们可以定义一些自定义属性参数,当外部修改参数时我们就可以接收到信息进行主动动作(setter):

<progress.js>

props: [       progress, // 进度       backgroundColor, // 圆环背景颜色       progressColor // 进度前景颜色   ],   ... 

2.监听这些对外暴露的亿华云计算属性值变化(listener):

<progress.js>

onInit() {       ...      // 监听自定义属性值变化      this.$watch(progress, onProgressChanged)      this.$watch(backgroundColor, onBackgroundChanged)      this.$watch(progressColor, onForegroundChanged)      ...  },     // backgroundColor变化时会触发该回调  onBackgroundChanged(oldV, newV) {       this.backgroundColor = newV  },  // progressColor变化时会触发该回调  onForegroundChanged(oldV, newV) {       this.progressColor = newV  },  // progress变化时会触发该回调  onProgressChanged(oldV, newV) {       console.log("onProgressChanged from:" + oldV + " to: " + newV)      this.initWidget()      if (oldV >= 1) {           oldV = 1      }      if (newV >= 1) {           newV = 1      }      var options = {           duration: this.animDuration,          direction: alternate-reverse,          easing: linear,          fill: forwards,          iterations: 1,          begin: oldV * 360,          end: newV * 360      };      this.animator.update(options)      this.animator.play()  },  

3..外部设置参数,当外部改变这些参数时,我们自定义组件内部的回调方法就会触发,并执行刷新逻辑:

<index.hml>

<element src="../progress/progress.hml" name="progress-bar"></element> <div class="container">     <progress-bar background-color="#c2f135"                   progress-color="#6bfc33"                   progress="{ {  progress }}">     </progress-bar>     ... </div> 

如何监听自定义组件的参数

上面我们说到了外部如何改变自定义组件内部的属性,本质上就是一个典型观察者模式。同理,外部调用者需要监听我们自定义组件的参数变化,也是通过这种方式:

1.首先我们在自定义组件中需要定义一个被观察者对象(key),并且在该对象值变化时对外发送消息:

<progress.js>

notifyChanged() {         // currentAngle, currentProgress就是被观察者对象,key-value结构,value就是我们对外发送的值        // 注意:驼峰命名        this.$emit("currentAngle", this.angle)        this.$emit("currentProgress", Math.ceil(this.angle / 3.6) / 100)        this.progressText = Math.ceil(this.angle / 3.6) + "%"    }, 

2.外部使用者需要注册监听回调方法,对被观察者对象(key)进行监听:

 <index.hml>

<element src="../progress/progress.hml" name="progress-bar"></element> <div class="container">     // 通过@current-angle和@current-progress进行该参数的监听,注意参数前加"@",并且参数根据驼峰命名方式拆分单词,每个词语用"-"隔开     <progress-bar background-color="#c2f135"                   progress-color="#6bfc33"                   progress="{ {  progress }}"                   @current-angle="onAngleChanged"                   @current-progress="onProgressChanged">     </progress-bar>     ...  </div>

<index.js>

// 当自定义组件内部的 currentAngle, currentProgress变化时,会触发下面的回调方法通知外部使用者    onAngleChanged(angle) {         console.log("onAngleChanged: " + angle.detail)    },    onProgressChanged(progress) {         console.log("onProgressChanged: " + progress.detail)    } 

其他关键点

1.<canvas>标签的绘制内容默认是不显示的,我们可以在初始化的时候监听首帧回调,网站模板主动进行刷新一次:

<progress.js>

onInit() {      ...     // 监听首帧,触发首次绘制,类似attachToWindow的触发时机     requestAnimationFrame(function () {          _this.initWidget()         _this.draw(_this.ctx)     }) }, 

2.自定义组件如何获取宽高信息,在API6+系统已经提供相关的方法可以进行获取,类似onSizeChanged中读取宽高信息:

<progress.js>

initWidget() {       console.log("init widget")      if (this.ctx === null) {           // 获取标签元素          let widget = this.$element(progress-bar);          this.ctx = widget.getContext(2d, {               antialias: true          })          // 获取宽高,并计算出绘制圆环的宽高,中心点,半径信息          this.width = widget.getBoundingClientRect().width          this.height = widget.getBoundingClientRect().height          this.centerX = widget.getBoundingClientRect().left + this.width / 2          this.centerY = widget.getBoundingClientRect().top + this.height / 2          console.log("canvas size = " + this.width + ", " + this.height)          console.log("canvas center = " + this.centerX + ", " + this.centerY)      }  }, 

3.canvas画布和我们通常理解的是不同的,它是存在绘制缓存的,所以每一帧刷新时,我们需要在绘制前先清空之前的绘制内容。目前鸿蒙清空画布时会概率出现闪屏问题。

以上就是实现一个自定义JS进度条的核心代码了,源代码:JsProgress

想了解更多内容,请访问:

和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap