模糊效果这玩意儿,界面里用得特别多。背景模糊、图片模糊、转场动画模糊,都是为了让内容更突出,层次更分明。
image.png
但模糊算法有个坑——像素级处理,计算量大。要是动画里用模糊,那计算压力更大,容易卡顿丢帧。

这篇文章咱们聊的是:静态模糊和动态模糊哪个性能好?啥场景用哪个?我用真实数据对比一下,让你选方案时有个参考。

一、静态模糊和动态模糊啥区别

先说清楚概念:

静态模糊:对一张静态图片做一次模糊处理,拿到一张模糊后的图。之后这张图不再变化,你咋展示它都行。

动态模糊:模糊效果每帧都在变化——内容变化、模糊半径变化,都要实时重算。比如视频背景模糊,视频在动,背景模糊也要跟着变。

静态模糊适合:静态图片、固定背景
动态模糊适合:视频、实时变化的UI

系统提供的API

HarmonyOS给开发者提供了两套API:

静态模糊API

  • EffectKit的Filter.blur():设置模糊半径,一次处理拿到PixelMap

动态模糊API

  • blur属性:直接给组件加模糊,设置半径就行
  • backdropBlur属性:背景模糊,类似iOS的磨砂玻璃效果
  • backgroundBlurStyle/foregroundBlurStyle:系统预设模糊样式,不能调参数
  • backgroundEffect:可自定义参数(模糊半径、饱和度、蒙版颜色等)

二、转场场景对比:动态模糊VS静态模糊

我用一个常见场景做对比:点击按钮,全屏模态转场拉起一个图片模糊的页面。

动态模糊实现

直接给Image组件加blur属性,模糊半径设13:

@Builder
motionBlurBuilder() {
  Stack({ alignContent: Alignment.Bottom }) {
    Image($r('app.media.test'))
      .width('100%')
      .height('100%')
      .objectFit(ImageFit.Fill)
      .blur(13) // 动态模糊
      
    Button('关闭')
      .width('90%')
      .height(40)
      .margin({ bottom: this.bottomSafeHeight + 16 })
      .onClick(() => {
        this.isShowMotionBlur = false;
      })
  }
}

555.gif
代码看着挺简洁,一个属性搞定。

静态模糊实现

静态模糊要手动处理图片,分三步走:

  1. 创建PixelMap
  2. 创建Filter实例
  3. 调用blur处理,获取模糊后的PixelMap
async staticBlur(): Promise<void> {
  // 读取图片
  const context = this.getUIContext().getHostContext()!;
  const fileData = await context.resourceManager.getRawFileContent('test.png');
  const buffer = fileData.buffer.slice(0);
  const imgSource = image.createImageSource(buffer);
  
  // 创建PixelMap
  const opts = {
    editable: true,
    pixelFormat: 3, // RGBA_8888
    size: { height: 4, width: 6 }
  };
  const pixelMap = await imgSource.createPixelMap(opts);
  
  // 模糊处理
  const blurRadius = 3;
  const filter = effectKit.createEffect(pixelMap);
  if (filter) {
    filter.blur(blurRadius);
    const blurredPixelMap = await filter.getEffectPixelMap();
    this.pixelMap = blurredPixelMap;
  }
}

666.gif
代码多几行,但模糊处理只在转场开始时执行一次。

效果等效条件

这里有个坑:动态模糊blur和静态模糊blur的半径数值不等效。

动态模糊blur设13,静态模糊blur设3,视觉效果差不多。

为啥?底层算法不同。动态模糊用的是实时渲染算法,静态模糊用的是离线处理算法。

我在对比时,把两个半径调到视觉效果相近,这样性能对比才公平。

三、性能数据对比

用DevEco Studio的Profiler工具分析帧率。

点击按钮触发转场,抓取整个过程:

动态模糊性能

RenderFrame(GPU绘制)标签显示:

  • 平均渲染耗时:6.113ms
  • 平均帧率:108.0fps

静态模糊性能

RenderFrame标签显示:

  • 平均渲染耗时:3.357ms
  • 平均帧率:119.9fps

渲染耗时对比:

  • 动态模糊:6.113ms
  • 静态模糊:3.357ms
  • 静态模糊减少约45%

帧率对比:

  • 动态模糊:108fps
  • 静态模糊:119fps
  • 静态模糊更高,接近系统最佳帧率

这数据说明啥?在视觉效果相近的情况下,静态模糊性能比动态模糊好一大截。

四、为啥静态模糊更快

底层实现机制不同:

静态模糊:只算一次。拿到图片,模糊处理,存成PixelMap。之后用这个PixelMap就行,不用再算。

动态模糊:每帧都要算。组件在渲染时,每帧都要重新执行模糊算法。就算图片没变,渲染管线也会触发重新计算。

动画叠加时更明显:转场动画+模糊计算,双倍计算压力。要是页面还有其他动态元素,那更容易卡顿。

五、啥场景用啥模糊

优先用静态模糊的场景

  1. 转场动画里的背景模糊:转场开始时算一次,之后用静态图
  2. 卡片背景模糊:固定图片做背景,不变化
  3. 图片预览页面的模糊效果:图片固定,模糊一次就行
  4. 弹窗背景模糊:弹窗内容固定,背景也固定

这些场景的共同点:模糊对象是静态内容,不需要实时更新。

必须用动态模糊的场景

  1. 视频背景模糊:视频在播放,背景模糊要跟着变
  2. 实时相机预览的背景模糊:相机画面在变化
  3. 滚动内容的模糊效果:内容位置实时变化
  4. 动态UI的模糊过渡:模糊半径在动画变化

这些场景的共同点:模糊对象是动态内容,每帧都要更新。

六、静态模糊的最佳实践

何时预渲染

静态模糊要提前算,不能在动画开始时才算——那样会卡一帧。

建议在页面加载时就预渲染模糊图,存起来,动画触发时直接用。

aboutToAppear() {
  this.preloadBlurredImage(); // 页面加载时就预渲染
}

async preloadBlurredImage() {
  // 图片加载和模糊处理
  // 结果存到this.blurredPixelMap
}

模糊半径的坑

静态模糊的半径数值和动态模糊不一样。你得调试几次,找到等效值。

一般来说:

  • 动态模糊半径大,效果明显
  • 静态模糊半径小,效果类似

具体数值要看实际图片,自己对比调整。

内存占用

静态模糊会多占一份内存——原始图一份,模糊图一份。

如果图片很大,要注意内存压力。可以在不需要时释放模糊图:

aboutToDisappear() {
  this.blurredPixelMap = undefined; // 释放内存
}

七、动态模糊的优化技巧

如果必须用动态模糊,有几个技巧能减少卡顿:

减少模糊半径

模糊半径越大,计算越慢。在不影响效果的前提下,尽量用小半径。

降低模糊频率

不是每帧都要更新模糊效果。比如视频背景模糊,可以每隔几帧更新一次,而不是每帧都算。

但这个优化需要自己实现调度逻辑,系统API目前不支持。

简化页面内容

页面其他元素越少,渲染压力越小。动态模糊场景里,尽量简化其他UI。

八、选择方案的建议

判断标准很简单:

模糊对象会不会变化?

  • 不会变化:用静态模糊
  • 会变化:用动态模糊

页面复杂度高不高?

  • 复杂页面(很多动画、很多UI):优先静态模糊
  • 简单页面:动态模糊也行

性能要求高不高?

  • 要求流畅(接近120fps):静态模糊
  • 要求不高(90fps以上):动态模糊也行

记住一个原则:能用静态模糊就用静态模糊,性能更好。除非内容确实需要实时更新,才用动态模糊。

总结

模糊效果性能优化,核心就是选对方案:

场景 推荐方案 性能表现
转场背景模糊 静态模糊 120fps
卡片固定背景 静态模糊 120fps
视频背景模糊 动态模糊 100fps左右
实时相机背景 动态模糊 视设备而定

实际测试数据:静态模糊渲染耗时比动态模糊少45%,帧率更高。

下次做模糊效果时,先判断内容是不是静态的。静态的就预渲染一次,动态的就用动态API。选对了方案,性能差距挺明显的。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐