1.重构imageknife整个渲染层

a.重构ImageKnifeComponent
b.重构ImageOption
c.抽象绘制生命周期

Signed-off-by: zhoulisheng1 <zhoulisheng1@huawei.com>
This commit is contained in:
zhoulisheng1 2022-11-14 03:44:42 -08:00
parent c1c3ce210f
commit 7cf9a129e9
10 changed files with 1515 additions and 443 deletions

View File

@ -19,6 +19,7 @@ import {EngineKeyFactories} from "../cache/key/EngineKeyFactories"
import {RequestOption} from "../imageknife/RequestOption"
import {AsyncCallback} from "../imageknife/interface/asynccallback"
import {PlaceHolderManager} from "../imageknife/holder/PlaceHolderManager"
import {RetryHolderManager} from "../imageknife/holder/RetryHolderManager"
import {ErrorHolderManager} from "../imageknife/holder/ErrorHolderManager"
import {RequestManager} from "../imageknife/requestmanage/RequstManager"
import {NONE} from "../cache/diskstrategy/enum/NONE"
@ -27,12 +28,12 @@ import {DownloadClient} from '../imageknife/networkmanage/DownloadClient'
import {IDataFetch} from '../imageknife/networkmanage/IDataFetch'
import {ParseResClient} from '../imageknife/resourcemanage/ParseResClient'
import {IResourceFetch} from '../imageknife/resourcemanage/IResourceFetch'
import {ImageKnifeData} from '../imageknife/ImageKnifeData'
import {ImageKnifeData,ImageKnifeType} from '../imageknife/ImageKnifeData'
import {FileUtils} from '../cache/FileUtils'
import {FileReader} from '../cache/FileReader'
import image from "@ohos.multimedia.image"
import featureAbility from '@ohos.ability.featureAbility';
import {CompressBuilder} from "../imageknife/compress/CompressBuilder"
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
export class ImageKnife {
static readonly SEPARATOR: string = '/'
@ -49,11 +50,15 @@ export class ImageKnife {
private pendingRequest: Array<RequestOption>;
private fileTypeUtil: FileTypeUtil; // 通用文件格式辨别
private diskCacheFolder: string = "ImageKnifeDiskCache"
private svgAndGifFolder: string = "svgAndGifFolder"; // svg和gif的文件路径地址
private svgAndGifCommitFile: string = "svgAndGifCommitFile" // svg和gif提交记录
private defaultListener: AsyncCallback<ImageKnifeData>; // 全局监听器
// gifWorker
private gifWorker;
private defaultLifeCycle: IDrawLifeCycle;
private constructor(imgCtx) {
this.imageKnifeContext = imgCtx;
@ -71,7 +76,6 @@ export class ImageKnife {
// 初始化本地 文件保存
this.filesPath = this.imageKnifeContext.filesDir;
this.initSvgAndGifEnvironment();
this.runningRequest = new Array();
this.pendingRequest = new Array();
@ -103,16 +107,6 @@ export class ImageKnife {
return this.fileTypeUtil;
}
getSvgAndGifFolder(): string{
return this.svgAndGifFolder;
}
setSvgAndGifFolder(folderPath: string){
this.svgAndGifFolder = folderPath;
}
getImageKnifeContext() {
return this.imageKnifeContext;
}
@ -125,31 +119,21 @@ export class ImageKnife {
return this.defaultListener;
}
private initSvgAndGifEnvironment() {
let folderExist = FileUtils.getInstance().existFolder(this.filesPath + "/" + this.svgAndGifFolder)
let fileExist =
FileUtils.getInstance()
.exist(this.filesPath + "/" + this.svgAndGifFolder + "/" + this.svgAndGifCommitFile)
if (folderExist && fileExist) {
// 创建完成,需要删除上次使用的文件
var fileReader = new FileReader(this.filesPath + "/" + this.svgAndGifFolder + "/" + this.svgAndGifCommitFile)
var line: string = ''
while (!fileReader.isEnd()) {
line = fileReader.readLine()
line = line.replace('\n', "").replace('\r', "")
FileUtils.getInstance().deleteFile(this.filesPath + "/" + this.svgAndGifFolder + "/" + line)
}
FileUtils.getInstance().clearFile(this.filesPath + "/" + this.svgAndGifFolder + "/" + this.svgAndGifCommitFile)
} else {
if (!folderExist) {
FileUtils.getInstance().createFolder(this.filesPath + "/" + this.svgAndGifFolder);
}
if (!fileExist) {
FileUtils.getInstance().createFile(this.filesPath + "/" + this.svgAndGifFolder + "/" + this.svgAndGifCommitFile)
}
}
setGifWorker(worker){
this.gifWorker = worker
}
getGifWorker(){
return this.gifWorker;
}
getDefaultLifeCycle(){
return this.defaultLifeCycle;
}
setDefaultLifeCycle(viewLifeCycle:IDrawLifeCycle){
this.defaultLifeCycle = viewLifeCycle;
}
private static sInstance: ImageKnife;
@ -195,9 +179,7 @@ export class ImageKnife {
preload(request: RequestOption) {
// 每个request 公共信息补充
request.setFilesPath(this.filesPath);
// svg特殊处理
request._svgAndGifFolder = this.svgAndGifFolder;
request._svgAndGifCommitFile = this.svgAndGifCommitFile;
return this.parseSource(request);
}
@ -211,16 +193,16 @@ export class ImageKnife {
// 每个request 公共信息补充
request.setFilesPath(this.filesPath);
// svg特殊处理
request._svgAndGifFolder = this.svgAndGifFolder;
request._svgAndGifCommitFile = this.svgAndGifCommitFile;
// 首先执行占位图 解析任务
if (request.placeholderSrc) {
PlaceHolderManager.execute(request)
}
// 其次执行重试占位图 解析任务
if (request.retryholderSrc) {
RetryHolderManager.execute(request)
}
// 其次解析错误占位图
// 最后解析错误占位图
if (request.errorholderSrc) {
ErrorHolderManager.execute(request)
}
@ -374,9 +356,7 @@ export class ImageKnife {
parseSource(request: RequestOption) {
if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') {
let imageKnifeData = new ImageKnifeData();
imageKnifeData.imageKnifeType = ImageKnifeData.PIXELMAP
imageKnifeData.imageKnifeValue = request.loadSrc as PixelMap
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap)
request.loadComplete(imageKnifeData);
} else
if (typeof request.loadSrc == 'string') {

View File

@ -13,98 +13,176 @@
* limitations under the License.
*/
import {ImageKnifeOption} from '../imageknife/ImageKnifeOption'
import {TransformType} from '../imageknife/transform/TransformType'
import {RequestOption} from '../imageknife/RequestOption'
import {ImageKnifeData} from '../imageknife/ImageKnifeData'
import {PixelMapPack} from '../imageknife/PixelMapPack'
import { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
import { TransformType } from '../imageknife/transform/TransformType'
import { RequestOption } from '../imageknife/RequestOption'
import { ImageKnifeData } from '../imageknife/ImageKnifeData'
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
@Component
export struct ImageKnifeComponent {
@Watch('watchImageKnifeOption') @Link imageKnifeOption: ImageKnifeOption;
@State imageKnifePixelMapPack: PixelMapPack = new PixelMapPack();
@State imageKnifeResource: Resource = undefined
@State imageKnifeString: string = ''
@State normalPixelMap: boolean = false;
@State normalResource: boolean = true;
previousData: ImageKnifeData = null;
nowData: ImageKnifeData = null;
@State percentVisible: Visibility = Visibility.Visible
@State retryVisible: Visibility = Visibility.Visible
@State imageVisible: Visibility = Visibility.Visible
@State percent: string = '0%'
@State percentWidth: string = '0%';
@State percentHeight: string = '0%';
@State retryWidth: string = '0%';
@State retryHeight: string = '0%';
@State imageWidth: string = '100%';
@State imageHeight: string = '100%';
@State imageKnifeRetry: Resource = undefined;
// 有效onAreaChanged触发计数
private onAreaCount:number = 0
hasRetry:boolean = false;
@State componentWidth: string = '100%'
@State componentHeight: string = '100%'
@State gifPixelMap:PixelMap = undefined;
drawLifeCycle: IDrawLifeCycle
autoPlay = true
private preSize: {
width: string,
height: string
} = { width: '0', height: '0' }
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private hasDisplayRetryholder = false;
private currentWidth: number = 0
private currentHeight: number = 0
// 定时器id
private gifTimerId: number = 0
// 完整gif播放时间
private gifLoopDuration: number = 0
private startGifLoopTime: number = 0
private endGifLoopTime: number = 0
defaultLifeCycle: IDrawLifeCycle = {
// 展示占位图
displayPlaceholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawPlaceholder(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
},
// 展示加载进度
displayProgress: (context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawProgress(context, progress, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
},
// 展示缩略图
displayThumbSizeMultiplier: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawThumbSizeMultiplier(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
},
// 展示主图
displayMainSource: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawMainSource(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
},
// 展示重试图层
displayRetryholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawRetryholder(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
},
// 展示失败占位图
displayErrorholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawErrorholder(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
return true;
}
}
build() {
Stack() {
Text(this.percent)
.fontSize(this.imageKnifeOption.size ? Math.min(this.imageKnifeOption.size.height, this.imageKnifeOption.size.width) / 2 : 24)
.fontWeight(FontWeight.Bold)
.visibility(this.retryVisible)
.width(this.percentWidth)
.height(this.percentHeight)
Image(this.imageKnifeRetry)
.onClick(()=>{
this.retryClick();
})
.visibility(this.retryVisible)
.width(this.retryWidth)
.height(this.retryHeight)
Image(this.normalPixelMap ? this.imageKnifePixelMapPack.pixelMap : (this.normalResource ? this.imageKnifeResource : this.imageKnifeString))
.objectFit(this.imageKnifeOption.imageFit ? this.imageKnifeOption.imageFit : ImageFit.Fill)
.visibility(this.imageVisible)
.width(this.imageWidth)
.height(this.imageHeight)
}
.width(this.imageKnifeOption.size ? this.imageKnifeOption.size.width : '100%')
.height(this.imageKnifeOption.size ? this.imageKnifeOption.size.height : '100%')
.backgroundColor(this.imageKnifeOption.backgroundColor ? this.imageKnifeOption.backgroundColor : Color.White)
.margin(this.imageKnifeOption.margin ? this.imageKnifeOption.margin : { left: 0, top: 0, right: 0, bottom: 0 })
Canvas(this.context)
.width(this.componentWidth)
.height(this.componentHeight)
.onAreaChange((oldValue: Area, newValue: Area) => {
this.currentWidth = newValue.width as number
this.currentHeight = newValue.height as number
console.log('onAreaChange stack currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight)
if (this.onAreaCount > 0) {
this.onAreaCount--;
this.imageKnifeExecute()
}
})
.backgroundColor(this.imageKnifeOption.backgroundColor ? this.imageKnifeOption.backgroundColor : Color.White)
.margin(this.imageKnifeOption.margin ? this.imageKnifeOption.margin : { left: 0, top: 0, right: 0, bottom: 0 })
.onReady(() => {
})
.onClick(() => {
if (this.imageKnifeOption.canRetryClick && this.hasDisplayRetryholder) {
this.retryClick()
}
})
}
watchImageKnifeOption() {
this.imageKnifeExecute();
console.log('watchImageKnifeOption is happened!')
if (this.imageKnifeOption.sizeAnimate) {
animateTo(this.imageKnifeOption.sizeAnimate, () => {
this.resetGifData();
this.whetherWaitSize();
})
} else {
this.resetGifData();
this.whetherWaitSize();
}
}
whetherWaitSize(){
this.componentWidth = this.imageKnifeOption.size.width
this.componentHeight = this.imageKnifeOption.size.height
if(this.newSizeEqualPreSize(this.imageKnifeOption.size)){
console.log('whetherWaitSize 宽高不变 直接发送请求')
this.imageKnifeExecute()
}else{
this.onAreaCount ++;
// waitSize changed
this.preSize = this.imageKnifeOption.size
console.log('whetherWaitSize 宽高改变 等待组件回调onAreaChange后发送请求')
}
}
retryClick(){
this.hasRetry = true;
newSizeEqualPreSize(newSize:{width: string, height: string}):boolean{
if(this.preSize.width == newSize.width && this.preSize.height == newSize.height){
return true;
}
return false;
}
retryClick() {
this.hasDisplayRetryholder = false;
this.imageKnifeExecute();
}
aboutToAppear() {
console.log('imageKnifeComponent aboutToAppear happened!')
this.imageKnifeExecute();
this.onAreaCount ++;
this.componentWidth = this.imageKnifeOption.size.width
this.componentHeight = this.imageKnifeOption.size.height
}
configNecessary(request: RequestOption){
configNecessary(request: RequestOption) {
request.load(this.imageKnifeOption.loadSrc)
.addListener((err, data) => {
console.log('request.load callback')
this.imageKnifeChangeSource(data)
this.animateTo('image');
return false;
})
.addListener((err, data) => {
console.log('request.load callback')
this.displayMainSource(data)
return false;
})
if (this.imageKnifeOption.size) {
request.setImageViewSize(this.imageKnifeOption.size)
let realSize = {
width: this.currentWidth,
height:this.currentHeight
}
request.setImageViewSize(realSize)
}
}
}
configCacheStrategy(request: RequestOption){
configCacheStrategy(request: RequestOption) {
if (this.imageKnifeOption.onlyRetrieveFromCache) {
request.retrieveDataFromCache(this.imageKnifeOption.onlyRetrieveFromCache)
}
@ -119,36 +197,30 @@ export struct ImageKnifeComponent {
if (this.imageKnifeOption.allCacheInfoCallback) {
request.addAllCacheInfoCallback(this.imageKnifeOption.allCacheInfoCallback)
}
}
}
configDisplay(request: RequestOption){
if(this.imageKnifeOption.animateDuration >= 0){
request.animateDuration = this.imageKnifeOption.animateDuration;
}
configDisplay(request: RequestOption) {
if (this.imageKnifeOption.placeholderSrc) {
request.placeholder(this.imageKnifeOption.placeholderSrc, (data) => {
console.log('request.placeholder callback')
this.imageKnifeChangeSource(data)
this.animateTo('image');
this.displayPlaceholder(data)
})
}
if (this.imageKnifeOption.thumbSizeMultiplier) {
request.thumbnail(this.imageKnifeOption.thumbSizeMultiplier, (data) => {
console.log('request.thumbnail callback')
this.imageKnifeChangeSource(data)
this.animateTo('image');
})
this.displayThumbSizeMultiplier(data)
}, this.imageKnifeOption.thumbSizeDelay)
}
if (this.imageKnifeOption.errorholderSrc) {
request.errorholder(this.imageKnifeOption.errorholderSrc, (data) => {
console.log('request.errorholder callback')
this.imageKnifeChangeSource(data)
this.animateTo('image');
this.displayErrorholder(data)
})
}
if (this.imageKnifeOption.retryholderSrc) {
this.imageKnifeRetry = this.imageKnifeOption.retryholderSrc
}
if (this.imageKnifeOption.transform) {
this.requestAddTransform(request)
}
@ -164,164 +236,301 @@ export struct ImageKnifeComponent {
if (this.imageKnifeOption.displayProgress) {
request.addProgressListener((percentValue: string) => {
request.addProgressListener((percentValue: number) => {
// 如果进度条百分比 未展示大小,展示其动画
this.percent = percentValue;
if(this.imageKnifeOption.displayProgressListener){
this.imageKnifeOption.displayProgressListener(percentValue);
}
this.animateTo('progress');
this.displayProgress(percentValue)
})
}
if(this.imageKnifeOption.retryLoad){
request.addRetryListener((error: any) => {
if (this.imageKnifeOption.retryholderSrc) {
request.retryholder(this.imageKnifeOption.retryholderSrc, (data) => {
console.log("RetryListener callback!")
this.animateTo('retry');
this.hasDisplayRetryholder = true
this.displayRetryholder(data)
})
}
}
// imageknife 第一次启动和数据刷新后重新发送请求
imageKnifeExecute() {
let request = new RequestOption();
this.configNecessary(request);
this.configCacheStrategy(request);
this.configDisplay(request);
let request = new RequestOption();
this.configNecessary(request);
this.configCacheStrategy(request);
this.configDisplay(request);
globalThis.ImageKnife.call(request);
}
imageKnifeChangeSource(data:ImageKnifeData) {
this.imageKnifeSpecialFixed(data);
}
displayPlaceholder(data: ImageKnifeData) {
displayPixelMap(data:ImageKnifeData){
console.log('displayPixelMap start')
let pixelMapPack2 = new PixelMapPack();
pixelMapPack2.pixelMap = data.imageKnifeValue as PixelMap;
this.imageKnifePixelMapPack = pixelMapPack2;
this.normalPixelMap = true;
this.normalResource = true;
console.log('displayPixelMap end')
}
displayResource(data:ImageKnifeData){
console.log('displayResource start')
this.imageKnifeResource = data.imageKnifeValue as Resource;
this.normalPixelMap = false;
this.normalResource = true;
console.log('displayResource end')
}
displayString(data:ImageKnifeData){
console.log('displayString start')
let imageKnifeNeedStr = 'file://' + data.imageKnifeValue;
console.log('imageKnifeNeedStr='+imageKnifeNeedStr)
this.imageKnifeString = imageKnifeNeedStr;
this.normalPixelMap = false;
this.normalResource = false;
console.log('displayString end')
}
imageKnifeSpecialFixed(data:ImageKnifeData) {
if (data.isPixelMap()) {
this.displayPixelMap(data);
}
else if (data.isString()) {
this.displayString(data);
} else if (data.isResource()) {
this.displayResource(data);
} else {
}
}
animateTo(name: string) {
if (name == 'progress') {
this.percentVisible = Visibility.Visible;
this.imageVisible = Visibility.Hidden;
this.retryVisible = Visibility.Hidden;
if (this.percentWidth == '0%' || this.percentHeight == '0%') {
animateTo({ duration: this.imageKnifeOption.animateDuration , curve: Curve.Linear }, () => {
this.percentWidth = '100%';
this.percentHeight = '100%';
this.imageWidth = '0%';
this.imageHeight = '0%';
this.retryWidth = '0%';
this.retryHeight = '0%';
})
}
} else if (name == 'image') {
this.imageVisible = Visibility.Visible;
this.percentVisible = Visibility.Hidden;
this.retryVisible = Visibility.Hidden;
if (this.imageWidth == '0%' || this.imageHeight == '0%') {
animateTo({ duration: this.imageKnifeOption.animateDuration, curve: Curve.Linear }, () => {
this.imageWidth = '100%';
this.imageHeight = '100%';
this.percentWidth = '0%';
this.percentHeight = '0%';
this.retryWidth = '0%';
this.retryHeight = '0%';
})
}
} else if (name == 'retry') {
this.retryVisible = Visibility.Visible;
this.imageVisible = Visibility.Hidden;
this.percentVisible = Visibility.Hidden;
if (this.retryWidth == '0%' || this.retryHeight == '0%') {
animateTo({ duration: this.imageKnifeOption.animateDuration, curve: Curve.Linear }, () => {
this.imageWidth = '0%';
this.imageHeight = '0%';
this.percentWidth = '0%';
this.percentHeight = '0%';
this.retryWidth = '100%';
this.retryHeight = '100%';
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayPlaceholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayPlaceholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayPlaceholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
requestAddTransform(request:RequestOption){
if(TransformType.BlurTransformation == this.imageKnifeOption.transform.transformType){
displayProgress(percent: number) {
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayProgress', this.context, percent, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayProgress', this.context, percent, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayProgress(this.context, percent, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
displayThumbSizeMultiplier(data: ImageKnifeData) {
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayThumbSizeMultiplier', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayThumbSizeMultiplier', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayThumbSizeMultiplier(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
displayMainSource(data: ImageKnifeData) {
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayMainSource', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayMainSource', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayMainSource(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
displayRetryholder(data: ImageKnifeData) {
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayRetryholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayRetryholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayRetryholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
displayErrorholder(data: ImageKnifeData) {
if (!this.drawLifeCycleHasConsumed(this.imageKnifeOption.drawLifeCycle, 'displayErrorholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
if (!this.drawLifeCycleHasConsumed(globalThis.ImageKnife.getDefaultLifeCycle(), 'displayErrorholder', this.context, data, this.imageKnifeOption,
this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})) {
this.defaultLifeCycle.displayErrorholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => {
this.setGifTimeId(gifTimeId)
})
}
}
}
drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
console.log('default drawPlaceholder start!')
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
console.log('imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER
context.save();
context.clearRect(0, 0, compWidth, compHeight)
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
console.log('default drawPlaceholder end!')
})
}
drawProgress(context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
let pi = Math.PI * 2 / 100; //pi 讲圆的周长划分为100份
let rate = progress - 25;
let diameter = compWidth > compHeight ? compHeight : compWidth
context.lineWidth = Math.floor(diameter * 0.03)
context.lineCap = "round"
context.fillStyle = "#10a5ff"
context.font = Math.floor(diameter * 0.3 * px2vp(1))+'px'
let x0 = (compWidth - diameter) / 2.0 + Math.floor(diameter * 0.5)
let y0 = (compHeight - diameter) / 2.0 + Math.floor(diameter * 0.1)
let x1 = (compWidth - diameter) / 2.0 + Math.floor(diameter * 0.5)
let y1 = (compHeight - diameter) / 2.0 + Math.floor(diameter * 0.8)
let gradient = context.createLinearGradient(x0, y0, x1, y1)
gradient.addColorStop(0, "#11ffe4")
gradient.addColorStop(0.5, "#03c6fd")
gradient.addColorStop(1, "#10a5ff")
context.clearRect(0, 0, compWidth, compHeight)
context.shadowBlur = 0
context.beginPath()
context.strokeStyle = "#15222d"
let radius = Math.floor(diameter * 0.3)
let arcX = compWidth / 2.0
let arcY = compHeight / 2.0
context.arc(arcX, arcY, radius, 0, Math.PI * 2, true)
context.stroke()
context.beginPath()
let showText = rate + 25 + '%'
let metrics = context.measureText(showText)
let textX = (compWidth / 2.0) - metrics.width / 2.0
let textY = (compHeight / 2.0) + metrics.height * 0.3
context.fillText(showText, textX, textY)
context.stroke()
context.beginPath()
context.strokeStyle = gradient
context.arc(arcX, arcY, radius, pi * -25, pi * rate)
context.stroke();
}
drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
console.log('default drawThumbSizeMultiplier start!')
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
console.log('imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER
context.save();
context.clearRect(0, 0, compWidth, compHeight)
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
console.log('default drawThumbSizeMultiplier end!')
})
}
drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
console.log('default drawMainSource start!')
if (data.isPixelMap()) {
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
console.log('imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType)
context.save();
context.clearRect(0, 0, compWidth, compHeight)
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
console.log('default drawMainSource end!')
})
} else if (data.isGIFFrame()) {
this.drawGIFFrame(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
}
}
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
console.log('default drawRetryholder start!')
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
console.log('imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER
context.save();
context.clearRect(0, 0, compWidth, compHeight)
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
console.log('default drawRetryholder end!')
})
}
drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
console.log('default drawErrorholder start!')
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
console.log('imageinfo widht =' + imageInfo.size.width + ' height=' + imageInfo.size.height)
let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER
context.save();
context.clearRect(0, 0, compWidth, compHeight)
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
console.log('default drawErrorholder end!')
})
}
requestAddTransform(request: RequestOption) {
if (TransformType.BlurTransformation == this.imageKnifeOption.transform.transformType) {
request.blur(this.imageKnifeOption.transform.blur)
}else if(TransformType.BrightnessFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.BrightnessFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.brightnessFilter(this.imageKnifeOption.transform.brightnessFilter)
}else if(TransformType.ContrastFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.ContrastFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.contrastFilter(this.imageKnifeOption.transform.contrastFilter)
}else if(TransformType.CropCircleTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CropCircleTransformation == this.imageKnifeOption.transform.transformType) {
request.cropCircle()
}else if(TransformType.CropCircleWithBorderTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CropCircleWithBorderTransformation == this.imageKnifeOption.transform.transformType) {
request.cropCircleWithBorder(this.imageKnifeOption.transform.cropCircleWithBorder.border, this.imageKnifeOption.transform.cropCircleWithBorder.obj)
}else if(TransformType.CropSquareTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CropSquareTransformation == this.imageKnifeOption.transform.transformType) {
request.cropSquare()
}else if(TransformType.CropTransformation == this.imageKnifeOption.transform.transformType){
request.crop(this.imageKnifeOption.transform.crop.width,this.imageKnifeOption.transform.crop.height,this.imageKnifeOption.transform.crop.cropType)
}else if(TransformType.GrayscaleTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CropTransformation == this.imageKnifeOption.transform.transformType) {
request.crop(this.imageKnifeOption.transform.crop.width, this.imageKnifeOption.transform.crop.height, this.imageKnifeOption.transform.crop.cropType)
} else if (TransformType.GrayscaleTransformation == this.imageKnifeOption.transform.transformType) {
request.grayscale()
}else if(TransformType.InvertFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.InvertFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.invertFilter()
}else if(TransformType.MaskTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.MaskTransformation == this.imageKnifeOption.transform.transformType) {
request.mask(this.imageKnifeOption.transform.mask)
}else if(TransformType.PixelationFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.PixelationFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.pixelationFilter(this.imageKnifeOption.transform.pixelationFilter)
}else if(TransformType.RotateImageTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.RotateImageTransformation == this.imageKnifeOption.transform.transformType) {
request.rotateImage(this.imageKnifeOption.transform.rotateImage)
}else if(TransformType.RoundedCornersTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.RoundedCornersTransformation == this.imageKnifeOption.transform.transformType) {
request.roundedCorners(this.imageKnifeOption.transform.roundedCorners)
}else if(TransformType.SepiaFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.SepiaFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.sepiaFilter()
}else if(TransformType.SketchFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.SketchFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.sketchFilter()
}else if(TransformType.SwirlFilterTransformation == this.imageKnifeOption.transform.transformType){
} else if (TransformType.SwirlFilterTransformation == this.imageKnifeOption.transform.transformType) {
request.swirlFilter(this.imageKnifeOption.transform.swirlFilter)
}else if(TransformType.CenterCrop == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CenterCrop == this.imageKnifeOption.transform.transformType) {
request.centerCrop()
}else if(TransformType.CenterInside == this.imageKnifeOption.transform.transformType){
} else if (TransformType.CenterInside == this.imageKnifeOption.transform.transformType) {
request.centerInside()
}else if(TransformType.FitCenter == this.imageKnifeOption.transform.transformType){
} else if (TransformType.FitCenter == this.imageKnifeOption.transform.transformType) {
request.fitCenter()
}
}
aboutToDisappear() {
this.resetGifData();
}
onPageShow() {
@ -332,6 +541,278 @@ export struct ImageKnifeComponent {
onBackPress() {
}
setGifTimeId(timeId: number) {
this.gifTimerId = timeId;
}
private drawLifeCycleHasConsumed<K, T>(drawLifeCycle: IDrawLifeCycle, methodName: string,
context: CanvasRenderingContext2D, data: K, imageKnifeOption: T, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void
) {
if (drawLifeCycle && drawLifeCycle[methodName]) {
return drawLifeCycle[methodName](context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
}
return false;
}
private drawGIFFrame(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
let frames = data.drawGIFFrame.imageGIFFrames as GIFFrame[]
console.log('gifFrameLength =' + frames.length);
if (imageKnifeOption.gif && (typeof (imageKnifeOption.gif.seekTo) == 'number') && imageKnifeOption.gif.seekTo >= 0) {
this.autoPlay = false;
context.clearRect(0, 0, compWidth, compHeight)
this.drawSeekToFrame(frames, context, compWidth, compHeight)
} else {
this.autoPlay = true
context.clearRect(0, 0, compWidth, compHeight)
this.renderFrames.bind(this, frames, 0, context, compWidth, compHeight)()
}
}
private resetGifData() {
clearTimeout(this.gifTimerId)
this.gifLoopDuration = 0;
this.startGifLoopTime = 0;
this.endGifLoopTime = 0;
}
/**
* 绘制直接到第几帧方法由于gif非第一帧数据可能是不全的这里采用逐帧渲染的方式来绘制保证图像的完整性
*/
private drawSeekToFrame(frames: GIFFrame[], context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
for (let i = 0; i < this.imageKnifeOption.gif; i++) {
this.drawFrame(frames, i, context, compWidth, compHeight);
}
}
private renderFrames(frames: GIFFrame[], index: number, context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
console.log('renderFrames frames length =' + frames.length)
let start = new Date().getTime();
if (index === 0) {
// 如果是第一帧,我们只从开始渲染前记录时间
this.startGifLoopTime = start;
}
// draw Frame
this.drawFrame(frames, index, context, compWidth, compHeight);
// 记录渲染结束时间点
let end = new Date().getTime();
let diff = end - start
if (this.autoPlay) {
// 理论上该帧在屏幕上保留的时间
let stayTime = frames[index].delay;
if (this.imageKnifeOption.gif && this.imageKnifeOption.gif.speedFactory) {
stayTime = frames[index].delay / (this.imageKnifeOption.gif.speedFactory * 1.0);
}
// 减去程序执行消耗,剩余的准确延迟时间
let delayTime = Math.max(0, Math.floor(stayTime - diff));
this.endGifLoopTime = end;
// 当前gif到第N帧,所对应的N渲染时间和N-1的停留时间。第一帧只有渲染时间没有停留时间
let loopStayTime = this.endGifLoopTime - this.startGifLoopTime;
this.startGifLoopTime = end;
// 整个gif累计的时长;
this.gifLoopDuration += loopStayTime;
// 返回gif一次循环结束回调并且把当前循环的时间给出
if (index === (frames.length - 1) && this.imageKnifeOption.gif && this.imageKnifeOption.gif.loopFinish) {
this.imageKnifeOption.gif.loopFinish(this.gifLoopDuration)
this.gifLoopDuration = 0;
}
// update the frame index
index++
if (index >= frames.length) {
index = 0;
}
this.gifTimerId = setTimeout(this.renderFrames.bind(this, frames, index, context, compWidth, compHeight), delayTime)
}
}
private drawFrame(frames: GIFFrame[], index: number, context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
// get current frame
let frame = frames[index];
if (!frame || !context) {
// 数据保护,绘制保护
return;
}
if (!frame['drawPixelMap']) {
// 解码之后已经转为PixelMap
} else {
this.canvasDrawPixelMap(frames, index, context, compWidth, compHeight)
}
}
// 具体绘制过程
private canvasDrawPixelMap(frames: GIFFrame[], index: number, context: CanvasRenderingContext2D, compWidth: number, compHeight: number) {
console.log('canvasDrawPixelMap index=' + index)
let frame = frames[index];
let pixelmap = frame['drawPixelMap']
let disposal = 0
// disposal value is from preFrame
if (index >= 1) {
let preFrame = frames[index-1]
disposal = preFrame.disposalType
}
if (disposal === FrameDisposalType.DISPOSE_RestoreBackground) {
context.clearRect(0, 0, compWidth, compHeight)
}
let scaleType = (typeof this.imageKnifeOption.mainScaleType == 'number') ? this.imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
context.save();
let frameW = frames[0].dims.left+frames[0].dims.width
let frameH = frames[0].dims.top+frames[0].dims.height
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, compHeight,px2vp(frame.dims.left),px2vp(frame.dims.top))
// tips:worker如果不是在展示页面中创建,使用子线程回来的数据创建的图片,会导致canvas绘制不出来
context.restore();
console.log('default drawMainSource end!')
}
}
export enum FrameDisposalType {
// 0 - No disposal specified. The decoder is not required to take any action.
// 不使用处置方法
DISPOSE_Nothing = 0,
// 1-Do not dispose. The graphic is to be left in place.
// 不处置图形,把图形从当前位置移去
DISPOSE_NotResolve = 1,
// 2 - Restore to background color. The area used by the graphic must be restored to the background color.
// 回复到背景色
DISPOSE_RestoreBackground = 2,
// 3-Restore to previous
DISPOSE_PreviousStatus = 3
}
export enum ScaleType {
// 图像位于用户设置组件左上角显示,图像会缩放至全部展示
FIT_START = 1,
// 图像位于用户设置组件右下角显示,图像会缩放至全部展示
FIT_END = 2,
// 图像位于用户设置组件居中,图像会缩放至全部展示
FIT_CENTER = 3,
// 图像绝对居中展示,不缩放
CENTER = 4,
// 宽高中,短的部分缩放至组件大小,超出的全部裁剪
CENTER_CROP = 5,
// 图像拉伸至组件大小
FIT_XY = 6,
// 如果图像大于组件则执行FIT_CENTER,小于组件则CENTER
CENTER_INSIDE = 7,
// 如果不想适配,直接展示原图大小
NONE = 8
}
export class ScaleTypeHelper {
static drawImageWithScaleType(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | ImageBitmap, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number) {
let scaleW = compWidth / imageWidth
let scaleH = compHeight / imageHeight
let minScale = scaleW > scaleH ? scaleH : scaleW
let maxScale = scaleW > scaleH ? scaleW : scaleH
switch (scaleType) {
case ScaleType.FIT_START:
ScaleTypeHelper.drawFitStart(context, source, minScale, imageWidth, imageHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.FIT_END:
ScaleTypeHelper.drawFitEnd(context, source, minScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.FIT_CENTER:
ScaleTypeHelper.drawFitCenter(context, source, minScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.CENTER:
ScaleTypeHelper.drawCenter(context, source, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.CENTER_CROP:
ScaleTypeHelper.drawCenterCrop(context, source, maxScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.FIT_XY:
ScaleTypeHelper.drawFitXY(context, source, scaleW, scaleH, imageWidth, imageHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.CENTER_INSIDE:
ScaleTypeHelper.drawCenterInside(context, source, minScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
break;
case ScaleType.NONE:
ScaleTypeHelper.drawNone(context, source, imageWidth, imageHeight, imageOffsetX,imageOffsetY)
break;
default:
ScaleTypeHelper.drawNone(context, source, imageWidth, imageHeight, imageOffsetX,imageOffsetY)
break
}
}
static drawFitStart(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, minScale: number, imageWidth: number, imageHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
context.setTransform(minScale, 0, 0, minScale, 0, 0)
let dx = 0
let dy = 0
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawFitEnd(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
context.setTransform(minScale, 0, 0, minScale, 0, 0)
let dx = (compWidth - imageWidth * minScale) / (minScale * 1.0);
let dy = (compHeight - imageHeight * minScale) / (minScale * 1.0);
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawFitCenter(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
context.setTransform(minScale, 0, 0, minScale, 0, 0)
let dx = (compWidth - imageWidth * minScale) / (minScale * 2.0);
let dy = (compHeight - imageHeight * minScale) / (minScale * 2.0);
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawCenter(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
let dx = (compWidth - imageWidth) / 2.0;
let dy = (compHeight - imageHeight) / 2.0;
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawCenterCrop(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, maxScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
context.setTransform(maxScale, 0, 0, maxScale, 0, 0)
let dx = (compWidth - imageWidth * maxScale) / (maxScale * 2.0);
let dy = (compHeight - imageHeight * maxScale) / (maxScale * 2.0);
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawFitXY(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, scaleW: number, scaleH: number, imageWidth: number, imageHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
context.setTransform(scaleW, 0, 0, scaleH, 0, 0)
let dx = 0;
let dy = 0;
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
static drawCenterInside(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, minScale: number, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
if (minScale < 1) {
ScaleTypeHelper.drawFitCenter(context, source, minScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
} else {
ScaleTypeHelper.drawCenter(context, source, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX,imageOffsetY)
}
}
static drawNone(context: CanvasRenderingContext2D, source: PixelMap | ImageBitmap, imageWidth: number, imageHeight: number, imageOffsetX?:number,imageOffsetY?:number) {
let dx = 0;
let dy = 0;
let dw = imageWidth;
let dh = imageHeight;
context.drawImage(source, dx+imageOffsetX, dy+imageOffsetY)
}
}

View File

@ -12,63 +12,73 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { GIFFrame } from './utils/gif/GIFFrame'
export enum ImageKnifeType {
PIXELMAP = 'PixelMap',
STRING = 'String',
RESOURCE = 'Resource',
GIFFRAME = 'GIFFrame'
}
export class DrawPixelMap {
imagePixelMap: PixelMap
}
export class DrawString {
imageString: string
}
export class DrawResource {
imageResource: Resource
}
export class DrawGIFFrame {
imageGIFFrames: GIFFrame[]
}
export class ImageKnifeData {
static PIXELMAP = 'PixelMap'
static STRING = 'string'
static RESOURCE ='Resource'
static SVG = 'svg';
static GIF = 'gif';
static JPG = 'jpg';
static PNG = 'png';
static BMP = 'bmp';
static WEBP = 'webp';
imageKnifeType: ImageKnifeType;
drawPixelMap: DrawPixelMap;
drawGIFFrame: DrawGIFFrame;
drawResource: DrawResource;
drawString: DrawString;
imageKnifeType:string = '';
imageKnifeValue:PixelMap|string|Resource;
imageKnifeSourceType:string = ''
isSvg():boolean{
return ImageKnifeData.SVG == this.imageKnifeSourceType;
static createImagePixelMap(type: ImageKnifeType, value: PixelMap) {
let data = new ImageKnifeData();
data.imageKnifeType = type;
data.drawPixelMap = new DrawPixelMap();
data.drawPixelMap.imagePixelMap = value;
return data;
}
isGif():boolean{
return ImageKnifeData.GIF == this.imageKnifeSourceType;
static createImageGIFFrame(type: ImageKnifeType, value: GIFFrame[]) {
let data = new ImageKnifeData();
data.imageKnifeType = type;
data.drawGIFFrame = new DrawGIFFrame();
data.drawGIFFrame.imageGIFFrames = value;
return data;
}
isJpg():boolean{
return ImageKnifeData.JPG == this.imageKnifeSourceType;
isPixelMap(): boolean {
return ImageKnifeType.PIXELMAP == this.imageKnifeType;
}
isPng():boolean{
return ImageKnifeData.PNG == this.imageKnifeSourceType;
isGIFFrame(): boolean {
return ImageKnifeType.GIFFRAME == this.imageKnifeType;
}
isBmp():boolean{
return ImageKnifeData.BMP == this.imageKnifeSourceType;
isString(): boolean {
return ImageKnifeType.STRING == this.imageKnifeType;
}
isWebp():boolean{
return ImageKnifeData.WEBP == this.imageKnifeSourceType;
isResource(): boolean {
return ImageKnifeType.RESOURCE == this.imageKnifeType;
}
isString():boolean{
return ImageKnifeData.STRING == this.imageKnifeType;
}
isPixelMap():boolean{
return ImageKnifeData.PIXELMAP == this.imageKnifeType;
}
isResource():boolean{
return ImageKnifeData.RESOURCE == this.imageKnifeType;
}
can2PixelMap(){ // 可以转换为PixelMap的数据源
return this.isPixelMap() || this.isBmp() || this.isJpg() || this.isWebp() || this.isPng();
}
}

View File

@ -0,0 +1,481 @@
/*
* Copyright (C) 2022 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 { ImageKnifeOption } from '../imageknife/ImageKnifeOption'
import { ImageKnifeData } from '../imageknife/ImageKnifeData'
import { GIFFrame } from '../imageknife/utils/gif/GIFFrame'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
import { ScaleTypeHelper,ScaleType } from '../imageknife/ImageKnifeComponent'
export class ImageKnifeDrawFactory{
/**
* 绘制PixelMap内容情况下,主图的椭圆裁剪,可添加边框
* @param borderWidth 外边框 borderWidth vp
* @param colorString 例如 "#FF00FF"
*/
public static createOvalLifeCycle(borderWidth:number, colorString:string):IDrawLifeCycle{
let viewLifeCycle = {
// 展示占位图
displayPlaceholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示加载进度
displayProgress: (context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
},
// 展示缩略图
displayThumbSizeMultiplier: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示主图
displayMainSource: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
if (data.isPixelMap()) {
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
context.clearRect(0,0,compWidth,compHeight)
context.save();
// 绘制适配后的图像
context.save();
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
// 使用 destination-in 裁剪出椭圆
context.save();
context.globalCompositeOperation = 'destination-in'
context.beginPath();
this.setOval(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0,borderWidth)
context.closePath();
context.fill()
context.restore();
// 给椭圆加上边框
context.save();
if(borderWidth > 0) {
context.strokeStyle = colorString
context.lineWidth = borderWidth;
context.globalCompositeOperation = 'source-over'
context.beginPath();
this.setOval(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0, borderWidth)
context.closePath();
context.stroke()
context.restore();
}
context.restore();
})
return true;
}
return false;
},
// 展示重试图层
displayRetryholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
},
// 展示失败占位图
displayErrorholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
}
}
return viewLifeCycle;
}
/**
* 绘制椭圆的计算和绘制,可借鉴,私有方法不对外提供
* @param context
* @param scaleType
* @param source
* @param imageWidth
* @param imageHeight
* @param compWidth
* @param compHeight
* @param imageOffsetX
* @param imageOffsetY
* @param borderWidth
*/
private static setOval(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | ImageBitmap, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number
,borderWidth:number) {
let scaleW = compWidth / imageWidth
let scaleH = compHeight / imageHeight
let minScale = scaleW > scaleH ? scaleH : scaleW
let maxScale = scaleW > scaleH ? scaleW : scaleH
let circleX = compWidth / 2
let circleY = compHeight / 2
switch (scaleType) {
case ScaleType.FIT_START:
circleX = imageWidth * minScale / 2
circleY = imageWidth * minScale / 2
context.ellipse(circleX,circleY, (imageWidth * minScale - borderWidth) / 2, (imageHeight * minScale - borderWidth) / 2, 0, 0, Math.PI * 2)
break
case ScaleType.FIT_END:
if(scaleW >= scaleH){
circleX = compWidth - imageWidth * minScale/2
circleY = imageHeight * minScale / 2
}else{
circleX = imageWidth * minScale / 2
circleY = compHeight - imageHeight * minScale / 2
}
context.ellipse(circleX, circleY, (imageWidth * minScale - borderWidth) / 2, (imageHeight * minScale - borderWidth) / 2, 0, 0, Math.PI * 2)
break
case ScaleType.FIT_CENTER:
circleX = compWidth / 2;
circleY = compHeight / 2;
context.ellipse(circleX, circleY, (imageWidth * minScale - borderWidth) / 2, (imageHeight * minScale - borderWidth) / 2, 0, 0, Math.PI * 2)
break
case ScaleType.CENTER:
circleX = compWidth / 2;
circleY = compWidth / 2;
let centerRadiusX = (Math.min(compWidth,imageWidth) - borderWidth)/2
let centerRadiusY = (Math.min(compHeight,imageHeight) -borderWidth)/2
context.ellipse(circleX, circleY, centerRadiusX, centerRadiusY, 0, 0, Math.PI * 2)
break
case ScaleType.CENTER_CROP:
// 肯定会占满全屏
circleX = compWidth / 2;
circleY = compHeight / 2;
let centerCropRadiusX = (compWidth-borderWidth)/2
let centerCropRadiusY = (compHeight-borderWidth)/2
context.ellipse(circleX, circleY, centerCropRadiusX, centerCropRadiusY, 0, 0, Math.PI * 2)
break
case ScaleType.FIT_XY:
context.ellipse(compWidth / 2, compHeight / 2, (imageWidth * scaleW - borderWidth) / 2, (imageHeight * scaleH - borderWidth) / 2, 0, 0, Math.PI * 2)
break
case ScaleType.CENTER_INSIDE:
if(minScale < 1){ // FIT_CENTER
circleX = compWidth / 2;
circleY = compHeight / 2;
context.ellipse(circleX,circleY,(imageWidth * minScale - borderWidth) / 2, (imageHeight * minScale - borderWidth) / 2, 0, 0, Math.PI * 2)
}else{ // CENTER
circleX = compWidth / 2;
circleY = compWidth / 2;
let centerRadiusX = (Math.min(compWidth,imageWidth) - borderWidth)/2
let centerRadiusY = (Math.min(compHeight,imageHeight) -borderWidth)/2
context.ellipse(circleX, circleY, centerRadiusX, centerRadiusY, 0, 0, Math.PI * 2)
}
break
case ScaleType.NONE:
circleX = Math.min(compWidth,imageWidth)/2
circleY = Math.min(compHeight,imageHeight)/2
let noneRadiusX = (Math.min(compWidth,imageWidth)-borderWidth)/2
let noneRadiusY = (Math.min(compHeight,imageHeight)-borderWidth)/2
context.ellipse(circleX,circleY,noneRadiusX,noneRadiusY,0,0, Math.PI*2)
break;
default:
break;
}
}
/**
* 绘制PixelMap内容情况下,主图的圆角裁剪,可添加边框
* @param borderWidth 边框宽度
* @param colorString 边框颜色string
* @param connerRadius 圆角半径
*/
public static createRoundLifeCycle(borderWidth:number, colorString:string, connerRadius:number){
let viewLifeCycle = {
// 展示占位图
displayPlaceholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示加载进度
displayProgress: (context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
},
// 展示缩略图
displayThumbSizeMultiplier: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示主图
displayMainSource: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
if (data.isPixelMap()) {
// @ts-ignore
data.drawPixelMap.imagePixelMap.getImageInfo().then((imageInfo) => {
let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
context.clearRect(0,0,compWidth,compHeight)
context.save();
// 绘制适配后的图像
context.save();
ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0)
context.restore();
// 通过 destination-in 裁剪出圆角
context.save();
context.globalCompositeOperation = 'destination-in'
this.setRect(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0,borderWidth,connerRadius)
context.fill()
context.restore();
if(borderWidth > 0){
// 为圆角添加边框
context.save();
context.strokeStyle = colorString
context.lineWidth = borderWidth
context.globalCompositeOperation = 'source-over'
this.setRect(context, scaleType, data.drawPixelMap.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight,0,0,borderWidth,connerRadius)
context.stroke()
context.restore();
}
context.restore();
console.log('TestImageKnifeOptionChangedPage4 drawMainSource end!')
})
return true;
}
return false;
},
// 展示重试图层
displayRetryholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
},
// 展示失败占位图
displayErrorholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
return false;
}
}
return viewLifeCycle;
}
/**
* 绘制圆角的计算,可借鉴,私有方法不对外提供
* @param context
* @param scaleType
* @param source
* @param imageWidth
* @param imageHeight
* @param compWidth
* @param compHeight
* @param imageOffsetX
* @param imageOffsetY
* @param borderWidth
* @param cornerRadius
*/
private static setRect(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | ImageBitmap, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number
,borderWidth:number,cornerRadius:number) {
let scaleW = compWidth / imageWidth
let scaleH = compHeight / imageHeight
let minScale = scaleW > scaleH ? scaleH : scaleW
let maxScale = scaleW > scaleH ? scaleW : scaleH
let x1 = borderWidth/2
let y1 = borderWidth/2
let w1 = compWidth - borderWidth;
let h1 = compWidth - borderWidth;
switch (scaleType) {
case ScaleType.FIT_START:
x1 = borderWidth/2
y1 = borderWidth/2
w1 = imageWidth * minScale - borderWidth;
h1 = imageHeight * minScale - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.FIT_END:
x1 = compWidth - imageWidth * minScale + borderWidth / 2
y1 = compHeight - imageHeight * minScale + borderWidth / 2
w1 = imageWidth * minScale - borderWidth;
h1 = imageHeight * minScale - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.FIT_CENTER:
x1 = (compWidth - imageWidth * minScale) / 2 + borderWidth / 2
y1 = (compHeight - imageHeight * minScale) / 2 + borderWidth / 2
w1 = imageWidth * minScale - borderWidth
h1 = imageHeight * minScale - borderWidth
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.CENTER:
x1 = Math.max(0,(compWidth - Math.min(compWidth, imageWidth)))/2 + borderWidth/2
y1 = Math.max(0,(compHeight - Math.min(compHeight, imageHeight)))/2 + borderWidth/2
w1 = Math.min(compWidth, imageWidth) - borderWidth;
h1 = Math.min(compHeight, imageHeight) - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.CENTER_CROP:
x1 = borderWidth/2
y1 = borderWidth/2
w1 = compWidth - borderWidth;
h1 = compWidth - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.FIT_XY:
x1 = borderWidth/2
y1 = borderWidth/2
w1 = compWidth - borderWidth;
h1 = compWidth - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break
case ScaleType.CENTER_INSIDE:
if(minScale < 1){ // FIT_CENTER
x1 = (compWidth - imageWidth * minScale) / 2 + borderWidth / 2
y1 = (compHeight - imageHeight * minScale) / 2 + borderWidth / 2
w1 = imageWidth * minScale - borderWidth
h1 = imageHeight * minScale - borderWidth
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
}else{ // CENTER
x1 = Math.max(0,(compWidth - Math.min(compWidth, imageWidth)))/2 + borderWidth/2
y1 = Math.max(0,(compHeight - Math.min(compHeight, imageHeight)))/2 + borderWidth/2
w1 = Math.min(compWidth, imageWidth) - borderWidth;
h1 = Math.min(compHeight, imageHeight) - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
}
break;
case ScaleType.NONE:
x1 = borderWidth/2
y1 = borderWidth/2
w1 = Math.min(compWidth, imageWidth) - borderWidth;
h1 = Math.min(compHeight, imageHeight) - borderWidth;
this.roundRect(context, x1, y1, w1, h1, cornerRadius)
break;
}
}
/**
* 绘制圆角的绘制,可借鉴,私有方法不对外提供
* @param context
* @param x
* @param y
* @param w
* @param h
* @param r
*/
private static roundRect(context: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number) {
if (w < 2 * r) {
r = w / 2;
}
if (h < 2 * r) {
r = h / 2;
}
context.beginPath();
context.moveTo(x + r, y);
context.arcTo(x + w, y, x + w, y + h, r);
context.arcTo(x + w, y + h, x, y + h, r);
context.arcTo(x, y + h, x, y, r);
context.arcTo(x, y, x + w, y, r);
context.closePath();
}
/**
* 绘制下载进度条
* @param fontColor 文字颜色
* @param fontSizeRate 文字占半径比率[0,1]
*/
public static createProgressLifeCycle(fontColor:string, fontSizeRate:number){
let viewLifeCycle: IDrawLifeCycle = {
// 展示占位图
displayPlaceholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示加载进度
displayProgress: (context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
this.drawDefaultProgress(context, progress, imageKnifeOption, compWidth, compHeight,fontColor,fontSizeRate,setGifTimeId)
return true;
},
// 展示缩略图
displayThumbSizeMultiplier: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示主图
displayMainSource: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示重试图层
displayRetryholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
},
// 展示失败占位图
displayErrorholder: (context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) => {
// @ts-ignore
return false;
}
}
return viewLifeCycle;
}
/**
* 绘制默认的网络下载百分比
* @param context
* @param progress
* @param imageKnifeOption
* @param compWidth
* @param compHeight
* @param fontColor
* @param fontSizeRate
* @param setGifTimeId
*/
private static drawDefaultProgress(context: CanvasRenderingContext2D, progress: number, imageKnifeOption: ImageKnifeOption,
compWidth: number, compHeight: number,
fontColor:string, fontSizeRate:number,setGifTimeId?: (timeId: number) => void,){
let pi = Math.PI * 2 / 100; //pi 讲圆的周长划分为100份
let rate = progress - 25;
let diameter = compWidth > compHeight ? compHeight : compWidth
context.lineWidth = Math.floor(diameter * 0.03)
context.lineCap = "round"
context.fillStyle = fontColor//"#10a5ff"
context.font = Math.floor(diameter * fontSizeRate *px2vp(1))+'px'
let x0 = (compWidth - diameter) / 2.0 + Math.floor(diameter * 0.5)
let y0 = (compHeight - diameter) / 2.0 + Math.floor(diameter * 0.1)
let x1 = (compWidth - diameter) / 2.0 + Math.floor(diameter * 0.5)
let y1 = (compHeight - diameter) / 2.0 + Math.floor(diameter * 0.8)
let gradient = context.createLinearGradient(x0, y0, x1, y1)
gradient.addColorStop(0, "#11ffe4")
gradient.addColorStop(0.5, "#03c6fd")
gradient.addColorStop(1, "#10a5ff")
context.clearRect(0, 0, compWidth, compHeight)
context.shadowBlur = 0
context.beginPath()
context.strokeStyle = "#15222d"
let radius = Math.floor(diameter * 0.3)
let arcX = compWidth / 2.0
let arcY = compHeight / 2.0
context.arc(arcX, arcY, radius, 0, Math.PI * 2, true)
context.stroke()
context.beginPath()
let showText = rate + 25 + '%'
let metrics = context.measureText(showText)
let textX = (compWidth / 2.0) - metrics.width / 2.0
let textY = (compHeight / 2.0) + metrics.height * 0.3
context.fillText(showText, textX, textY)
context.stroke()
context.beginPath()
context.strokeStyle = gradient
context.arc(arcX, arcY, radius, pi * -25, pi * rate)
context.stroke();
}
}

View File

@ -13,17 +13,26 @@
* limitations under the License.
*/
import {AUTOMATIC} from "../cache/diskstrategy/enum/AUTOMATIC"
import {DiskStrategy} from "../cache/diskstrategy/DiskStrategy"
import {BaseTransform} from "../imageknife/transform/BaseTransform"
import {TransformType} from "../imageknife/transform/TransformType"
import {CropType} from "../imageknife/transform/CropTransformation"
import {AllCacheInfo, IAllCacheInfoCallback} from "../imageknife/interface/IAllCacheInfoCallback"
import { AUTOMATIC } from '../cache/diskstrategy/enum/AUTOMATIC'
import { DiskStrategy } from '../cache/diskstrategy/DiskStrategy'
import { BaseTransform } from '../imageknife/transform/BaseTransform'
import { TransformType } from '../imageknife/transform/TransformType'
import { CropType } from '../imageknife/transform/CropTransformation'
import { AllCacheInfo, IAllCacheInfoCallback } from '../imageknife/interface/IAllCacheInfoCallback'
import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle'
import { ScaleType } from '../imageknife/ImageKnifeComponent'
export class ImageKnifeOption {
// 组件大小
size: {
width: string,
height: string
};
// 主图资源
loadSrc: string | PixelMap | Resource;
mainScaleType?: ScaleType = ScaleType.FIT_CENTER
// 磁盘缓存策略
strategy?: DiskStrategy = new AUTOMATIC();
@ -33,59 +42,83 @@ export class ImageKnifeOption {
// 占位图
placeholderSrc?: PixelMap | Resource;
placeholderScaleType?: ScaleType = ScaleType.FIT_CENTER
// 失败占位图
errorholderSrc?: PixelMap | Resource;
errorholderSrcScaleType?: ScaleType = ScaleType.FIT_CENTER
// 重试占位图
retryholderSrc?: Resource;
retryholderScaleType?: ScaleType = ScaleType.FIT_CENTER
// 缩略图,范围(0,1)
thumbSizeMultiplier?: number;
// 缩略图展示时间
thumbSizeDelay?:number;
// 缩略图展示类型
thumbSizeMultiplierScaleType?: ScaleType = ScaleType.FIT_CENTER
// 进度条
displayProgress?: boolean;
// 进度条回调
displayProgressListener?;
// 重试图层
retryLoad?: boolean;
// 动画时长
animateDuration?: number = 500;
// 重试图层 可点击
canRetryClick?: boolean;
// 仅使用缓存加载数据
onlyRetrieveFromCache?: boolean = false;
// 是否开启一级内存缓存
// 是否开启一级内存缓存
isCacheable?: boolean = true;
// 变换相关
transform?:{
transformType:number,
blur?:number,
roundedCorners?:{ top_left: number, top_right: number, bottom_left: number, bottom_right: number }
cropCircleWithBorder?:{border:number, obj:{ r_color: number, g_color: number, b_color: number }}
crop?:{width: number, height: number, cropType: CropType}
brightnessFilter?:number,
contrastFilter?:number,
pixelationFilter?:number,
swirlFilter?:number,
mask?:Resource,
rotateImage?:number
// 用户自定义实现 绘制方案
drawLifeCycle?: IDrawLifeCycle;
gif?: {
loopFinish?: (loopTime?) => void
speedFactory?: number
seekTo?: number
}
transformation?:BaseTransform<PixelMap>;
transformations?:Array<BaseTransform<PixelMap>>;
// 变换相关
transform?: {
transformType: number,
blur?: number,
roundedCorners?: {
top_left: number,
top_right: number,
bottom_left: number,
bottom_right: number
}
cropCircleWithBorder?: {
border: number,
obj: {
r_color: number,
g_color: number,
b_color: number
}
}
crop?: {
width: number,
height: number,
cropType: CropType
}
brightnessFilter?: number,
contrastFilter?: number,
pixelationFilter?: number,
swirlFilter?: number,
mask?: Resource,
rotateImage?: number
}
transformation?: BaseTransform<PixelMap>;
transformations?: Array<BaseTransform<PixelMap>>;
// 输出缓存相关内容和信息
allCacheInfoCallback?: IAllCacheInfoCallback;
size: {
width: number,
height: number
};
imageFit?: ImageFit;
backgroundColor?: Color | number | string | Resource;
margin?: {
@ -95,7 +128,7 @@ export class ImageKnifeOption {
left?: number | string | Resource
} | number | string | Resource
sizeAnimate?: AnimateParam
constructor() {

View File

@ -55,19 +55,19 @@ export class RequestOption {
errorholderData: ImageKnifeData;
thumbSizeMultiplier: number;
// 如果存在缩略图,则主图延时3000ms加载
thumbDelayTime: number = 3000
// 如果存在缩略图,则主图延时1s加载
thumbDelayTime: number = 1000
thumbHolderFunc: AsyncSuccess<ImageKnifeData>;
requestListeners: Array<AsyncCallback<ImageKnifeData>>;
// 进度条
progressFunc: AsyncSuccess<string>;
progressFunc: AsyncSuccess<number>;
// 重试图层
retryFunc: AsyncSuccess<ImageKnifeData>
retryholderSrc: PixelMap | Resource;
retryholderFunc: AsyncSuccess<ImageKnifeData>
retryholderData: ImageKnifeData
// 图层切换时长
animateDuration: number = 500;
size: {
width: number,
height: number
@ -98,12 +98,13 @@ export class RequestOption {
loadMainReady = false;
// 失败占位图展示状态 当true 表示主图加载失败需要展示失败占位图
loadErrorReady = false
loadErrorReady = false;
// 重试占位图展示状态 当true 表示主图加载失败需要展示失败占位图
loadRetryReady = false;
// 缩略图展示
loadThumbnailReady = false;
_svgAndGifFolder: string = "svgAndGifFolder"; // svg和gif的文件路径地址
_svgAndGifCommitFile: string = "svgAndGifCommitFile"; // svg和gif提交记录
constructor() {
// 初始化全局监听
@ -166,21 +167,27 @@ export class RequestOption {
return this;
}
thumbnail(sizeMultiplier: number, func?: AsyncSuccess<ImageKnifeData>) {
this.thumbSizeMultiplier = sizeMultiplier;
this.thumbHolderFunc = func;
retryholder(src: PixelMap | Resource, func?: AsyncSuccess<ImageKnifeData>) {
this.retryholderSrc = src;
this.retryholderFunc = func;
return this;
}
addProgressListener(func?: AsyncSuccess<string>) {
thumbnail(sizeMultiplier: number, func?: AsyncSuccess<ImageKnifeData>,displayTime?:number) {
this.thumbSizeMultiplier = sizeMultiplier;
this.thumbHolderFunc = func;
if(displayTime){
this.thumbDelayTime = displayTime;
}
return this;
}
addProgressListener(func?: AsyncSuccess<number>) {
this.progressFunc = func;
return this;
}
addRetryListener(func?: AsyncSuccess<any>) {
this.retryFunc = func;
return this;
}
addListener(func: AsyncCallback<ImageKnifeData>) {
this.requestListeners.push(func);
@ -322,7 +329,7 @@ export class RequestOption {
placeholderOnComplete(imageKnifeData: ImageKnifeData) {
console.log("placeholderOnComplete has called!");
console.log("Main Image is Ready:" + this.loadMainReady);
if (!this.loadMainReady && !this.loadErrorReady && !this.loadThumbnailReady) {
if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) {
// 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图
this.placeholderFunc(imageKnifeData)
}
@ -333,9 +340,12 @@ export class RequestOption {
console.log("占位图解析失败 error =" + error)
}
// 缩略图解析成功
thumbholderOnComplete(imageKnifeData: ImageKnifeData) {
if (!this.loadMainReady && !this.loadErrorReady) {
if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady)) {
//主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图
this.thumbHolderFunc(imageKnifeData)
}
@ -360,6 +370,17 @@ export class RequestOption {
console.log("失败占位图解析失败 error =" + error)
}
retryholderOnComplete(imageKnifeData: ImageKnifeData){
this.retryholderData = imageKnifeData;
if(this.loadRetryReady){
this.retryholderFunc(imageKnifeData)
}
}
retryholderOnError(error){
console.log("重试占位图解析失败 error ="+ error)
}
loadComplete(imageKnifeData: ImageKnifeData) {
this.loadMainReady = true;
// 三级缓存数据加载成功
@ -375,12 +396,17 @@ export class RequestOption {
loadError(err) {
console.log("loadError:"+err);
console.log("loadError stack=:"+JSON.stringify(err.stack));
//失败占位图展示规则
this.loadErrorReady = true;
if (this.retryFunc) {
if (this.retryholderFunc) {
// 重试图层优先于加载失败展示
this.retryFunc(err)
this.loadRetryReady = true;
if(this.retryholderData != null){
this.retryholderFunc(this.retryholderData)
}
} else {
// 失败图层标记,如果已经有数据直接展示失败图层
this.loadErrorReady = true;
if (this.errorholderData != null) {
this.errorholderFunc(this.errorholderData)
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2022 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 {ImageKnifeData} from '../ImageKnifeData'
export interface IDrawExtension{
<T>(context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:T, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 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 {ImageKnifeOption} from '../ImageKnifeOption'
import {ImageKnifeData} from '../ImageKnifeData'
export interface IDrawLifeCycle{
// 展示占位图
displayPlaceholder?: (context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
// 展示加载进度
displayProgress?: (context: CanvasRenderingContext2D,progress: number, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
// 展示缩略图
displayThumbSizeMultiplier?: (context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
// 展示主图
displayMainSource?: (context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
// 展示重试图层
displayRetryholder?: (context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
// 展示失败占位图
displayErrorholder?: (context: CanvasRenderingContext2D,data: ImageKnifeData, imageKnifeOption:ImageKnifeOption, compWidth:number,compHeight:number, setGifTimeId?:(timeId:number)=>void)=>boolean
}

View File

@ -46,7 +46,7 @@ export class DownloadClient implements IDataFetch {
loadTask = downloadTask;
loadTask.on('progress', (receivedSize, totalSize) => {
let percent = Math.round(((receivedSize * 1.0) / (totalSize * 1.0)) * 100) + "%"
let percent = Math.round(((receivedSize * 1.0) / (totalSize * 1.0)) * 100)
if (request.progressFunc) {
request.progressFunc(percent);
}

View File

@ -23,11 +23,14 @@ import{DiskCacheProxy} from "../requestmanage/DiskCacheProxy"
import{FileTypeUtil} from "../utils/FileTypeUtil"
import{IDataFetch} from "../../imageknife/networkmanage/IDataFetch"
import{IResourceFetch} from "../../imageknife/resourcemanage/IResourceFetch"
import{ImageKnifeData} from "../ImageKnifeData"
import{ImageKnifeData,ImageKnifeType} from "../ImageKnifeData"
import {AllCacheInfo, IAllCacheInfoCallback} from "../../imageknife/interface/IAllCacheInfoCallback"
import{ParseImageUtil} from '../utils/ParseImageUtil'
import{IParseImage} from '../interface/IParseImage'
import image from "@ohos.multimedia.image"
import { SVGParseImpl } from '../utils/svg/SVGParseImpl'
import { GIFParseImpl } from '../utils/gif/GIFParseImpl'
import { GIFFrame } from '../utils/gif/GIFFrame'
export interface AsyncString {
(data: string): void;
@ -198,39 +201,24 @@ export class RequestManager {
let fileTypeUtil = new FileTypeUtil();
let typeValue = fileTypeUtil.getFileType(arrayBuffer)
console.log("RequstManager - 文件类型为= " + typeValue)
if ((ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag) || ImageKnifeData.SVG == typeValue) {
// 将图片资源 转换为文件地址
let folderPath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder;
let filePath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder + "/"
+ Md5.hashStr(this.options.generateDataKey) + "." + typeValue;
let filename = Md5.hashStr(this.options.generateDataKey) + "." + typeValue;
new Promise((resolve, reject) => {
// 存文件至svg目录并且记录文件名
resolve("svg gif 文件开始本地保存!");
// gif处理
if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){
// 处理gif
this.gifProcess(onComplete,onError, arrayBuffer,typeValue,(imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData)
})
.then((res) => {
// 创建文件
FileUtils.getInstance()
.createFileProcess(folderPath, filePath, arrayBuffer);
// 写入记录
FileUtils.getInstance()
.writeData(this.options.getFilesPath() +
"/" + this.options._svgAndGifFolder + "/" + this.options._svgAndGifCommitFile, filename + "\n");
console.log("svg gif 本地保存成功 输出!");
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.STRING, filePath, typeValue);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}else if(ImageKnifeData.SVG == typeValue){
// 处理svg
this.svgProcess(onComplete,onError,arrayBuffer,typeValue,(imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey,imageKnifeData)
})
.catch((err) => {
onError(err)
})
}
else {
} else {
if (request.transformations[0]) {
request.transformations[0].transform(arrayBuffer, request, (error, pixelMap: PixelMap) => {
// 输出给Image
if (pixelMap) {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, pixelMap, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap);
this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
} else {
@ -240,7 +228,7 @@ export class RequestManager {
}
else {
let success = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}
@ -310,29 +298,16 @@ export class RequestManager {
// 步骤一文件转为pixelMap 然后变换 给Image组件
let fileTypeUtil = new FileTypeUtil();
let typeValue = fileTypeUtil.getFileType(source);
if (((ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag) || ImageKnifeData.SVG == typeValue)) {
// 将图片资源 转换为文件地址
let folderPath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder;
let filePath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder + "/"
+ Md5.hashStr(this.options.generateDataKey) + "." + typeValue;
let filename = Md5.hashStr(this.options.generateDataKey) + "." + typeValue;
new Promise((resolve, reject) => {
// 存文件至svg目录并且记录文件名
resolve("svg gif 文件开始本地保存!");
}).then((res) => {
FileUtils.getInstance()
.createFileProcess(folderPath, filePath, source);
FileUtils.getInstance()
.writeData(this.options.getFilesPath() +
"/" + this.options._svgAndGifFolder + "/" + this.options._svgAndGifCommitFile, filename + "\n");
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.STRING, filePath, typeValue)
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
// 解析磁盘文件 gif 和 svg
if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){
// 处理gif
this.gifProcess(onComplete,onError,source,typeValue, (imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData)
})
.catch((err) => {
onError(err)
}else if(ImageKnifeData.SVG == typeValue){
this.svgProcess(onComplete,onError, source, typeValue, (imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData)
})
} else {
if (this.options.transformations[0]) {
if (this.options.thumbSizeMultiplier) {
@ -345,7 +320,7 @@ export class RequestManager {
let thumbError = this.options.thumbholderOnError.bind(this.options);
this.options.transformations[0].transform(source, thumbOption, (error, pixelMap: PixelMap) => {
if (pixelMap) {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, pixelMap, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap);
thumbCallback(imageKnifeData);
} else {
thumbError(error);
@ -355,7 +330,7 @@ export class RequestManager {
this.options.transformations[0].transform(source, request, (error, pixelMap: PixelMap) => {
if (pixelMap) {
// 保存一份变换后的图片PixelMap到MemoryCache
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, pixelMap, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
} else {
@ -368,7 +343,7 @@ export class RequestManager {
this.options.transformations[0].transform(source, request, (error, pixelMap: PixelMap) => {
if (pixelMap) {
// 保存一份变换后的图片PixelMap到MemoryCache
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, pixelMap, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
} else {
@ -382,13 +357,13 @@ export class RequestManager {
let thumbCallback = this.options.thumbholderOnComplete.bind(this.options);
let thumbError = this.options.thumbholderOnError.bind(this.options);
let thumbSuccess = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
thumbCallback(imageKnifeData);
}
this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError);
setTimeout(()=>{
let success = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}
@ -397,7 +372,7 @@ export class RequestManager {
}
else {
let success = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}
@ -416,13 +391,13 @@ export class RequestManager {
let thumbCallback = this.options.thumbholderOnComplete.bind(this.options);
let thumbError = this.options.thumbholderOnError.bind(this.options);
let thumbSuccess = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
thumbCallback(imageKnifeData);
}
this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError);
setTimeout(()=>{
let success = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}
@ -430,7 +405,7 @@ export class RequestManager {
},this.options.thumbDelayTime)
}else{
let success = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, typeValue)
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value)
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
}
@ -456,40 +431,35 @@ export class RequestManager {
onError("暂不支持 下载文件类型!类型=" + filetype);
return;
}
if ((ImageKnifeData.GIF == filetype && !this.options.dontAnimateFlag) || ImageKnifeData.SVG == filetype) {
// 将图片资源 转换为文件地址
let folderPath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder;
let filePath = this.options.getFilesPath() + "/" + this.options._svgAndGifFolder + "/"
+ Md5.hashStr(this.options.generateDataKey) + "." + filetype;
let filename = Md5.hashStr(this.options.generateDataKey) + "." + filetype;
// 1.文件保存 2.内存缓存 3.输出结果
new Promise((resolve, reject) => {
// 存文件至svg目录并且记录文件名
resolve("svg gif 文件开始本地保存!");
// 解析磁盘文件 gif 和 svg
if(ImageKnifeData.GIF == filetype && !this.options.dontAnimateFlag){
// 处理gif
this.gifProcess(onComplete,onError,source,filetype, (imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData)
})
.then((res) => {
FileUtils.getInstance()
.createFileProcess(folderPath, filePath, source);
FileUtils.getInstance()
.writeData(this.options.getFilesPath() +
"/" + this.options._svgAndGifFolder + "/" + this.options._svgAndGifCommitFile, filename + "\n");
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.STRING, filePath, filetype);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
onComplete(imageKnifeData);
})
.catch((err) => {
onError(err)
})
// 保存二级磁盘缓存
new Promise((resolve, reject) => {
resolve(source)
})
.then(async (arraybuffer: ArrayBuffer) => {
Promise.resolve(source)
.then(async (arraybuffer: ArrayBuffer)=>{
await this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer)
})
.catch((err) => {
console.log("save diskLruCache error=" + err);
.catch(err=>{
console.log('download file is ='+ImageKnifeData.GIF+'and save diskLruCache error ='+ err)
})
}else if(ImageKnifeData.SVG == filetype){
// 处理svg
this.svgProcess(onComplete,onError, source, filetype, (imageKnifeData)=>{
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData)
})
// 保存二级磁盘缓存
Promise.resolve(source)
.then(async (arraybuffer: ArrayBuffer)=>{
await this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer)
})
.catch(err=>{
console.log('download file is ='+ImageKnifeData.SVG+'and save diskLruCache error ='+ err)
})
} else {
// 进行变换
@ -512,7 +482,7 @@ export class RequestManager {
let thumbCallback = this.options.thumbholderOnComplete.bind(this.options);
let thumbError = this.options.thumbholderOnError.bind(this.options);
let thumbSuccess = (value: PixelMap) => {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, filetype);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
thumbCallback(imageKnifeData);
}
this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError);
@ -532,17 +502,18 @@ export class RequestManager {
}
}
createImageKnifeData(imageKnifeType:string, imageKnifeValue:PixelMap|string|Resource, imageKnifeSourceType:string):ImageKnifeData{
let imageKnifeData = new ImageKnifeData();
imageKnifeData.imageKnifeType = imageKnifeType;
imageKnifeData.imageKnifeValue = imageKnifeValue;
imageKnifeData.imageKnifeSourceType = imageKnifeSourceType;
return imageKnifeData;
createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData{
return ImageKnifeData.createImagePixelMap(imageKnifeType,imageKnifeValue);
}
createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData{
return ImageKnifeData.createImageGIFFrame(imageKnifeType,imageKnifeValue);
}
private saveCacheAndDisk(value: PixelMap, filetype:string, onComplete, source:ArrayBuffer) {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, value, filetype);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value);
this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData);
let save2DiskCache = async (arraybuffer) => {
await this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer)
@ -565,7 +536,7 @@ export class RequestManager {
let thumbError = this.options.thumbholderOnError.bind(this.options);
this.options.transformations[0].transform(source, thumbOption, (error, pixelMap: PixelMap) => {
if (pixelMap) {
let imageKnifeData = this.createImageKnifeData(ImageKnifeData.PIXELMAP, pixelMap, filetype);
let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap);
thumbCallback(imageKnifeData);
} else {
thumbError(error);
@ -581,6 +552,41 @@ export class RequestManager {
})
}, this.options.thumbDelayTime)
}
private svgProcess(onComplete, onError, arraybuffer, typeValue, cacheStrategy?: (cacheData: ImageKnifeData) => void) {
let svgParseImpl = new SVGParseImpl()
let size = { width: this.options.size.width, height: this.options.size.height }
svgParseImpl.parseSvg(arraybuffer, size).then((value: PixelMap) => {
let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value)
if(cacheStrategy){
cacheStrategy(imageKnifeData)
}
onComplete(imageKnifeData)
}).catch(err => {
onError(err)
})
}
private gifProcess(onComplete, onError, arraybuffer, typeValue, cacheStrategy?: (cacheData: ImageKnifeData) => void) {
let gifParseImpl = new GIFParseImpl()
gifParseImpl.parseGifs(arraybuffer, (data?,err?)=>{
if(err){
onError(err)
}
console.log("gifProcess data is null:"+(data == null));
if(!!data){
let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME,data)
console.log('gifProcess 生成gif 返回数据类型')
if(cacheStrategy){
console.log('gifProcess 生成gif并且存入了缓存策略')
cacheStrategy(imageKnifeData)
}
onComplete(imageKnifeData)
}else{
onError('Parse GIF callback data is null, you need check callback data!')
}
})
}
}