HarmonyOS 鸿蒙Next中如何实现渐变毛玻璃(模糊)效果?

HarmonyOS 鸿蒙Next中如何实现渐变毛玻璃(模糊)效果?

背景

在 ArkUI 中,使用 backgroundBlurStyleforegroundBlurStyle 分别可以为组件背景、前景添加毛玻璃效果,示例代码如下:

@Entry
@Component
struct BackgroundBlurStyleDemo {
  build() {
    Column() {
      Row() {
        Text("Thin Material")
      }
      .width('50%')
      .height('50%')
      .backgroundBlurStyle(
        BlurStyle.Thin,
        {
          colorMode: ThemeColorMode.LIGHT,
          adaptiveColor: AdaptiveColor.DEFAULT,
          scale: 1.0 
        })
      .position({ x: '15%', y: '30%' })
    }
    .height('100%')
    .width('100%')
    .backgroundImageSize(ImageSize.Cover)
  }
}

这里的 .backgroundBlurStyle() 为这个 Row() 组件添加了一层预设为 BlurStyle.Thin 的模糊效果,看上去就好像文字 Text("Thin Material") 有一层毛玻璃的衬底。

举一反三,可以使用 .backgroundBlurStyle().foregroundBlurStyle() 在任何想要的位置制作模糊效果。

问题

然而,这两种方法默认的作用效果都是一层均匀的模糊效果,怎么样才能做出从完全清晰到完全模糊的渐变效果?

在 Harmony OS 系统应用的多处,都可以发现使用了该效果,并且做成了我想要在我应用内实现的渐变效果(红框处):

设置应用的渐变毛玻璃

尝试过的方法

我先前尝试过使用 .opacity(),但是使用了 .backgroundBlurStyle().foregroundBlurStyle() 的组件,无法再使用 .opacity() 等方法调节其透明度,并且也无从下手向它传入一套线性渐变的值:

@Entry
@Component
struct BackgroundBlurStyleDemo {
  build() {
    Column() {
      Row() {
        Text("Thin Material")
      }
      .width('50%')
      .height('50%')
      .backgroundBlurStyle(
        BlurStyle.Thin,
        {
          colorMode: ThemeColorMode.LIGHT,
          adaptiveColor: AdaptiveColor.DEFAULT,
          scale: 1.0 
        })
      .opacity(0.8)     // 此处的 opacity 无法与 backgroundBlurStyle 同时生效
      .position({ x: '15%', y: '30%' })
    }
    .height('100%')
    .width('100%')
    .backgroundImageSize(ImageSize.Cover)
  }
}

后来又尝试退而求其次,使用支持与 .opacity() 共存的 .blur(),在需要渐变透明的地方堆叠很多层不同可见程度的模糊层,来做到粗糙的伪渐变效果。然而这样做不但看起来不太自然(有稍明显的硬边缘),而且代码实现上也不优雅,并且完全不是我想要的高级毛玻璃效果:

@Component
export struct BottomBlurGradientMask {
  @Prop maskHeight: number = 80;

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Stack({ alignContent: Alignment.Bottom }) {
        ForEach(
          [
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
            10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
            20, 21, 22, 23, 24, 25, 26, 27, 28, 29
          ],
          (index: number) => {
            Column()
              .width("100%")
              .height(Math.round(this.maskHeight * (index + 1) / 30))
              .opacity(index * 0.05)
              .blur(index * index * 0.005)
              .backgroundColor(Color.Transparent)
          },
          (index: number) => index.toString()
        )

        Column()
          .width("100%")
          .height("100%")
          .linearGradient({
            direction: GradientDirection.Top,
            colors: [
              ['rgba(0, 0, 0, 0.6)', 0.0],
              ['rgba(0, 0, 0, 0.0)', 0.6]
            ]
          })
      }
      .width("100%")
      .height(this.maskHeight)
    }
    .width("100%")
    .height("100%")
    .hitTestBehavior(HitTestMode.Transparent)
  }
}

所以,最终的问题就归结在:如何为 .backgroundBlurStyle().foregroundBlurStyle() 增加对其模糊程度关于组件位置坐标的线性渐变效果?


更多关于HarmonyOS 鸿蒙Next中如何实现渐变毛玻璃(模糊)效果?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

开发者您好,可通过linearGradientBlur为组件添加内容线性渐变模糊效果,参考示例2,根据开发者demo修改如下:

// BackgroundBlurStyleDemo.ets
@Entry
@Component
struct BackgroundBlurStyleDemo {
  build() {
    Column() {
      Row() {
        Text('Thin Material');
      }
      .width('50%')
      .height('50%');
    }
    .height('100%')
    .width('100%')
    .linearGradientBlur(10,
      {
        fractionStops: [[0, 0], [0, 0.33], [1, 0.66], [1, 1]],
        direction: GradientDirection.Bottom
      }) // 添加linearGradientBlur属性
    .backgroundImage($r('app.media.xx')) // 自行添加图片背景
    .backgroundImageSize(ImageSize.Cover);
  }
}

更多关于HarmonyOS 鸿蒙Next中如何实现渐变毛玻璃(模糊)效果?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


backgroundBlurStyle/foregroundBlurStyle 本身不支持渐变模糊,也无法与 opacity 同层生效;正确做法是用 backdropBlur + 渐变透明度蒙版 + 混合模式 实现系统级同款渐变毛玻璃

@Entry
@Component
struct GradientBlurDemo {
  build() {
    Stack() {
      // 1. 底层内容(你的列表/图片/页面)
      Scroll() {
        Column() {
          ForEach(Array.from({ length: 20 }), (_, index) => {
            Text(`设置项 ${index + 1}`)
              .width('100%')
              .height(50)
              .margin({ top: 10 })
              .backgroundColor(Color.White)
              .borderRadius(8)
              .textAlign(TextAlign.Center)
          })
        }
        .width('100%')
        .padding(10)
      }

      // 2. 渐变模糊遮罩层(核心)
      Stack()
        .width('100%')
        .height('100%')
        // 背景模糊(毛玻璃本体)
        .backdropBlur(15)
        .backgroundColor(Color.White.opacity(0.1))
        // 渐变透明度蒙版:顶部清晰 → 底部模糊
        .linearGradient({
          direction: GradientDirection.Bottom,
          colors: [
            [Color.Transparent, 0.0],   // 顶部完全清晰(不模糊)
            [Color.White, 0.6]          // 底部完全模糊
          ]
        })
        // 混合模式:用渐变透明度"擦除"模糊层
        .blendMode(BlendMode.DST_IN, BlendApplyType.OFFSCREEN)
        // 不拦截点击
        .hitTestBehavior(HitTestMode.Transparent)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(0xF2F2F2)
  }
}

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

实测具有属性 .backdropBlur().linearGradient() 的组件在具有 .blendMode() 属性时并不能正常按预期显示。

在您给出的示例代码中,如果同时存在 .backdropBlur(15).blendMode(BlendMode.DST_IN, BlendApplyType.OFFSCREEN),那么在实际渲染的 UI 中看起来它们都会失效,并且 .linearGradient() 也不起作用。

在HarmonyOS Next中,使用ArkTS实现渐变毛玻璃效果:在组件上应用blur({ radius: 10 })实现模糊,配合LinearGradient设置渐变背景。例如:

Column() {
  // 内容
}
.backgroundImage('...')
.backgroundImageSize(ImageSize.Cover)
.blur({ radius: 20 })
.linearGradient({ direction: GradientDirection.Bottom, colors: [['rgba(255,255,255,0)', 0], ['rgba(255,255,255,0.8)', 1]] })

通过blurlinearGradient组合即可。

backgroundBlurStyle/foregroundBlurStyle本身不支持渐变系数的情况下,可通过混合模式 + 渐变遮罩实现视觉上的渐变模糊效果。核心思路:在模糊层下方放置一个从透明到不透明的渐变蒙版,并给模糊层设置blendMode(BlendMode.SRC_IN),使其只显示在蒙版不透明区域,从而产生模糊从无到有的渐变过渡。

Stack() {
  // 渐变遮罩:顶部不透明,底部完全透明(可根据需要调整方向)
  Column()
    .width('100%')
    .height(200)
    .linearGradient({
      direction: GradientDirection.Bottom,
      colors: [['rgba(255,255,255,1)', 0.0], ['rgba(255,255,255,0)', 1.0]]
    })
  // 模糊层,仅显示在遮罩不透明处
  Column()
    .width('100%')
    .height(200)
    .backgroundBlurStyle(BlurStyle.Thin, {
      colorMode: ThemeColorMode.LIGHT,
      adaptiveColor: AdaptiveColor.DEFAULT,
      scale: 1.0
    })
    .blendMode(BlendMode.SRC_IN)
}
  • 原理:使用BlendMode.SRC_IN,模糊层作为源,其输出只保留与下层蒙版不透明区域相交的部分,蒙版透明度渐变使得模糊层的可见度也渐变。
  • 适用场景:屏幕底部、顶部或任何需要从清晰过渡到模糊毛玻璃的区域,代码简洁,无需多层堆叠。
回到顶部