HarmonyOS自定义UI之水波纹效果

开发 前端 OpenHarmony
任何东西都可以在生活中找到案例,我们要做水波纹效果,就想象一下,每个人应该都有把石头扔进进水里的经历,首先水波是从中心点的小圆慢慢放大为大圆,然后慢慢消失。

[[423871]]

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

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

https://harmonyos.51cto.com

概述

​ 当点击一个view时,然后一个水波纹就会从点击处扩散开来,模拟水波背景动效实现。

实现思路

​ 任何东西都可以在生活中找到案例,我们要做水波纹效果,就想象一下,每个人应该都有把石头扔进进水里的经历,首先水波是从中心点的小圆慢慢放大为大圆,然后慢慢消失,我们模拟的时候只需要画圆,通过Canvas. drawCircle(float x, float y, float radius, Paint paint); 方法来实现,通过一点点放大半径在根据变化速率设置透明度的方式实现。

效果如下:

HarmonyOS  自定义UI之水波纹效果-鸿蒙HarmonyOS技术社区

实现过程:

通过计算view的最大宽度 / 2为布局的最大半径,最小半径就是0然后给它一个插值动画让它动起来,每次点击就让它绘制圆,圆的半径为 (this.radiusMax * 插值动画比例 ),插值动画比例的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,代码如下:

  1. //初始化画笔 
  2. private void inint() { 
  3.      this.animationRunning = false
  4.      this.paint = new Paint(); 
  5.      this.paint.setAntiAlias(true); 
  6.      this.paint.setStyle(FILLANDSTROKE_STYLE); 
  7.      this.paint.setColor(new Color(Color.getIntColor("#0000FF")));//设置水波纹的颜色 
  8.  } 
  9.  
  10.  @Override 
  11.  public void onDraw(Component component, Canvas canvas) { 
  12.      float touchX = (float) this.getWidth() / 2; 
  13.      float touchY = (float) this.getHeight() / 2; 
  14.       this.paint.setAlpha(1 - 1* this.ripplePose);//透明都也是从0到1  因为ripplePose 是从0-1变换 
  15.      this.radiusMax = component.getWidth()/2; 
  16.         float radiusMax2 = component.getWidth()/4; 
  17.      //根据比例设置半径大小 
  18.       canvas.drawCircle(touchX, touchY,  this.radiusMax * this.ripplePose +radiusMax2, this.paint); 
  19.  } 

this.ripplePose的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,来绘制圆,这里我为什么加 radiusMax2是因为给他一个初始半径,让他不要从圆心开始,这样看起来就比较舒服了, 在点击的时候触发动画,代码如下:

  1. @Override 
  2. public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  3.  if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_DOWN) { // 一个手指按下 
  4.      this.downX = this.getTouchX(touchEvent, 0); 
  5.      this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); 
  6.  } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == POINT_MOVE) { // 一个手指移动 
  7.  
  8.  
  9.  } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_UP) { //一个手指抬起 
  10.      this.invalidate(); 
  11.  } 
  12.  return false

效果如下:

但是当我把水波纹应用到 PageSlider 中带有ListContainer 的时候,滑动的时候也会触发水波纹,所以需要解决事件冲突。

事件冲突解决

在添加了长按阴影效果后,在滑动PageSlider 页面的时候listContainer的子item也会触发点击事件,导致各种事件冲突,解决方法就是 在Touch事件中计算定义出各种事件,各个击破。

1.点击事件:抬起时间-按下时间 没有超过200ms

2.长按事件:按下超过200ms

3.长按滑动事件:移动监听里面计算 滑动距离超过20,时间超过200 没有抬起

4.短按滑动事件: 时间少于200ms 距离超过 200px 没有抬起

5.然后处理各种事件

另外发现,手机点击的时候移动事件会触发,而模拟器不会触发,这个也需要注意!

这样就完成了整个绘制过程,代码如下:

  1. /** 
  2.   * 触摸监听 
  3.   * 
  4.   * @param component view 
  5.   * @param touchEvent touchEvent 
  6.   * @return 是否消费 
  7.   */ 
  8. public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  9.  if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM1) { 
  10.      this.downX = this.getTouchX(touchEvent, 0); 
  11.      this.downY = this.getTouchY(touchEvent, 0); 
  12.      this.downTime = System.currentTimeMillis(); 
  13.      this.isDownPose = true
  14.      this.isSlide = false
  15.      isYinYing = false
  16.  } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM3) { 
  17.      float upx = this.getTouchX(touchEvent, 0); 
  18.      long theTime = System.currentTimeMillis(); 
  19.      int xjuli = (int) Math.abs(upx - downX); 
  20.      int timejuli = (int) Math.abs(theTime - this.downTime); 
  21.      if (isDownPose && timejuli > NUM200 && xjuli < NUM2 && !isSlide) { 
  22.          this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); 
  23.          myevenHandler.sendEvent(1, NUM250); 
  24.          this.isSlide = true
  25.          return true
  26.      } 
  27.      return true
  28.  } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM2) { 
  29.      float upx = this.getTouchX(touchEvent, 0); 
  30.      long upTimes = System.currentTimeMillis(); 
  31.      isDownPose = false
  32.      isYinYing = false
  33.      int xjuli = (int) Math.abs(upx - downX); 
  34.      if (!this.isDownPose && upTimes - this.downTime < NUM200 && xjuli < NUM5) { 
  35.          this.isDownPose = false
  36.          this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0)); 
  37.      } else if (!this.isDownPose && System.currentTimeMillis() - this.downTime > NUM250) { 
  38.          this.invalidate(); 
  39.      } else { 
  40.          this.isDownPose = false
  41.      } 
  42.      this.isDownPose = false
  43.  } 
  44.  return true
  45.   
  46. /** 
  47.   * 长按监听 
  48.   * 
  49.   * @param component view 
  50.   */ 
  51. @Override 
  52.  public void onLongClicked(Component component) { 
  53.      this.createAnimation(this.downX, this.downY); 
  54.      myevenHandler.sendEvent(1, NUM250); 
  55.      this.isSlide = true
  56.  } 
  57.   
  58.   
  59.    /** 
  60.   * 绘制 
  61.   * 
  62.   * @param var1 Component 
  63.   * @param var2 Canvas 
  64.   */ 
  65.   public void onDraw(Component var1, Canvas var2) { 
  66.      if (isYinYing) { 
  67.          this.paint.setAlpha(NUM03F); 
  68.          var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - 0 / NUM2F, this.paint); 
  69.      } else { 
  70.          if (this.getWidth() != 0 && this.getHeight() != 0) { 
  71.              this.radiusMax = (float) Math.sqrt(this.getWidth() 
  72.                      * this.getWidth() + this.getHeight() * this.getHeight()); 
  73.              if (this.rippleType != NUM2) { 
  74.                  this.radiusMax /= NUM2F; 
  75.              } 
  76.              this.radiusMax -= (float) this.ripplePadding; 
  77.              if (this.isCentered || this.rippleType == 1) { 
  78.                  this.touchX = (float) this.getWidth() / NUM2F; 
  79.                  this.touchY = (float) this.getHeight() / NUM2F; 
  80.              } 
  81.              this.paint.setAlpha(this.rippleAlpha - this.rippleAlpha * this.ripplePose); 
  82.              float var3 = 0.0F; 
  83.              if (this.rippleType == NUM1 && this.ripplePose > NUM04) { 
  84.                  this.paint.setStyle(Paint.Style.STROKE_STYLE); 
  85.                  var3 = this.radiusMax * this.ripplePose - this.radiusMax * (this.ripplePose - NUM04) / NUM06; 
  86.                  this.paint.setStrokeWidth(this.radiusMax * this.ripplePose 
  87.                          - this.radiusMax * (this.ripplePose - NUM04) / NUM06); 
  88.              } else { 
  89.                  this.paint.setStyle(Paint.Style.FILL_STYLE); 
  90.              } 
  91.              var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - var3 / NUM2F, this.paint); 
  92.          } 
  93.      } 
  94.  } 

效果如下:

完整项目代码

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

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

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2023-10-30 08:35:50

水波纹效果vue

2023-12-20 17:28:48

水波纹ArkUI动画开发

2024-05-31 08:43:31

2021-09-02 10:00:42

鸿蒙HarmonyOS应用

2021-09-15 10:19:15

鸿蒙HarmonyOS应用

2009-06-25 14:53:35

自定义UI组件JSF框架

2022-05-18 16:17:31

设备开发鸿蒙

2011-03-02 10:24:23

DashboardAndroid用户界面设计模板

2021-01-21 07:35:40

JenkinsUICSS

2022-03-21 15:19:27

鸿蒙UI组件ets自定义

2021-03-09 15:23:45

鸿蒙HarmonyOS应用开发

2022-04-24 15:17:56

鸿蒙操作系统

2013-03-28 10:58:30

自定义Android界android

2013-01-06 10:43:54

Android开发View特效

2024-05-30 08:23:37

ViewPager滑动效果接口

2015-02-12 15:33:43

微信SDK

2010-04-28 11:14:20

Windows 7桌面

2022-04-07 14:17:15

Harmonytoast组件鸿蒙

2023-02-20 15:20:43

启动页组件鸿蒙

2021-08-24 15:25:59

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号