HarmonyOS 鸿蒙Next 请提供一个canvas的demo

发布于 1周前 作者 caililin 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 请提供一个canvas的demo

画一个扇形,具体要求如下:扇形有中心点是固定的,扇形的两条边的长度也是固定而且相等,但是扇形的角度是可变化的,都手触摸到某一条边上,这条边可随手的方向移动,这时候扇形的角度可边变或变小,也就是这条变可随手360度旋转。另外,扇形里面填充绿色,颜色从里到外是渐变的,从深变浅,呈现阴影的效果,如果手按压着扇形阴影部分区域,手旋转或拖动,这时候整个扇形会随手围绕着中心点旋转。 


更多关于HarmonyOS 鸿蒙Next 请提供一个canvas的demo的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

参考以下demo: // xxx.ets

@Entry // 定义入口组件
@Component // 定义该结构体为组件
struct Index {
  private settings: RenderingContextSettings = new RenderingContextSettings(true) // 渲染上下文设置
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) // 创建一个2D画布上下文对象
  // 定义组件的状态变量,表示线条和点的位置、角度等
  @State lineOneX: number = 300
  @State lineOneY: number = 200
  @State lineTwoX: number = 200
  @State lineTwoY: number = 300
  @State pointX: number = 200
  @State pointY: number = 200
  @State radius: number = 100
  @State counterclockwise: boolean = false // 判断是否逆时针
  @State lineOneAngle: number = 0 // 第一条线的角度
  @State lineTwoAngle: number = Math.PI / 2 // 第二条线的角度
  currentOneAngle: number = 0 // 当前角度,用于计算
  currentTwoAngle: number = 0 // 当前角度,用于计算
  startX: number = 0 // 手势起始位置X坐标
  startY: number = 0 // 手势起始位置Y坐标
  judgeLocationFlag: number = 0 // 判断位置的标志

  build() {
    // 布局设置为垂直方向对齐,并且将画布居中
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      // 绘制画布
      Canvas(this.context)
        .width('100%')
        .height('100%')
        .onReady(() => {
          this.Draw() // 在画布准备好时绘制图形
        })
    }
    .width('100%')
    .height('100%')
    .gesture(
      PanGesture() // 监听滑动手势
        .onActionStart((event) => {
          // 手势开始时保存当前角度和起始位置
          this.currentOneAngle = this.lineOneAngle
          this.currentTwoAngle = this.lineTwoAngle
          this.startX = event.fingerList[0].localX
          this.startY = event.fingerList[0].localY
          // 计算坐标点和位置判断
          this.judgeLocationFlag = this.JudgeLocation(this.startX - this.pointX, this.startY - this.pointY)
        })
        .onActionUpdate((event) => {
          // 手势更新时,根据偏移量计算新的角度和坐标
          let currentAngle =
            this.AngleOfRotation(this.startX, this.startY, this.startX + event.offsetX, this.startY + event.offsetY)
          // 根据判断位置标志,更新两条线的角度和坐标
          if (this.judgeLocationFlag === 3) {
            this.lineOneAngle = currentAngle + this.currentOneAngle
            this.lineTwoAngle = currentAngle + this.currentTwoAngle
            this.lineOneX = this.radius * Math.cos(this.lineOneAngle) + this.pointX
            this.lineOneY = this.radius * Math.sin(this.lineOneAngle) + this.pointY
            this.lineTwoX = this.radius * Math.cos(this.lineTwoAngle) + this.pointX
            this.lineTwoY = this.radius * Math.sin(this.lineTwoAngle) + this.pointY
          } else if (this.judgeLocationFlag === 1) {
            this.lineOneAngle = currentAngle + this.currentOneAngle
            this.lineOneX = this.radius * Math.cos(this.lineOneAngle) + this.pointX
            this.lineOneY = this.radius * Math.sin(this.lineOneAngle) + this.pointY
          } else if (this.judgeLocationFlag === 2) {
            this.lineTwoAngle = currentAngle + this.currentTwoAngle
            this.lineTwoX = this.radius * Math.cos(this.lineTwoAngle) + this.pointX
            this.lineTwoY = this.radius * Math.sin(this.lineTwoAngle) + this.pointY
          }
          this.Draw() // 重新绘制图形
        })
    )
  }

  // 判断某个点相对于扇形的角度位置
  JudgeLocation(x: number, y: number) {
    let angle = Math.atan(y / x) // 计算点的角度
    if (x > 0 && y > 0) {
    } else if (x > 0) {
    } else if (y > 0) {
      angle = Math.PI + angle // 根据象限调整角度
    } else {
      angle = -Math.PI + angle
    }

    // 判断点是否在两条线之间,以及与线的角度关系
    if (x * x + y * y <= this.radius * this.radius) {
      let lineAngle: number = this.lineOneAngle - this.lineTwoAngle
      if (Math.abs(this.lineOneAngle - angle) < 0.15) {
        return 1 // 角度接近第一条线
      } else if (Math.abs(this.lineTwoAngle - angle) < 0.15) {
        return 2 // 角度接近第二条线
      } else if (lineAngle > Math.PI) {
        if (angle > this.lineOneAngle || angle < this.lineTwoAngle) {
          return 3 // 在两条线之间
        }
      } else if (lineAngle < -Math.PI) {
        if (angle < this.lineOneAngle || angle > this.lineTwoAngle) {
          return 3 // 在两条线之间
        }
      } else if (lineAngle < 0) {
        if (angle > this.lineOneAngle && angle < this.lineTwoAngle) {
          return 3 // 在两条线之间
        }
      } else {
        if (angle < this.lineOneAngle && angle > this.lineTwoAngle) {
          return 3 // 在两条线之间
        }
      }
    }
    return 0 // 不在扇形范围内
  }

  // 计算两点之间的旋转角度
  AngleOfRotation(startX: number, startY: number, endX: number, endY: number) {
    let x1 = startX - this.pointX
    let y1 = startY - this.pointY
    let x2 = endX - this.pointX
    let y2 = endY - this.pointY
    let res = Math.acos((x1 * x2 + y1 * y2) / ((Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2))) * (Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2))))) // 计算角度
    if (x1 * y2 - x2 * y1 > 0) {
      return res // 顺时针旋转
    } else {
      return -res // 逆时针旋转
    }
  }

  // 绘制图形(扇形)
  Draw() {
    this.context.clearRect(0, 0, 400, 400) // 清空画布
    this.context.beginPath()
    this.context.lineWidth = 3
    let grad = this.context.createRadialGradient(this.pointX, this.pointY, 0, this.pointX, this.pointY, 200) // 创建渐变色
    grad.addColorStop(0.0, '#8000ff00')
    grad.addColorStop(1.0, '#2000ff00')
    this.context.strokeStyle = grad
    // 绘制从中心到扇形边界的两条线
    this.context.moveTo(this.pointX, this.pointY)
    this.context.lineTo(this.lineOneX, this.lineOneY)
    this.context.moveTo(this.pointX, this.pointY)
    this.context.lineTo(this.lineTwoX, this.lineTwoY)
    this.context.stroke()
    // 开始绘制扇形路径
    this.context.beginPath()
    this.context.fillStyle = "#3000FF00"
    this.context.moveTo(this.pointX, this.pointY)
    if (this.lineOneAngle - this.lineTwoAngle < -Math.PI || this.lineOneAngle - this.lineTwoAngle > 0 && this.lineOneAngle - this.lineTwoAngle < Math.PI) {
      this.context.arc(this.pointX, this.pointY, this.radius, this.lineTwoAngle, this.lineOneAngle, this.counterclockwise) // 绘制圆弧
    } else {
      this.context.arc(this.pointX, this.pointY, this.radius, this.lineOneAngle, this.lineTwoAngle, this.counterclockwise) // 绘制圆弧
    }
    // 填充扇形区域
    this.context.closePath()
    this.context.fill()
  }
}

更多关于HarmonyOS 鸿蒙Next 请提供一个canvas的demo的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,以下是一个简单的Canvas绘制示例。这个示例展示了如何在自定义组件中使用Canvas进行基本的绘图操作。

// MyCanvasComponent.java
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.Canvas;
import ohos.agp.components.Component;
import ohos.agp.components.Paint;
import ohos.agp.graphics.Color;
import ohos.agp.window.Window;

public class MyCanvasComponent extends Component {
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(5);
        
        // 绘制矩形
        canvas.drawRect(50, 50, 200, 200, paint);
        
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        
        // 绘制圆形
        canvas.drawCircle(300, 150, 50, paint);
    }
    
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        requestFocusAbility(Window.FOCUS_ALL);
    }
}

在AbilitySlice中使用该组件:

// MainAbilitySlice.java
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.DirectionalLayout;

public class MainAbilitySlice extends AbilitySlice {
    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        DirectionalLayout layout = new DirectionalLayout(this);
        layout.addComponent(new MyCanvasComponent(this));
        setUIContent(layout);
    }
}

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部