欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

在这里插入图片描述

前言

在移动应用中,图片加载是一个关键的体验点。网络环境不佳时,图片区域长时间显示白屏或灰底,用户体验非常割裂。
传统的做法是放一个 Loading 转圈或固定的占位图,但这种方式依然比较生硬。

BlurHash 是一种革命性的占位符技术。它将图片压缩成一段只有二三十个字符的短字符串。客户端只需要这段字符串,就能瞬间(< 1ms)在本地解码并渲染出一个模糊但色调与原图一致的占位图。

blurhash_dart 是该算法的 Dart 纯实现版本。对于 OpenHarmony 应用,这意味着你可以在不增加太多带宽成本的情况下,实现如丝般顺滑的各种图片加载过渡效果。

一、核心原理与效果

1.1 什么是 BlurHash?

BlurHash 算法基于 离散余弦变换 (DCT),类似于 JPEG 的压缩原理,但它只保留最低频的颜色信息(Base83 编码)。

  • 输入:一张 4MB 的高清大图。
  • 编码:在服务端生成一段字符串,如 LEHV6nWB2yk8pyo0adR*.7kCMdnj
  • 传输:API 返回 JSON { "url": "http://...", "blurhash": "LEHV..." }
  • 解码:客户端拿到字符串,毫秒级还原出一张模糊图。

1.2 效果对比

阶段 传统方式 BlurHash 方式
加载前 灰色方块 / Loading 带有原图色调的模糊光影
加载中 突兀的图片显示 模糊图平滑过渡到原图
1. 计算 Hash
2. JSON API
3. 解码 Hash
4. 下载图片

淡入

服务端

数据库

鸿蒙 App

绘制模糊图

网络加载

二、集成与用法详解

2.1 添加依赖

这个库是纯 Dart 算法实现,用于解码和编码。通常我们在 UI 层只需要解码。

dependencies:
  blurhash_dart: ^1.2.1
  image: ^4.0.0 # 用于处理图片数据

2.2 基础用法:解码 (Decode)

将 Hash 字符串转为图片像素数据。

import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:image/image.dart' as img;

void main() {
  const hash = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
  
  // 1. 解码为 Image 对象 (32x32 足够模糊图使用了)
  img.Image image = BlurHash.decode(hash, width: 32, height: 32);

  // 2. 转换为 PNG 或其他格式使用
  List<int> pngBytes = img.encodePng(image);
}

在这里插入图片描述

2.3 结合 Flutter UI

在 Flutter 中,我们通常配合 flutter_blurhash 包(封装了 Widget)或者自己写一个 CustomPainter。这里展示如何用 blurhash_dart 手动实现,以便更好地理解原理和控制。

import 'dart:ui' as ui;
import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:image/image.dart' as img;

Future<ui.Image> generateBlurImage(String hash) async {
  // 1. 解码
  final image = BlurHash.decode(hash, width: 32, height: 32);
  
  // 2. 转换为 Flutter ui.Image
  final completer = Completer<ui.Image>();
  ui.decodeImageFromPixels(
    image.getBytes(), // 获取 RGBA 字节
    32, 
    32, 
    ui.PixelFormat.rgba8888,
    (result) => completer.complete(result),
  );
  return completer.future;
}

三、OpenHarmony 适配与实战

在 OpenHarmony 上,处理图像数据需要注意性能。由于 blurhash_dart 是在 Dart 堆内存中进行数学运算,如果主线程解码大量 Hash,可能会导致掉帧。

3.1 性能优化:Compute Isolate

强烈建议将解码过程放在 compute 中执行。

import 'package:flutter/foundation.dart';

// 这是一个顶级函数
Uint8List decodeHashIsolate(String hash) {
  final image = BlurHash.decode(hash, width: 20, height: 20); // 20x20 足够小且快
  return img.encodePng(image); // 返回 PNG 字节,方便 Image.memory 使用
}

class BlurImagePlaceholder extends StatelessWidget {
  final String hash;

  const BlurImagePlaceholder({required this.hash});

  
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List>(
      future: compute(decodeHashIsolate, hash), // 放到子线程
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Image.memory(
            snapshot.data!,
            fit: BoxFit.cover,
            gaplessPlayback: true,
          );
        }
        return Container(color: Colors.grey[200]); // 降级方案
      },
    );
  }
}

3.2 鸿蒙特定的内存考量

OpenHarmony 系统通常对应用内存有管理策略。blurhash_dart 生成的是 img.Image 对象,包含了完整的像素数组。用完后(转为 UI 纹理后),Dart GC 会自动回收,但在列表滚动等高频场景下,要注意生成的图片尺寸不要太大。通常 20x20 到 32x32 的模糊图放大后效果最好,且内存占用极低。

四、编码端 (Encoder)

虽然编码通常在服务端进行,但如果你的应用允许用户上传图片,你可以在上传前计算 BlurHash 发给服务器。

import 'dart:io';
import 'package:image/image.dart' as img;

void preUpload(File file) {
  // 1. 读取并缩放图片(编码大图很慢!)
  final original = img.decodeImage(file.readAsBytesSync())!;
  final resized = img.copyResize(original, width: 32, height: 32);
  
  // 2. 编码
  final hash = BlurHash.encode(resized, numCompX: 4, numCompY: 3);
  print('Generated Hash: $hash');
  
  // 3. 上传 hash 和 file ...
}

在这里插入图片描述

五、总结

blurhash_dart 是一个小巧但能极大提升 App 精致感的库。

对于 OpenHarmony 开发者:

  • 纯 Dart 优势:无需链接 C++ 库或调用鸿蒙原生 NDK,完全跨平台兼容。
  • 视觉提升:让你的应用看起来像国际一线大厂(如 Pinterest, Unsplash)的产品一样细腻。

最佳实践

  1. 尺寸控制:解码宽高设为 20-32px 即可,无需更高,模糊后看不出区别。
  2. 异步处理:务必使用 computeIsolate 进行解码,避免阻塞 UI 线程。
  3. 缓存:对于同一个 Hash,解码后的图片应当在内存中缓存,避免重复计算。

六、完整实战示例

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:image/image.dart' as img; // 需要 image 库配合

class BlurHashImage extends StatelessWidget {
  final String hash; // 例如: 'LEHV6nWB2yk8pyo0adR*.7kCMdnj'

  const BlurHashImage({required this.hash, Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List>(
      // 将解码任务放入微任务或 Isolate 中
      future: Future.microtask(() {
        // 1. 解码 Hash 为像素数据
        // 宽高设为 32x32 足够模糊占位用了,性能最好
        final image = BlurHash.decode(hash, 32, 32);
        
        // 2. 编码为 PNG 二进制以便 Flutter 显示
        // 注意:实际项目中建议缓存这个 bytes,避免重复编码
        return Uint8List.fromList(img.encodePng(image));
      }),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Image.memory(
            snapshot.data!,
            fit: BoxFit.cover,
            gaplessPlayback: true, // 防止重绘闪烁
          );
        }
        // 解码前的灰色占位
        return Container(color: Colors.grey[200]);
      },
    );
  }
}

在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐