diff --git a/CHANGELOG.md b/CHANGELOG.md index fc778cb..70da81f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ +## 3.0.1-rc.1 +- 新增ImageKnifeAnimatorComponent控制动图组件 +- 修复部分heif图无法解码 + ## 3.0.1-rc.0 -- 文件缓存设置最大缓存数量改为无上限 +- 文件缓存设置最大缓存数量改为无限制 ## 3.0.0 - 修复图形变换的闪退问题 diff --git a/README.md b/README.md index 1ee760b..fe58b14 100644 --- a/README.md +++ b/README.md @@ -255,7 +255,29 @@ ImageKnifeComponent({ },syncLoad:true }) ``` +#### 10.ImageKnifeAnimatorComponent 示例 +``` +ImageKnifeAnimatorComponent({ + imageKnifeOption:{ + loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed') + },animatorOption:this.animatorOption + }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) +``` ## 接口说明 +### ImageKnife组件 +| 组件名称 | 入参内容 | 功能简介 | +|-----------------------------|---------------------------------|--------| +| ImageKnifeComponent | ImageKnifeOption | 图片显示组件 | +| ImageKnifeAnimatorComponent | ImageKnifeOption、AnimatorOption | 动图控制组件 | + +### AnimatorOption参数列表 +| 参数名称 | 入参内容 | 功能简介 | +|-----------------------|-------------------------------------------------------|----------| +| state | AnimationStatus | 播放状态(可选) | +| iterations | number | 播放次数(可选) | +| reverse | boolean | 播放顺序(可选) | ### ImageKnifeOption参数列表 @@ -342,4 +364,5 @@ DevEco Studio 5.0 Canary3(5.0.3.221)--SDK:API12 ## 遗留问题 -- 添加组件闪动问题 \ No newline at end of file +- ImageKnifeAnimator组件无法设置ImageFit属性 +- ImageKnifeAnimator组件设置border属性无法将图片变为圆角 \ No newline at end of file diff --git a/entry/src/main/ets/common/CustomEngineKeyImpl.ets b/entry/src/main/ets/common/CustomEngineKeyImpl.ets index 6fe7c37..06f2901 100644 --- a/entry/src/main/ets/common/CustomEngineKeyImpl.ets +++ b/entry/src/main/ets/common/CustomEngineKeyImpl.ets @@ -19,14 +19,14 @@ import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKni export class CustomEngineKeyImpl implements IEngineKey { // 生成内存缓存key generateMemoryKey(loadSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource, - imageKnifeOption: ImageKnifeOption, width?: number, height?: number): string { + imageKnifeOption: ImageKnifeOption,isAnimator?: boolean, width?: number, height?: number): string { let key = "" if(imageKnifeOption.signature == "aaa" && typeof loadSrc == "string") { let num = loadSrc.indexOf("?") let src = loadSrc.substring(0,num) key = "loadSrc=" + src } else { - key = "loadSrc=" + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" + key = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" } if (requestSource === ImageKnifeRequestSource.SRC) { if (imageKnifeOption.signature !== undefined && imageKnifeOption.signature !== "") { @@ -40,14 +40,14 @@ export class CustomEngineKeyImpl implements IEngineKey { } // 生成文件缓存key - generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string): string { + generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string,isAnimator?: boolean): string { let src = "" if(signature == "aaa" && typeof loadSrc == "string") { let num = loadSrc.indexOf("?") let key = loadSrc.substring(0,num) src = "loadSrc=" + key } else { - src = "loadSrc=" + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" + src = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" } if (signature !== undefined && signature !== "") { src += "signature=" + signature + ";" diff --git a/entry/src/main/ets/pages/ImageAnimatorPage.ets b/entry/src/main/ets/pages/ImageAnimatorPage.ets new file mode 100644 index 0000000..bde0508 --- /dev/null +++ b/entry/src/main/ets/pages/ImageAnimatorPage.ets @@ -0,0 +1,41 @@ +import { AnimatorOption, ImageKnifeAnimatorComponent } from "@ohos/libraryimageknife" + +@Entry +@Component +struct ImageAnimatorPage { + @State animatorOption: AnimatorOption = { + state: AnimationStatus.Running, + iterations: -1 + } + build() { + Column(){ + Flex(){ + Button("播放").onClick(()=>{ + this.animatorOption.state = AnimationStatus.Running + }) + Button("暂停").onClick(()=>{ + this.animatorOption.state = AnimationStatus.Paused + }) + Button("停止").onClick(()=>{ + this.animatorOption.state = AnimationStatus.Stopped + }) + Button("无限循环").onClick(()=>{ + this.animatorOption.iterations = -1 + }) + Button("播放一次").onClick(()=>{ + this.animatorOption.iterations = 1 + }) + Button("播放两次").onClick(()=>{ + this.animatorOption.iterations = 2 + }) + } + ImageKnifeAnimatorComponent({ + imageKnifeOption:{ + loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc:$r('app.media.loading'), + errorholderSrc:$r('app.media.failed') + },animatorOption:this.animatorOption + }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30}) + }.width("100%").height("100%") + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 2053dc2..ee212d9 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -26,10 +26,14 @@ struct Index { build() { Scroll(){ Column() { - Button("测试加载多张相同图片").onClick(()=>{ + Button("测试ImageAnimator组件").onClick(()=>{ + router.push({ + uri: 'pages/ImageAnimatorPage', + }); + }) + Button("测试加载多张相同图片").margin({top:10}).onClick(()=>{ router.push({ uri: 'pages/TestCommonImage', - }); }) Button("测试HSP场景预加载").margin({top:10}).onClick(()=>{ diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index b522a2e..83d9632 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -19,6 +19,7 @@ "pages/TestHspPreLoadImage", "pages/TestRemoveCache", "pages/dataShareUriLoadPage", - "pages/TestCommonImage" + "pages/TestCommonImage", + "pages/ImageAnimatorPage" ] } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/FileLruCache.test.ets b/entry/src/ohosTest/ets/test/FileLruCache.test.ets index 00ddc29..2dd76f3 100644 --- a/entry/src/ohosTest/ets/test/FileLruCache.test.ets +++ b/entry/src/ohosTest/ets/test/FileLruCache.test.ets @@ -18,6 +18,8 @@ import Constants from '../../../main/ets/common/Constants'; import taskpool from '@ohos.taskpool'; import { GlobalContext } from '../../../main/ets/common/GlobalContext'; import { FileCache } from '@ohos/imageknife/src/main/ets/utils/FileCache'; +import { IEngineKey, ImageKnifeOption } from '@ohos/imageknife'; +import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey'; export default function FileLruCacheTest() { @@ -149,6 +151,15 @@ export default function FileLruCacheTest() { fileCache.put(JSON.stringify("xxxxx"),buf) expect(fileCache.get(JSON.stringify("xxxxx"))?.byteLength).assertEqual(1024 * 1024) }); + it('fileCacheEngineKey', 0, () => { + let engineKey: IEngineKey = new DefaultEngineKey() + let imageKnifeOption: ImageKnifeOption = { + loadSrc:"abc" + } + let imageKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"") + let imageAnimatorKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"",true) + expect(imageKey == imageAnimatorKey).assertFalse() + }); }); } diff --git a/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets index 8315fed..98f2514 100644 --- a/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets +++ b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets @@ -19,6 +19,8 @@ import image from '@ohos.multimedia.image'; import Constants from '../../../main/ets/common/Constants'; import { MemoryLruCache } from '@ohos/imageknife/src/main/ets/utils/MemoryLruCache'; import { ImageKnifeData } from '@ohos/imageknife/src/main/ets/model/ImageKnifeData'; +import { IEngineKey, ImageKnifeOption,ImageKnifeRequestSource } from '@ohos/imageknife'; +import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey'; export default function MemoryLruCacheTest() { @@ -118,6 +120,16 @@ export default function MemoryLruCacheTest() { expect(memoryCache.get("ccc")).assertEqual(data) }); + it('memoryCacheEngineKey', 0, () => { + let engineKey: IEngineKey = new DefaultEngineKey() + let imageKnifeOption: ImageKnifeOption = { + loadSrc:"abc" + } + let imageKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption) + let imageAnimatorKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption,true) + expect(imageKey == imageAnimatorKey).assertFalse() + }); + }); } diff --git a/library/index.ets b/library/index.ets index 671d8a0..4576c08 100644 --- a/library/index.ets +++ b/library/index.ets @@ -14,9 +14,11 @@ */ export { ImageKnifeComponent } from './src/main/ets/components/ImageKnifeComponent' +export { ImageKnifeAnimatorComponent } from './src/main/ets/components/ImageKnifeAnimatorComponent' + export { ImageKnife } from './src/main/ets/ImageKnife' -export { ImageKnifeOption } from './src/main/ets/ImageKnifeOption' +export { ImageKnifeOption , AnimatorOption } from './src/main/ets/ImageKnifeOption' export { ImageKnifeRequest } from './src/main/ets/ImageKnifeRequest' diff --git a/library/oh-package.json5 b/library/oh-package.json5 index d048c99..1d80900 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -14,7 +14,7 @@ "main": "index.ets", "repository": "https://gitee.com/openharmony-tpc/ImageKnife", "type": "module", - "version": "3.0.1-rc.0", + "version": "3.0.1-rc.1", "dependencies": { "@ohos/gpu_transform": "^1.0.2" }, diff --git a/library/src/main/ets/ImageKnife.ets b/library/src/main/ets/ImageKnife.ets index 3888631..527156d 100644 --- a/library/src/main/ets/ImageKnife.ets +++ b/library/src/main/ets/ImageKnife.ets @@ -364,12 +364,12 @@ export class ImageKnife { return false } - async execute(request: ImageKnifeRequest): Promise { + async execute(request: ImageKnifeRequest,isAnimator?: boolean): Promise { LogUtil.log("ImageKnife_DataTime_execute.start:"+request.imageKnifeOption.loadSrc) if (this.headerMap.size > 0) { request.addHeaderMap(this.headerMap) } - this.dispatcher.enqueue(request) + this.dispatcher.enqueue(request,isAnimator) LogUtil.log("ImageKnife_DataTime_execute.end:"+request.imageKnifeOption.loadSrc) } diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index db17a57..a6d896a 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -51,7 +51,7 @@ export class ImageKnifeDispatcher { // 开发者可配置全局缓存 private engineKey: IEngineKey = new DefaultEngineKey(); - showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): boolean { + showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): boolean { LogUtil.log("ImageKnife_DataTime_showFromMemomry.start:" + request.imageKnifeOption.loadSrc) let memoryCache: ImageKnifeData | undefined; if ((typeof (request.imageKnifeOption.loadSrc as image.PixelMap).isEditable) == 'boolean') { @@ -62,7 +62,7 @@ export class ImageKnifeDispatcher { } } else { memoryCache = ImageKnife.getInstance() - .loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption)); + .loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption,isAnimator)); } @@ -75,7 +75,7 @@ export class ImageKnifeDispatcher { LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadStart:" + request.imageKnifeOption.loadSrc) } LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.start:" + request.imageKnifeOption.loadSrc) - request.ImageKnifeRequestCallback?.showPixelMap(request.componentVersion, memoryCache.source, requestSource) + request.ImageKnifeRequestCallback?.showPixelMap(request.componentVersion, memoryCache.source, requestSource,memoryCache.imageAnimator) LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.end:" + request.imageKnifeOption.loadSrc) if (requestSource == ImageKnifeRequestSource.SRC) { @@ -97,10 +97,10 @@ export class ImageKnifeDispatcher { } - enqueue(request: ImageKnifeRequest): void { + enqueue(request: ImageKnifeRequest,isAnimator?: boolean): void { //1.内存有的话直接渲染 - if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC)) { + if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)) { return } // 2.内存获取占位图 @@ -114,10 +114,10 @@ export class ImageKnifeDispatcher { this.jobQueue.add(request) return } - this.executeJob(request) + this.executeJob(request,isAnimator) } - executeJob(request: ImageKnifeRequest): void { + executeJob(request: ImageKnifeRequest,isAnimator?: boolean): void { LogUtil.log("ImageKnife_DataTime_executeJob.start:" + request.imageKnifeOption.loadSrc) // 加载占位符 if (request.imageKnifeOption.placeholderSrc !== undefined && request.drawPlayHolderSuccess == false) { @@ -125,16 +125,16 @@ export class ImageKnifeDispatcher { } // 加载主图 - this.getAndShowImage(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC) + this.getAndShowImage(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator) LogUtil.log("ImageKnife_DataTime_executeJob.end:" + request.imageKnifeOption.loadSrc) } /** * 获取和显示图片 */ - getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): void { + getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): void { LogUtil.log("ImageKnife_DataTime_getAndShowImage.start:" + currentRequest.imageKnifeOption.loadSrc) - let memoryKey: string = this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption) + let memoryKey: string = this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator) let requestList: List | undefined = this.executingJobMap.get(memoryKey) if (requestList == undefined) { requestList = new List() @@ -175,7 +175,8 @@ export class ImageKnifeDispatcher { requestSource: requestSource, isWatchProgress: isWatchProgress, memoryKey: memoryKey, - fileCacheFolder: ImageKnife.getInstance().getFileCache().getCacheFolder() + fileCacheFolder: ImageKnife.getInstance().getFileCache().getCacheFolder(), + isAnimator:isAnimator } @@ -192,7 +193,7 @@ export class ImageKnifeDispatcher { LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start:" + currentRequest.imageKnifeOption.loadSrc) taskpool.execute(task).then((res: Object) => { - this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource); + this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); if (isWatchProgress){ emitter.off(Constants.PROGRESS_EMITTER + memoryKey) } @@ -208,7 +209,7 @@ export class ImageKnifeDispatcher { }) } else { //主线程请求 requestJob(request, requestList).then((res: RequestJobResult | undefined) => { - this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource); + this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator); LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc) LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc) }).catch((err:BusinessError)=>{ @@ -234,7 +235,7 @@ export class ImageKnifeDispatcher { } private doTaskCallback(requestJobResult: RequestJobResult | undefined, requestList: List , - currentRequest: ImageKnifeRequest, memoryKey: string, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource):void { + currentRequest: ImageKnifeRequest, memoryKey: string, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean):void { LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.start:"+currentRequest.imageKnifeOption.loadSrc) if (requestJobResult === undefined){ return @@ -275,12 +276,21 @@ export class ImageKnifeDispatcher { imageHeight: requestJobResult.size == undefined ? 0 : requestJobResult.size.height, type:requestJobResult.type }; - + if(requestJobResult.pixelMapList != undefined) { + let imageAnimator: Array = [] + requestJobResult.pixelMapList.forEach((item,index)=>{ + imageAnimator.push({ + src:requestJobResult.pixelMapList![index], + duration:requestJobResult.delayList![index] + }) + }) + ImageKnifeData.imageAnimator = imageAnimator + } // 保存内存缓存 if (currentRequest.imageKnifeOption.writeCacheStrategy !== CacheStrategy.File) { LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.start:"+currentRequest.imageKnifeOption.loadSrc) ImageKnife.getInstance() - .saveMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption), + .saveMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator), ImageKnifeData); LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.end:"+currentRequest.imageKnifeOption.loadSrc) } @@ -297,7 +307,7 @@ export class ImageKnifeDispatcher { requestWithSource.request.requestState === ImageKnifeRequestState.PROGRESS)) { LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.start:"+currentRequest.imageKnifeOption.loadSrc) requestWithSource.request.ImageKnifeRequestCallback.showPixelMap(requestWithSource.request.componentVersion, - ImageKnifeData.source, requestWithSource.source); + ImageKnifeData.source, requestWithSource.source,ImageKnifeData.imageAnimator); LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.end:"+currentRequest.imageKnifeOption.loadSrc) } @@ -380,7 +390,7 @@ async function requestJob(request: RequestJobRequest, requestList?: List = [] + let delayList: Array = [] + await imageSource.createPixelMapList(decodingOptions).then(async (pixelList: Array) => { + //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() + await imageSource.getDelayTimeList().then(delayTimes => { + if (pixelList.length > 0) { + for (let i = 0; i < pixelList.length; i++) { + pixelMapList.push(pixelList[i]); + if (i < delayTimes.length) { + delayList.push(delayTimes[i]); + } else { + delayList.push(delayTimes[delayTimes.length - 1]) + } + } + imageSource.release(); + } + }) + }) + return { + pixelMap: "", + bufferSize: bufferSize, + fileKey: fileKey, + type: typeValue, + pixelMapList, + delayList + } + } else { + return { + pixelMap: undefined, + bufferSize: 0, + fileKey: '', + loadFail: "ImageKnifeAnimatorComponent组件仅支持动态图", + } + } + } let resPixelmap: PixelMap | undefined = undefined if (typeValue === 'gif' || typeValue === 'webp') { let size = (await imageSource.getImageInfo()).size diff --git a/library/src/main/ets/ImageKnifeOption.ets b/library/src/main/ets/ImageKnifeOption.ets index ad4162e..438849e 100644 --- a/library/src/main/ets/ImageKnifeOption.ets +++ b/library/src/main/ets/ImageKnifeOption.ets @@ -23,6 +23,15 @@ export interface HeaderOptions { value: Object; } +@Observed +export class AnimatorOption { + @Track + state?: AnimationStatus = AnimationStatus.Running + @Track + iterations?: number = -1 + @Track + reverse?: boolean = false +} @Observed export class ImageKnifeOption { diff --git a/library/src/main/ets/ImageKnifeRequest.ets b/library/src/main/ets/ImageKnifeRequest.ets index b7c98ae..cb2115b 100644 --- a/library/src/main/ets/ImageKnifeRequest.ets +++ b/library/src/main/ets/ImageKnifeRequest.ets @@ -64,5 +64,5 @@ export enum ImageKnifeRequestState { export interface ImageKnifeRequestCallback { - showPixelMap: (version: number, pixelMap: PixelMap | string , requestSource: ImageKnifeRequestSource) => void; + showPixelMap: (version: number, pixelMap: PixelMap | string , requestSource: ImageKnifeRequestSource,imageAnimator?: Array) => void; } diff --git a/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets new file mode 100644 index 0000000..2e1671f --- /dev/null +++ b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { AnimatorOption, ImageKnifeOption } from '../ImageKnifeOption'; +import { ImageKnifeRequest, ImageKnifeRequestState } from '../ImageKnifeRequest'; +import common from '@ohos.app.ability.common'; +import { ImageKnife } from '../ImageKnife'; +import { LogUtil } from '../utils/LogUtil'; +import { ImageKnifeRequestSource } from '../model/ImageKnifeData'; + +@Component +export struct ImageKnifeAnimatorComponent { + @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption; + @Watch('watchAnimatorOption') @State animatorOption: AnimatorOption = new AnimatorOption(); + @State pixelMap: PixelMap | string | undefined = undefined + @State imageAnimator: Array | undefined = undefined + @State state: AnimationStatus = AnimationStatus.Running + @State iterations: number = -1 + @State reverse: boolean = false + @State adaptiveWidth: Length = '100%' + @State adaptiveHeight: Length = '100%' + @State objectFit: ImageFit = ImageFit.Contain + private request: ImageKnifeRequest | undefined + private lastWidth: number = 0 + private lastHeight: number = 0 + private currentWidth: number = 0 + private currentHeight: number = 0 + private componentVersion: number = 0 + private currentContext: common.UIAbilityContext | undefined = undefined + + aboutToAppear(): void { + this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit + } + + aboutToDisappear(): void { + if (this.request !== undefined) { + this.request.requestState = ImageKnifeRequestState.DESTROY + this.request = undefined + } + } + + aboutToRecycle() { + if (this.request !== undefined) { + this.request.requestState = ImageKnifeRequestState.DESTROY + this.request = undefined + } + this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit + } + + build() { + ImageAnimator() + .images(this.imageAnimator) + .width(this.adaptiveWidth) + .height(this.adaptiveHeight) + .border(this.imageKnifeOption.border) + .state(this.state) + .iterations(this.iterations) + .reverse(this.reverse) + .onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => { + this.currentWidth = newValue.width as number + this.currentHeight = newValue.height as number + this.lastWidth = oldValue.width as number + this.lastHeight = oldValue.height as number + if (this.currentWidth <= 0 || this.currentHeight <= 0) { + // 存在宽或者高为0,此次重回无意义,无需进行request请求 + } else { + // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制 + if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) { + LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight) + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight),true) + } + } + }) + } + + watchAnimatorOption(){ + if(this.animatorOption.state != undefined) { + this.state = this.animatorOption.state + } + if(this.animatorOption.iterations != undefined) { + this.iterations = this.animatorOption.iterations + } + if(this.animatorOption.reverse != undefined) { + this.reverse = this.animatorOption.reverse + } + } + + watchImageKnifeOption() { + if (this.request !== undefined) { + this.request.requestState = ImageKnifeRequestState.DESTROY + } + this.request = undefined + this.componentVersion++ + ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight),true) + } + + getCurrentContext(): common.UIAbilityContext { + if (this.currentContext == undefined) { + this.currentContext = getContext(this) as common.UIAbilityContext + } + return this.currentContext + } + + getRequest(width: number, height: number): ImageKnifeRequest { + if (this.request == undefined) { + this.request = new ImageKnifeRequest( + this.imageKnifeOption, + this.imageKnifeOption.context !== undefined ? this.imageKnifeOption.context : this.getCurrentContext(), + width, + height, + this.componentVersion, + { + showPixelMap: async (version: number, pixelMap: PixelMap | string, requestSource: ImageKnifeRequestSource,imageAnimator?: Array) => { + if (version !== this.componentVersion) { + return //针对reuse场景,不显示历史图片 + } + if (imageAnimator != undefined) { + this.imageAnimator = imageAnimator + } else { + this.imageAnimator = [ + { + src: pixelMap + } + ] + } + + if (requestSource == ImageKnifeRequestSource.SRC) { + this.objectFit = + this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit + } else if (requestSource == ImageKnifeRequestSource.PLACE_HOLDER) { + this.objectFit = + this.imageKnifeOption.placeholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.placeholderObjectFit + } else { + this.objectFit = + this.imageKnifeOption.errorholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.errorholderObjectFit + } + } + }) + } + + return this.request + } +} \ No newline at end of file diff --git a/library/src/main/ets/key/DefaultEngineKey.ets b/library/src/main/ets/key/DefaultEngineKey.ets index b2d3600..6d43be1 100644 --- a/library/src/main/ets/key/DefaultEngineKey.ets +++ b/library/src/main/ets/key/DefaultEngineKey.ets @@ -22,8 +22,8 @@ import { ImageKnifeRequestSource } from '../model/ImageKnifeData'; export class DefaultEngineKey implements IEngineKey { // 生成内存缓存key generateMemoryKey(loadSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource, - imageKnifeOption: ImageKnifeOption, width?: number, height?: number): string { - let key = "loadSrc=" + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" + imageKnifeOption: ImageKnifeOption,isAnimator?: boolean, width?: number, height?: number): string { + let key = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" if (requestSource === ImageKnifeRequestSource.SRC) { if (imageKnifeOption.signature !== undefined && imageKnifeOption.signature !== "") { key += "signature=" + imageKnifeOption.signature + ";" @@ -36,8 +36,8 @@ export class DefaultEngineKey implements IEngineKey { } // 生成文件缓存key - generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string): string { - let src = "loadSrc=" + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" + generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string,isAnimator?: boolean): string { + let src = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";" if (signature !== undefined && signature !== "") { src += "signature=" + signature + ";" } diff --git a/library/src/main/ets/key/IEngineKey.ets b/library/src/main/ets/key/IEngineKey.ets index 746df1b..54be5bb 100644 --- a/library/src/main/ets/key/IEngineKey.ets +++ b/library/src/main/ets/key/IEngineKey.ets @@ -18,10 +18,10 @@ import { ImageKnifeRequestSource } from '../model/ImageKnifeData' export interface IEngineKey { // 生成内存缓存key generateMemoryKey(loadSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource, - imageKnifeOption: ImageKnifeOption, width?: number, height?: number): string + imageKnifeOption: ImageKnifeOption,isAnimator?: boolean, width?: number, height?: number): string // 生成文件缓存key - generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string): string + generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string,isAnimator?: boolean): string } diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets index e26ee6b..79fe5d0 100644 --- a/library/src/main/ets/model/ImageKnifeData.ets +++ b/library/src/main/ets/model/ImageKnifeData.ets @@ -23,7 +23,8 @@ export interface ImageKnifeData { source: PixelMap | string, imageWidth: number, imageHeight: number, - type?:string + type?:string, + imageAnimator?: Array } /** * onComplete成功回调 @@ -75,7 +76,9 @@ export interface RequestJobResult { fileKey: string loadFail?: string, size?:Size, - type?: string + type?: string, + pixelMapList?:Array, + delayList?: Array } /** @@ -97,6 +100,7 @@ export interface RequestJobRequest { engineKey: IEngineKey isWatchProgress: boolean memoryKey: string - fileCacheFolder: string + fileCacheFolder: string, + isAnimator?: boolean } diff --git a/library/src/main/ets/utils/FileTypeUtil.ets b/library/src/main/ets/utils/FileTypeUtil.ets index ee834c4..44ada15 100644 --- a/library/src/main/ets/utils/FileTypeUtil.ets +++ b/library/src/main/ets/utils/FileTypeUtil.ets @@ -25,7 +25,7 @@ export class FileTypeUtil { 'webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])], 'tiff': [new Uint8Array([0x49, 0x20, 0x49]), new Uint8Array([0x49, 0x49, 0x2A, 0x00]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2A]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2B])], // 添加更多的文件类型和特征 - 'heic': [new Uint8Array([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00])], + 'heic': [new Uint8Array([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00]),new Uint8Array([0x00, 0x00, 0x00, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00])], }; diff --git a/library/src/main/ets/utils/MemoryLruCache.ets b/library/src/main/ets/utils/MemoryLruCache.ets index 50b78af..493fc11 100644 --- a/library/src/main/ets/utils/MemoryLruCache.ets +++ b/library/src/main/ets/utils/MemoryLruCache.ets @@ -104,8 +104,13 @@ export class MemoryLruCache implements IMemoryCache { private removeMemorySize(value: ImageKnifeData): void { if (value.source != undefined) { - if (typeof value.source === 'string') { + if (typeof value.source === 'string' && value.source != "") { this.currentMemory -= value.source.length + } else if (value.source == "") { + for (let index = 0;index < value.imageAnimator!.length;index++) { + let pixelMap = value.imageAnimator![index].src as PixelMap + this.currentMemory -= pixelMap.getPixelBytesNumber() + } } else { this.currentMemory -= value.source.getPixelBytesNumber(); value.source.release() @@ -116,8 +121,13 @@ export class MemoryLruCache implements IMemoryCache { private addMemorySize(value: ImageKnifeData): void { if (value.source != undefined) { - if (typeof value.source === 'string') { + if (typeof value.source === 'string' && value.source != "") { this.currentMemory += value.source.length + } else if (value.source == "") { + for (let index = 0;index < value.imageAnimator!.length;index++) { + let pixelMap = value.imageAnimator![index].src as PixelMap + this.currentMemory += pixelMap.getPixelBytesNumber() + } } else { this.currentMemory += value.source.getPixelBytesNumber(); } diff --git a/sharedlibrary/Index.ets b/sharedlibrary/Index.ets index 84f5bb0..5d74d4b 100644 --- a/sharedlibrary/Index.ets +++ b/sharedlibrary/Index.ets @@ -18,11 +18,11 @@ export { InitImageKnife } from "./src/main/ets/pages/InitImageKnife" export { IndexComponent } from "./src/main/ets/pages/Index" -export { ImageKnifeComponent } from '@ohos/imageknife' +export { ImageKnifeComponent,ImageKnifeAnimatorComponent } from '@ohos/imageknife' export { ImageKnife } from '@ohos/imageknife' -export { ImageKnifeOption } from '@ohos/imageknife' +export { ImageKnifeOption,AnimatorOption } from '@ohos/imageknife' export { ImageKnifeRequest } from '@ohos/imageknife'