Go to file
tyBrave 0799ed85e2 update code because of some idea
Signed-off-by: tyBrave <tianyong13@huawei.com>
2023-02-22 10:55:02 +08:00
AppScope 1.add AppScope folder: adapter stage mode api9 2022-07-14 22:52:57 -07:00
entry update code because of some idea 2023-02-22 10:55:02 +08:00
gpu_transform update code because of some idea 2023-02-22 10:55:02 +08:00
imageknife update code because of some idea 2023-02-22 10:55:02 +08:00
screenshot 1.测试页面修改 2022-11-16 00:57:37 -08:00
.gitignore 1.hvigor submit the construction project for the first version 2022-04-13 11:49:12 +08:00
CHANGELOG.md 1.update README.md CHANGELOG.md 2022-12-14 02:00:47 -08:00
LICENSE 1.add LICENSE 2022-04-13 16:20:39 +08:00
NOTICE 1.测试页面修改 2022-11-16 00:57:37 -08:00
OAT.xml 1.测试页面修改 2022-11-16 00:57:37 -08:00
README.OpenSource 1.update README.OpenSource 2022-11-16 17:44:40 -08:00
README.md update code because of some idea 2023-02-22 10:55:02 +08:00
build-profile.json5 add gpu transform module 2023-02-02 16:59:13 +08:00
hvigorfile.js 1.update ImageKnife Project Config Files 2022-07-14 23:14:51 -07:00
package.json updae code of codecheck 2023-02-03 10:48:56 +08:00

README.md

ImageKnife

专门为OpenHarmony打造的一款图像加载缓存库致力于更高效、更轻便、更简单。

简介

本项目基于开源库 Glide 进行OpenHarmony的自研版本

  • 支持内存缓存使用LRUCache算法对图片数据进行内存缓存。
  • 支持磁盘缓存,对于下载图片会保存一份至磁盘当中。
  • 支持进行图片变换: 支持图像像素源图片变换效果。
  • 支持用户配置参数使用:(例如:配置是否开启一级内存缓存,配置磁盘缓存策略,配置仅使用缓存加载数据,配置图片变换效果,配置占位图,配置加载失败占位图等)。
  • 推荐使用ImageKnifeComponent组件配合ImageKnifeOption参数来实现功能。
  • 支持用户自定义配置实现能力参考ImageKnifeComponent组件中对于入参ImageKnifeOption的处理。

下载安装

npm install @ohos/imageknife --save

OpenHarmony npm环境配置等更多内容参考安装教程 如何安装OpenHarmony npm包

使用说明

1.在AbilityStage.ts中,初始化全局ImageKnife实例。

import AbilityStage from "@ohos.application.AbilityStage"
import {ImageKnife} from '@ohos/imageknife'

export default class MyAbilityStage extends AbilityStage {
    onCreate() {
        // 初始化全局ImageKnife实例在AbilityStage.ts中调用ImageKnife.with(this.context)进行初始化
        globalThis.ImageKnife = ImageKnife.with(this.context)
    }
 }

1.加载普通图片

接下来我们来写个简单实例看看:

import {ImageKnifeComponent} from '@ohos/imageknife'
import {ImageKnifeOption} from '@ohos/imageknife'
@Entry
@Component
struct Index {
  @State imageKnifeOption1: ImageKnifeOption =
    { // 加载一张本地的jpg资源必选
      loadSrc: $r('app.media.jpgSample'),
      // 占位图使用本地资源icon_loading可选
      placeholderSrc: $r('app.media.icon_loading'),
      // 失败占位图使用本地资源icon_failed可选
      errorholderSrc: $r('app.media.icon_failed')
    };
  build() {
    Scroll() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
          .width(300) // 自定义组件支持通用属性链式调用,可以直接设置宽高
          .height(300)
      }
    }
    .width('100%')
    .height('100%')
  }
}

非常简单仅需定义一个ImageKnifeOption数据对象然后在你需要的UI位置加入ImageKnifeComponent自定义组件就可以加载出一张图像了。

2.加载SVG图片

加载svg其实和普通流程没有区别,只要将 loadSrc: $r('app.media.jpgSample'), 改成一张 loadSrc: $r('app.media.svgSample'),svg类型图片即可。

目前加载SVG图片解析依赖了 SVG三方库由于目前该库还无法解析mask标签所以这里大家需要留意一下。

3.加载GIF图片

3.1加载GIF其实和普通流程也没有区别只要将 loadSrc: $r('app.media.jpgSample'), 改成一张 loadSrc: $r('app.media.gifSample'),GIF图片即可。

但是解析gif图片属于耗时操作所以我们需要将其放入子线程操作。 这里我们需要在页面的创建和销毁上添加一个worker子线程操作。

import router from '@system.router';
import {
  ImageKnifeComponent,
  ImageKnifeOption,
} from '@ohos/imageknife'
import ArkWorker from '@ohos.worker'
@Entry
@Component
struct IndexFunctionDemo {
  private globalGifWorker:any = undefined
  @State imageKnifeOption: ImageKnifeOption =
    {
      loadSrc: $r('app.media.gifSample'),
      placeholderSrc: $r('app.media.icon_loading'),
      errorholderSrc: $r('app.media.icon_failed')
    };

  build() {
    Scroll() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Text("简单示例加载一张gif图片").fontSize(15)
        ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption })
          .width(300) // 自定义组件支持通用属性链式调用,可以直接设置宽高
          .height(300)
      }
    }
    .width('100%')
    .height('100%')
  }

  aboutToAppear() {
  // 页面初始化创建worker
    this.globalGifWorker = new ArkWorker.Worker('entry/ets/pages/workers/gifParseWorker.ts', {
      type: 'classic',
      name: 'ImageKnifeParseGIF'
    })
    // gif解析在子线程,请在页面构建后创建worker,注入imageknife
    globalThis.ImageKnife.setGifWorker(this.globalGifWorker)
  }
  aboutToDisappear(){
   // 页面销毁 销毁worker
   if(this.globalGifWorker){
      this.globalGifWorker.terminate();
    }
  }

}

创建worker

由于使用到了worker目前worker创建只能在entry里面所以我这边着重讲一下worker配置的流程。

在entry/src/main/ets/pages目录下创建文件夹workers,然后在文件夹中创建gifParseWorker.ts文件最后填入内容:

import arkWorker from '@ohos.worker';
import { gifHandler } from '@ohos/imageknife/GifWorker'

arkWorker.parentPort.onmessage = gifHandler;

代码处理流程已经封装在gifHanlder函数中了。

接下来我们需要在entry/build-profile.json5文件中添加配置

{
  "apiType": 'stageMode',
  "buildOption": {
    "sourceOption": {
      "workers": [
    	"./src/main/ets/pages/workers/gifParseWorker.ts",
      ]
    }
  },
  "targets": [
    {
      "name": "default",
    },
    {
      "name": "ohosTest",
    }
  ]
}

经过了上面的配置就配置好了worker, GIF图片加载就能顺利进行了。 如果GIF加载未成功可以检查一下worker配置是否完成

进阶使用

如果简单的加载一张图像无法满足需求我们可以看看ImageKnifeOption这个类提供了哪些扩展能力。

ImageKnifeOption参数列表

参数名称 入参内容 功能简介
loadSrc string | PixelMap | Resource 设置主图资源(必选)
mainScaleType ScaleType 设置主图展示样式(可选)
strategy DiskStrategy 设置磁盘缓存策略(可选)
dontAnimateFlag boolean gif加载展示一帧可选
placeholderSrc PixelMap | Resource 设置占位图(可选)
placeholderScaleType ScaleType 设置占位图展示样式(可选)
errorholderSrc PixelMap | Resource 设置加载失败占位图(可选)
errorholderSrcScaleType ScaleType 设置失败占位图展示样式(可选)
retryholderSrc PixelMap | Resource 设置加载失败重试占位图(可选)
retryholderScaleType ScaleType 设置重试占位图展示样式(可选)
thumbSizeMultiplier number 范围(0,1] 设置缩略图占比(可选)
thumbSizeDelay number 设置缩略图展示时间(可选)
thumbSizeMultiplierScaleType ScaleType 设置缩略图展示样式(可选)
displayProgress boolean 设置是否展示下载进度条(可选)
canRetryClick boolean 设置重试图层是否点击重试(可选)
onlyRetrieveFromCache boolean 仅使用缓存加载数据(可选)
isCacheable boolean 是否开启一级内存缓存(可选)
gif {
// 返回一周期动画gif消耗的时间
loopFinish?: (loopTime?) => void
// gif播放速率相关
speedFactory?: number
// 直接展示gif第几帧数据
seekTo?: number
}
GIF播放控制能力可选
transformation BaseTransform 单个变换(可选)
transformations Array<BaseTransform> 多个变换,目前仅支持单个变换(可选)
allCacheInfoCallback IAllCacheInfoCallback 输出缓存相关内容和信息(可选)
drawLifeCycle IDrawLifeCycle 用户自定义实现绘制方案(可选)

其他参数只需要在ImageKnifeOption对象上按需添加即可。

这里我们着重讲一下自定义实现绘制方案。为了增强绘制扩展能力目前ImageKnifeComponent使用了Canvas的渲染能力作为基础。在此之上为了抽象组件绘制表达。我将图像的状态使用了IDrawLifeCycle绘制生命周期进行表达

大致流程 展示占位图->展示网络加载进度->展示缩略图->展示主图->展示重试图层->展示失败占位图

ImageKnifeComponent内部责任链实现。 用户参数设置->全局参数设置->自定义组件内部设置

采用责任链的好处是,用户可以通过自定义绘制,重新绘制图层。如果不想绘制也可以通过预制回调获取绘制流程信息。

场景1:默认的展示不满足需求,需要加个圆角效果。

代码如下:

import {ImageKnifeComponent} from '@ohos/imageknife'
import {ImageKnifeOption} from '@ohos/imageknife'
import {ImageKnifeDrawFactory} from '@ohos/imageknife'
@Entry
@Component
struct Index {
  @State imageKnifeOption1: ImageKnifeOption =
    { // 加载一张本地的jpg资源必选
      loadSrc: $r('app.media.jpgSample'),
      // 组件宽设置为300高设置为300必选
      size: { width: '300', height: '300' },
      // 占位图使用本地资源icon_loading可选
      placeholderSrc: $r('app.media.icon_loading'),
      // 失败占位图使用本地资源icon_failed可选
      errorholderSrc: $r('app.media.icon_failed'),
      // 绘制圆角30边框5边框"#ff00ff".用户自定义绘制(可选)
      drawLifeCycle:ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30)
    };


  build() {
    Scroll() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
      }
    }
    .width('100%')
    .height('100%')
  }
}

ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30)我们深入查看源码可以发现实际上是对IDrawLifeCycle接口的部分实现这里我介绍一下IDrawLifeCycle。

IDrawLifeCycle的返回值代表事件是否被消费如果被消费接下来组件内部就不会处理如果没被消费就会传递到下一个使用者。目前消费流程用户自定义->全局配置定义->组件内部默认定义)

所以我们在当数据是一张PixelMap的时候目前jpg png bmp webp svg返回的都是PixelMapgif返回GIFFrame数组我们返回了true。消费了事件代表这个绘制流程用户自定义完成。

由于IDrawLifeCycle实现较为冗长我们封装了ImageKnifeDrawFactory工厂提供了网络下载百分比效果、圆角、椭圆添加边框等能力。下面我们就再看看使用工厂封装之后的场景代码。

场景2: 网络下载百分比效果展示

当进行加载网络图片时,可能需要展示网络下载百分比动画。但是默认的动画又不能满足需求,这个时候我们就需要自定义网络下载百分比效果。代码如下:

import AbilityStage from '@ohos.application.AbilityStage'
import { ImageKnife,ImageKnifeDrawFactory} from '@ohos/imageknife'

import ArkWorker from '@ohos.worker'

export default class MyAbilityStage extends AbilityStage {
    onCreate() {
        globalThis.ImageKnife = ImageKnife.with(this.context);
        // 全局配置网络加载进度条
        globalThis.ImageKnife.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
    }
}

这里大家可能会问为什么会将这个IDrawLifeCycle放在AbilityStage里面实现

这是因为网络下载百分比进度很多时候都是全局通用如果有需要全局配置的自定义展示方案。推荐在AbilityStage里面往ImageKnife的setDefaultLifeCycle函数中注入即可将ImageKnifeComponent中的默认绘制方案替换。

在这里我们实现的效果如下图所示。

高级用法

以上简单使用和进阶使用都是经过一层自定义组件封装之后形成的RequestOption封装成了ImageKnifeOption,绘制部分封装成了自定义组件ImageKnifeComponent。

如果用户其实并不关心绘制部分或者说想用自己的通用方案对自定义组件ImageKnifeComponent重构都是可以的。

下面我们会着重指导用户如何复用图片加载逻辑重构自定义组件ImageKnifeComponent。

首先我们先看看RequestOption构建的内容,如下所示:

数据加载

RequestOption构建:

请查看[RequestOption接口方法](##'RequestOption 用户配置参数')

了解了RequestOption的参数内容后我们可以参考ImageKnifeComponent组件代码进行分析。

imageKnifeExecute()函数入口首先我们需要构建一个RequestOption对象let request = new RequestOption(), 接下来就是按需配置request对象的内容最后使用 globalThis.ImageKnife.call(request)发送request执行任务即可。

是不是很简单,而其实最重要的内容是就是: 按需配置request对象的内容 为了更好理解,我举例说明一下:

场景一: 简单加载一张图片

let request = new RequestOption();
(必传)
request.load("图片url")
  (可选 整个request监听回调
  .addListener((err, data) => {
	// data 是ImageKnifeData对象
	if(data.isPixelMap()){
	// 这样就获取到了目标PixelMap
	let pixelmap = data.drawPixleMap.imagePixelMap;
	}
    return false;
  })
 
  let compSize = {
    width: this.currentWidth,
    height:this.currentHeight
  }
  // 必传这里setImageViewSize函数必传组件大小因为涉及到图片变换效果都需要适配图像源和组件大小。
 request.setImageViewSize(compSize)
 // 最后使用ImageKnife的call函数调用request即可
 globalThis.ImageKnife.call(request)

其他场景,可以按需加载

比如我需要配置 占位图 只需要 在request对象创建好之后调用 placeholder 函数即可

request.placeholder(this.imageKnifeOption.placeholderSrc, (data) => {
  console.log('request.placeholder callback')
  this.displayPlaceholder(data)
})

再比如 我对缓存配置有要求,我要禁用内存缓存,调用 skipMemoryCache 函数即可

request.skipMemoryCache(true)

这里只是简单介绍部分使用,更多的内容请参考 按需加载 原则并且可以参考ImageKnifeComponent源码或者根据文档自行探索实现。

接口说明

RequestOption 用户配置参数:

方法名 入参 接口描述
load(src: string | PixelMap | Resource) string | PixelMap | Resource 待加载图片的资源
setImageViewSize(imageSize: { width: number, height: number }) { width: number, height: number } 传入显示图片组件的大小,变换的时候需要作为参考
diskCacheStrategy(strategy: DiskStrategy) DiskStrategy 配置磁盘缓存策略 NONE SOURCE RESULT ALL AUTOMATIC
placeholder(src: PixelMap | Resource, func?: AsyncSuccess) src: PixelMap | Resource, func?: AsyncSuccess 配置占位图其中func为数据回调函数
errorholder(src: PixelMap | Resource, func?: AsyncSuccess) src: PixelMap | Resource, func?: AsyncSuccess 配置加载失败占位图其中func为数据回调函数
retryholder(src: PixelMap | Resource, func?: AsyncSuccess) src: PixelMap | Resource, func?: AsyncSuccess 配置重试占位图,如果配置则加载失败后优先展示重试占位图
addListener(func: AsyncCallback) func: AsyncCallback 配置整个监听回调,数据正常加载返回,加载失败返回错误信息
thumbnail(sizeMultiplier:number, func?: AsyncSuccess) sizeMultiplier:number, func?: AsyncSuccess 设置缩略图比例,缩略图返回后,加载并展示缩略图
addProgressListener(func?: AsyncSuccess){ this.progressFunc = func; return this; } func?: AsyncSuccess 设置网络下载百分比监听,返回数据加载百分比数值
addRetryListener(func?: AsyncSuccess){ this.retryFunc = func; return this; } func?: AsyncSuccess 设置重试监听
addAllCacheInfoCallback(func: IAllCacheInfoCallback) func: IAllCacheInfoCallback 设置获取所有缓存信息监听
skipMemoryCache(skip: boolean) skip: boolean 配置是否跳过内存缓存
retrieveDataFromCache(flag: boolean) flag: boolean 配置仅从缓存中加载数据

同时支持图片变换相关接口。

ImageKnife 启动器/门面类

方法名 入参 接口描述
call(request: RequestOption) request: RequestOption 根据用户配置参数具体执行加载流程
preload(request: RequestOption) request: RequestOption 根据用户配置参数具体执行预加载流程

缓存策略相关

使用方法 类型 策略描述
request.diskCacheStrategy(new ALL()) ALL 表示既缓存原始图片,也缓存转换过后的图片
request.diskCacheStrategy(new AUTOMATIC()) AUTOMATIC 表示尝试对本地和远程图片使用适合的策略
request.diskCacheStrategy(new DATA()) DATA 表示只缓存原始图片
request.diskCacheStrategy(new NONE()) NONE 表示不缓存任何内容
request.diskCacheStrategy(new RESOURCE()) RESOURCE 表示只缓存转换过后的图片

图片变换相关

使用方法 类型 相关描述
request.centerCrop() CenterCrop 可以根据图片文件目标显示大小进行对应centerCrop
request.centerInside() CenterInside 可以根据图片文件目标显示大小进行对应centerInside
request.fitCenter() FitCenter 可以根据图片文件目标显示大小进行对应fitCenter
request.blur() BlurTransformation 模糊处理
request.brightnessFilter() BrightnessFilterTransformation 亮度滤波器
request.contrastFilter() ContrastFilterTransformation 对比度滤波器
request.cropCircle() CropCircleTransformation 圆形剪裁显示
request.cropCircleWithBorder() CropCircleWithBorderTransformation 圆环展示
request.cropSquare() CropSquareTransformation 正方形剪裁
request.crop() CropTransformation 自定义矩形剪裁
request.grayscale() GrayscaleTransformation 灰度级转换
request.invertFilter() InvertFilterTransformation 反转滤波器
request.pixelationFilter() PixelationFilterTransformation 像素化滤波器
request.rotateImage() RotateImageTransformation 图片旋转
request.roundedCorners() RoundedCornersTransformation 圆角剪裁
request.sepiaFilter() SepiaFilterTransformation 乌墨色滤波器
request.sketchFilter() SketchFilterTransformation 素描滤波器
request.mask() MaskTransformation 遮罩
request.swirlFilter() SwirlFilterTransformation 扭曲滤波器
request.kuwaharaFilter() KuwaharaFilterTransform 桑原滤波器
request.toonFilter() ToonFilterTransform 动画滤波器
request.vignetteFilter() VignetteFilterTransform 装饰滤波器

兼容性

支持 OpenHarmony API version 9 及以上版本。

目录结构

/imageknife/src/
- main/ets/components
    - cache                  # 缓存相关内容
       - diskstrategy        # 缓存策略
       - key                 # 缓存key生成策略
       - Base64.ets          # Base64算法
       - CustomMap.ets       # 自定义Map封装
       - DiskCacheEntry.ets  # 磁盘缓存entry
       - DiskLruCache.ets    # 磁盘LRU缓存策略
       - FileReader.ets      # 文件读取相关
       - FileUtils.ets       # 文件工具类
       - LruCache.ets        # 内存LRU缓存策略
       - Md5.ets             # MD5算法
       
    - imageknife             # imageknife主要内容
       - compress            # 压缩相关
       - constants           # 常量相关
       - entry               # 部分数据结构
       - holder              # 占位图相关解析
       - interface           # 接口相关
       - networkmanage       # 网络相关
       - pngj                # pngj相关
       - requestmanage       # imageknife请求相关
       - resourcemanage      # 本地资源解析相关
       - transform           # 图片变换相关
       - utils               # 工具类相关
       - ImageKnife.ets            # imageknife门面app持久化类
       - ImageKnifeData.ets        # 数据封装
       - ImageKnifeComponent.ets   # 自定义控件封装
       - ImageKnifeDrawFactory.ets # IDrawLifeCycle用户自定义实现
       - ImageKnifeOption.ets      # 用户传参数封装
       - RequestOption.ets         # 用户设置参数封装
       
/entry/src/
- main/ets/MainAbility     
    - pages                                    # 测试page页面列表
       - basicTestFeatureAbilityPage.ets       # 测试列表加载
       - basicTestFileIOPage.ets               # 测试fileio
       - basicTestMediaImage.ets               # 测试媒体image
       - basicTestResourceManagerPage.ets      # 测试本地资源解析
       - compressPage.ets                      # 压缩页面
       - cropImagePage2.ets                    # 手势裁剪页面
       - frescoImageTestCasePage.ets           # 测试属性动画组件切换
       - frescoRetryTestCasePage.ets           # 测试ImageKnifeComponent加载失败重试
       - gifTestCasePage.ets				   # 测试gif解析页面
       - svgTestCasePage.ets				   # 测试svg解析页面
       - imageknifeTestCaseIndex.ets           # 测试用例页面入口
       - index.ets                             # 程序入口页面
       - loadNetworkTestCasePage.ets           # 网络加载测试
       - loadResourceTestCasePage.ets          # 本地加载测试
       - showErrorholderTestCasePage.ets       # 加载失败占位图测试
       - storageTestDiskLruCache.ets           # 磁盘缓存测试
       - storageTestLruCache.ets               # 内存缓存测试
       - testAllCacheInfoPage.ets              # 所有缓存信息获取测试
       - testGifDontAnimatePage.ets            # gif加载静态图片测试
       - testImageKnifeOptionChangedPage.ets   # 数据切换测试
       - testImageKnifeOptionChangedPage2.ets  # 数据切换测试,部分变换
       - testImageKnifeOptionChangedPage3.ets  # 数据切换测试,组件动画
       - testImageKnifeOptionChangedPage4.ets  # 数据切换测试,内容动画
       - testImageKnifeOptionChangedPage5.ets  # 数据切换测试,ImageKnifeDrawFactory封装圆角圆环边框等
       - testPreloadPage.ets                   # 预加载测试
       - transformPixelMapPage.ets             # 所有类型变换测试
       
    - gifParseWorker.ts     # gifworker子线程解析
    - worker1.js            # worker多线程测试

贡献代码

使用过程中发现任何问题都可以提 issue 给我们,当然,我们也非常欢迎你给我们发 PR

开源协议

本项目基于 Apache License 2.0 ,请自由的享受和参与开源。

遗留问题

1.目前只支持一种图片变换效果。

2.目前svg和gif动图不支持变换效果。

3.svg解析目前不支持mask标签。