HarmonyOS鸿蒙Next中遮罩中开一个孔能点击下层组件怎么实现?

HarmonyOS鸿蒙Next中遮罩中开一个孔能点击下层组件怎么实现? 之前可能描述不够清楚,修改了一下。

下面测试代码,灰色遮罩遮住一部分下层组件,屏蔽下层点击,镂空区域需要允许点击下层。

最好是通用的,因为遮罩和下层的东西(其他人的模块)是改不了先后顺序和组件内容的

@Entry
@Component
struct Canvas02 {
  build() {
    Column() {
      Text('下层组件A').position({ x: 100, y: 100 }).onClick(() => console.log('下层组件A'))
      Text('下层组件B').position({ x: 100, y: 150 }).onClick(() => console.log('下层组件B'))
      Column().backgroundColor('#39373737').size({ width: 300, height: 300 }).onClick(() => console.log('遮罩组件'))
    }
  }
}

下图右边是镂空目标效果(改为圆角矩形),

点击下层组件B,要输出遮罩组件日志,

点击下层组件A,要输下层组件A日志

cke_17726.png


更多关于HarmonyOS鸿蒙Next中遮罩中开一个孔能点击下层组件怎么实现?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

13 回复

楼主你试试新建一个组件覆盖在下层组件A的位置,然后把点击事件搬过来,参考一下

@Entry
@Component
struct Index{
  build() {
    Column() {
      this.mainComponent()

      this.myComponent()
    }
  }

  @Builder
  mainComponent() {
    Column() {
      Text('下层组件A').position({ x: 100, y: 100 }).onClick(() => console.log('下层组件A'))
      Text('下层组件B').position({ x: 100, y: 150 }).onClick(() => console.log('下层组件B'))
      Column().backgroundColor('#39373737').size({ width: 300, height: 300 }).onClick(() => console.log('遮罩组件'))
    };
  }

  @Builder
  myComponent() {
    Column() {
      Text('下层组件A').position({ x: 0, y: -200 }).onClick(() => console.log('下层组件A'))
    }
    .width(100)
  }
}

更多关于HarmonyOS鸿蒙Next中遮罩中开一个孔能点击下层组件怎么实现?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


使用Canvas如何实现部分区域镂空的效果

使用 HitTestMode.Transparent 让 Canvas 的事件传递到底层。

@Entry
@Component
struct Index {
  @State offsetY: number = 100
  private contextSettings: RenderingContextSettings = new RenderingContextSettings(true);
  private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.contextSettings);
  private rect: Rectangle = {}

  build() {
    Stack() {
      Text('下层组件B')
        .fontSize(20)
        .fontColor('#333')
        .offset({ y: 200 })

      Text('下层组件A')
        .fontSize(20)
        .fontColor('#333')
        .padding({
          left: 50,
          right: 50,
          top: 30,
          bottom: 30
        })
        .offset({ y: this.offsetY })
        .onAreaChange((oldValue: Area, newValue: Area) => {
          const x = newValue.position.x
          const y = newValue.position.y
          const width = newValue.width
          const height = newValue.height
          this.rect = {
            x,
            y,
            width,
            height
          }
          this.draw()
        })
        .onClick(() => {
          this.offsetY += 10
          this.draw()
          console.log('下层组件-点击了 this.offsetY = ' + this.offsetY)
        })


      Canvas(this.ctx)
        .width('100%')
        .height('100%')
        .backgroundColor(Color.Transparent)
        .hitTestBehavior(HitTestMode.Transparent)
        .onReady(() => {

        })
    }
    .align(Alignment.Top)
    .width('100%')
    .height('100%')
    .backgroundColor('#ff0')
  }

  draw() {
    const x = this.rect.x as number
    const y = this.rect.y as number + this.offsetY
    const width = this.rect.width as number
    const height = this.rect.height as number
    const radius = 10

    this.ctx.reset()

    // 遮罩层
    this.ctx.fillStyle = '#40000000';
    this.ctx.beginPath();
    this.ctx.moveTo(0, 0);
    this.ctx.lineTo(0, this.ctx.height);
    this.ctx.lineTo(this.ctx.width, this.ctx.height);
    this.ctx.lineTo(this.ctx.width, 0);
    this.ctx.lineTo(0, 0);

    // 圆角矩形  API20 可用 roundRect
    this.ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
    this.ctx.lineTo(width - radius + x, y);
    this.ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);
    this.ctx.lineTo(width + x, height + y - radius);
    this.ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2);
    this.ctx.lineTo(radius + x, height + y);
    this.ctx.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI);
    this.ctx.lineTo(x, y + radius);
    this.ctx.fill();
    this.ctx.closePath();
  }
}

cke_119.png

点击下层组件B,要输出遮罩组件日志,

点击下层组件A,要输下层组件A日志 现在你这个代码点击遮罩层如果输出日志,A日志好像触发不了,

把Colunm与Text创建顺序调整一下就行,后创建的组件会覆盖先创建的组件

Column().backgroundColor('#39373737').size({ width: 300, height: 300 })
        Text('下层组件').position({ x: 100, y: 100 }).onClick(() => console.log('点击下层'))
          .width(100).height(100).backgroundColor("#fff")

如果不想改顺序在Text组件后面加上zIndex(vaule:number)也是可以解决的,当元素重叠时,具有较高z-index值的元素将显示在具有较低z-index值的元素之上

        Text('下层组件').position({ x: 100, y: 100 }).onClick(() => console.log('点击下层'))
          .width(100).height(100).backgroundColor("#fff").zIndex(999)
        Column().backgroundColor('#39373737').size({ width: 300, height: 300 })

通常遇到组件重叠()、层级控制可以用stack,这样你就不需要Position来定位到Column上面了

Stack() {
         Column()
          .backgroundColor('#39373737')
          .size({ width: 300, height: 300 })
        Text('上层组件')
          .width(100)
          .height(100)
          .backgroundColor("#fff")
      }

参考链接:Stack-行列与堆叠-ArkTS组件-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者

我之前可能没有描述清楚,编辑了问题,镂空区穿透,以外的区域不穿透,怎么实现啊?

@Entry
@Component
struct Canvas02 {
  build() {
    Stack({ alignContent: Alignment.TopStart }) {
      // 底层遮罩组件
      Column()
        .backgroundColor('#39373737')
        .size({ width: 300, height: 300 })
        .onClick(() => console.log('遮罩组件'))
        .hitTestBehavior(HitTestMode.Transparent) // 允许事件穿透
      // 上层交互组件
      Column() {
        Text('下层组件A')
          .position({ x: 100, y: 100 })
          .onClick(() => console.log('下层组件A'))

        Text('下层组件B')
          .position({ x: 100, y: 150 })
          .hitTestBehavior(HitTestMode.None) // 禁用事件拦截
      }
    }
  }
}

使用.hitTestBehavior(HitTestMode.Transparent)允许穿透,.hitTestBehavior(HitTestMode.None)禁止穿透

您好使用触摸测试可以实现,望采纳!

@Entry
@Component
struct Index {
  build() {
    Stack() {
      // 下层内容组件
      Column() {
        Text('底层内容区域').fontSize(20)
      }
      .width('100%')
      .height('100%')
      .backgroundColor(Color.White)
      .onClick(() => {
        this.getUIContext().getPromptAction().showToast({ message: '底层内容被点击' })
      })

      // 镂空遮罩层
      Column() {
        // 镂空区域(透明部分)
        Column()
          .width(200)
          .aspectRatio(1)
          .borderRadius('50%')
          .backgroundColor(Color.White)
          .position({ left: 20, top: 30 })
          .hitTestBehavior(HitTestMode.Transparent) // 关键:允许点击穿透
          .onClick(() => {
            this.getUIContext().getPromptAction().showToast({ message: '透明部分' })
          })
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#80000000')

    }
    .width('100%')
    .height('100%')
  }
}

为啥要搞这么复杂,你直接用 Stack 容器不就好了。
直接让按钮在上层,遮罩在下层,

我之前可能没有描述清楚,编辑了问题,镂空区穿透,以外的区域不穿透,怎么实现啊?

设置点击事件的行为为穿透

我之前可能没有描述清楚,编辑了问题,镂空区穿透,以外的区域不穿透,怎么实现啊?

在HarmonyOS Next中,可通过@State@Builder结合Stack组件实现遮罩开孔点击。使用Stack布局上层遮罩与下层内容,通过if条件控制遮罩层显示,在遮罩层使用RectCircle设置透明区域,并绑定onClick事件穿透至下层组件。示例代码片段:

@State isMaskVisible: boolean = true

build() {
  Stack() {
    // 下层组件
    Button('点击我')
      .onClick(() => {
        // 处理点击
      })

    // 遮罩层
    if (this.isMaskVisible) {
      Rect()
        .fill('#CC000000')
        .clip(new Circle({ radius: 50 }))
        .onClick(() => {
          this.isMaskVisible = false
        })
    }
  }
}

通过clip设置开孔形状,控制遮罩层显隐实现点击穿透。

在HarmonyOS Next中实现遮罩镂空并允许点击下层组件,可以通过以下方式:

  1. 使用Stack布局:将下层组件和遮罩层放在Stack容器中,遮罩层在上层。

  2. 遮罩层实现镂空

    • 使用RectShape组件绘制带镂空区域的遮罩
    • 通过Path命令定义镂空区域
    • 设置遮罩层背景色和透明度
  3. 事件处理

    • 遮罩层设置hitTestBehavior(HitTestMode.Transparent)允许点击事件穿透
    • 镂空区域对应位置的下层组件正常响应点击

示例代码:

@Entry
@Component
struct MaskExample {
  build() {
    Stack() {
      // 下层组件
      Column() {
        Text('下层组件A')
          .position({x: 100, y: 100})
          .onClick(() => console.log('下层组件A'))
        Text('下层组件B')  
          .position({x: 100, y: 150})
          .onClick(() => console.log('下层组件B'))
      }

      // 遮罩层
      Path()
        .width('100%')
        .height('100%')
        .commands('M0 0 H300 V300 H0 Z M50 50 H250 V250 H50 Z') // 外层全遮罩,内层镂空
        .fill(Color.Gray)
        .opacity(0.5)
        .hitTestBehavior(HitTestMode.Transparent) // 关键:允许点击穿透
    }
  }
}

这种方法不依赖组件顺序修改,通过事件穿透实现镂空区域点击下层组件。

回到顶部