@Component
export struct CropImageView {
@Require
handler: CropperController;
@Require
imageUri: string;
borderWidthVp: number = 1;
focusWidthVp: number = 250;
focusHeightVp: number = 250;
focusRadiusVp: number = 250 / 2;
cropBorderColor: string | number = "#AA808080";
maskColor: string | number = "#AF000000";
@Link
isLoading: boolean;
private maxScale: number = MAX_SCALE;
private minScale: number = 1;
private currentScale: number = 1;
private preScale = this.currentScale;
private config: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.config);
private cropRectPx = new Rect();
private originImageRectVp = new Rect();
private realImageRectVp = new Rect();
private convertContainer: [number, number] = [0, 0];
// @State
// private imageOffsetInfo: OffsetModel = new OffsetModel(0, 0);
// @State
// private imageRotateInfo: RotateModel = new RotateModel();
// 图片默认大小,即,与屏幕大小最适配的显示大小
@State
private imageDefaultSize: image.Size = { width: 0, height: 0 };
// 表示当前图片是根据宽度适配还是高度适配
@State
private imageWH: number = ImageFitTypeDefault;
// 图片原始宽高比
private imageWHRatio: number = 0;
private eventOffsetX: number = 0;
private eventOffsetY: number = 0;
private offsetX = 0;
private offsetY = 0;
private startOffsetX = 0;
private startOffsetY = 0;
private pntX = 0;
private pntY = 0;
private pinchCenterX = 0;
private pinchCenterY = 0;
// 使用矩阵变换对图形进行平移、旋转和缩放等。
@State
private matrix: matrix4.Matrix4Transit = matrix4.identity()
.translate({ x: this.offsetX, y: this.offsetY })
.scale({ x: this.currentScale, y: this.currentScale });
private updateMatrix(): void {
this.matrix = matrix4.identity()
.translate({ x: this.getUIContext().vp2px(this.offsetX), y: this.getUIContext().vp2px(this.offsetY) })
.scale({ x: this.currentScale, y: this.currentScale });
}
aboutToAppear(): void {
this.handler.bindCropHandler(async (path) => {
const node = this.getUIContext().getFrameNodeById(VIEW_ID);
const ctx = this.getUIContext().getHostContext();
if (node == null || ctx == null || this.cropRectPx.width <= 0 || this.cropRectPx.height <= 0) {
return false;
}
let bitmap: PixelMap | null = null;
let tmpFile: fileIo.File | null = null;
let packer: image.ImagePacker | null = null;
try {
//获取目标图
bitmap = await this.getUIContext().getComponentSnapshot()
.getWithUniqueId(node.getUniqueId(), {
waitUntilRenderFinished: true,
region: {
left: this.cropRectPx.left,
top: this.cropRectPx.top,
right: this.cropRectPx.right,
bottom: this.cropRectPx.bottom
}
});
//输出为500*500尺寸图
await bitmap.scale(500 / this.cropRectPx.width, 500 / this.cropRectPx.height);
//保存图到临时文件中
packer = image.createImagePacker();
const avatarTmpDir = ctx.cacheDir + "/" + TMP_FOLDER_NAME;
if (!await fileIo.access(avatarTmpDir)) {
await fileIo.mkdir(avatarTmpDir);
}
const tmpPath = avatarTmpDir + "/tmp.jpg";
if (await fileIo.access(tmpPath)) {
await fileIo.unlink(tmpPath);
}
tmpFile = await fileIo.open(tmpPath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
await packer.packToFile(bitmap, tmpFile.fd, { format: "image/jpeg", quality: 100 })
} catch (e) {
Log.d(e,"CropHandler error")
return false;
} finally {
bitmap?.release();
await FileUtil.closeFile(tmpFile);
await packer?.release();
}
return true;
});
}
build() {
Stack() {
Stack() {
Image(this.imageUri)
.objectFit(ImageFit.Contain)
.width("100%")
.height("100%")
.draggable(false)
.autoResize(true)
.transform(this.matrix)
.onComplete((event) => {
if (event) {
this.initImage(event);
}
})
.onError((error) => {
this.handler.error?.(error.message);
this.isLoading = false;
})
}
.width("100%")
.height("100%")
.id(VIEW_ID)
Canvas(this.context)
.width("100%")
.height("100%")
.onReady(() => {
console.log("canvas:"+this.context.width+" "+this.context.height)
const centX = this.context.width / 2.0;
const centY = this.context.height / 2.0;
const left = this.getUIContext().vp2px(centX - this.focusRadiusVp);
const top = this.getUIContext().vp2px(centY - this.focusRadiusVp);
const right = this.getUIContext().vp2px(centX + this.focusRadiusVp);
const bottom = this.getUIContext().vp2px(centY + this.focusRadiusVp);
this.cropRectPx.set(left, top, right, bottom);
this.context.fillStyle = this.maskColor;
this.context.beginPath();
this.context.moveTo(0, 0);
this.context.lineTo(this.context.width, 0);
this.context.lineTo(this.context.width, this.context.height);
this.context.lineTo(0, this.context.height);
this.context.closePath();
this.context.arc(centX, centY, this.focusRadiusVp, 0, 2 * Math.PI, true);
this.context.fill();
this.context.strokeStyle = this.cropBorderColor;
this.context.lineWidth = this.borderWidthVp;
this.context.beginPath();
this.context.arc(centX, centY, this.focusRadiusVp, 0, 2 * Math.PI, true);
this.context.stroke();
//删除
this.context.lineWidth = 1;
this.context.strokeStyle = Color.Red;
this.context.strokeRect(this.realImageRectVp.left, this.realImageRectVp.top, this.realImageRectVp.width,
this.originImageRectVp.height)
this.context.beginPath();
const c1 = (this.realImageRectVp.left+this.realImageRectVp.right)/2;
const c2 = (this.realImageRectVp.top+this.realImageRectVp.bottom)/2
console.log("中心点:"+centX+" "+centY+" === c1:"+c1+" c2: "+c2)
this.context.arc(c1,c2, 2, 0, 2 * Math.PI, true);
this.context.fill()
})
if (this.isLoading) {
LoadingProgress()
.color($r("app.color.color_accent"))
.width(Constants.DEFAULT_WAIT_SIZE)
.height(Constants.DEFAULT_WAIT_SIZE)
}
}
.width("100%")
.height("100%")
.clip(true)
.gesture(
GestureGroup(
GestureMode.Parallel,
PanGesture()
.onActionStart(() => {
this.startOffsetX = this.offsetX;
this.startOffsetY = this.offsetY;
})
.onActionUpdate((event) => {
this.offsetX = this.startOffsetX + event.offsetX / this.currentScale;
this.offsetY = this.startOffsetY + event.offsetY / this.currentScale;
this.updateMatrix();
this.testDraw();
})
)
)
}
private testDraw() {
this.context.reset();
const centX = this.context.width / 2.0;
const centY = this.context.height / 2.0;
const left = this.getUIContext().vp2px(centX - this.focusRadiusVp);
const top = this.getUIContext().vp2px(centY - this.focusRadiusVp);
const right = this.getUIContext().vp2px(centX + this.focusRadiusVp);
const bottom = this.getUIContext().vp2px(centY + this.focusRadiusVp);
this.cropRectPx.set(left, top, right, bottom);
this.context.fillStyle = this.maskColor;
this.context.beginPath();
this.context.moveTo(0, 0);
this.context.lineTo(this.context.width, 0);
this.context.lineTo(this.context.width, this.context.height);
this.context.lineTo(0, this.context.height);
this.context.closePath();
this.context.arc(centX, centY, this.focusRadiusVp, 0, 2 * Math.PI, true);
this.context.fill();
this.context.strokeStyle = this.cropBorderColor;
this.context.lineWidth = this.borderWidthVp;
this.context.beginPath();
this.context.arc(centX, centY, this.focusRadiusVp, 0, 2 * Math.PI, true);
this.context.stroke();
this.context.lineWidth = 1;
this.context.strokeStyle = Color.Red;
this.convertContainer[0] = this.originImageRectVp.left;
this.convertContainer[1] = this.originImageRectVp.top;
const realPoint1 = this.matrix.transformPoint(this.convertContainer);
this.convertContainer[0] = this.originImageRectVp.right;
this.convertContainer[1] = this.originImageRectVp.bottom;
const realPoint2 = this.matrix.transformPoint(this.convertContainer);
this.realImageRectVp.set(realPoint1[0],realPoint1[1],realPoint2[0],realPoint2[1])
this.context.strokeRect(this.realImageRectVp.left, this.realImageRectVp.top, this.realImageRectVp.width,
this.realImageRectVp.height)
this.context.beginPath();
const c1 = (this.realImageRectVp.left+this.realImageRectVp.right)/2;
const c2 = (this.realImageRectVp.top+this.realImageRectVp.bottom)/2
this.context.arc(c1,c2, 20, 0, 2 * Math.PI, true);
this.context.fill()
}
private midPointX = 0;
private midPointY = 0;
private viewWidthVp = 0;
private viewHeightVp = 0;
private contentWidthVp = 0;
private contentHeightVp = 0;
private initImage(event: ImageLoadResult) {
if (event.loadingStatus != 1) {
return;
}
this.viewWidthVp = this.getUIContext().px2vp(event.componentWidth);
this.viewHeightVp = this.getUIContext().px2vp(event.componentHeight);
this.contentWidthVp = this.getUIContext().px2vp(event.contentWidth);
this.contentHeightVp = this.getUIContext().px2vp(event.contentHeight);
this.midPointX = event.componentWidth / 2;
this.midPointY = event.componentHeight / 2;
const fitFocusScale =
this.getScale(this.contentWidthVp, this.contentHeightVp, this.focusWidthVp, this.focusHeightVp, true);
this.maxScale = fitFocusScale * MAX_SCALE;
const fitViewScale =
this.getScale(this.contentWidthVp, this.contentHeightVp, this.viewWidthVp, this.viewHeightVp, false);
const scale = fitViewScale > fitFocusScale ? fitViewScale : fitFocusScale;
// this.currentScale = scale;
// this.matrix = matrix4.identity().scale({ x: scale, y: scale });
this.isLoading = false;
const midX = this.viewWidthVp / 2;
const midY = this.viewHeightVp / 2;
this.originImageRectVp.set(midX - this.contentWidthVp / 2, midY - this.contentHeightVp / 2, midX + this.contentWidthVp / 2,
midY + this.contentHeightVp / 2)
this.testDraw();
}
private getScale(bitmapWidth: number, bitmapHeight: number,
minWidth: number, minHeight: number, isMinScale: boolean) {
const scaleX = minWidth / bitmapWidth;
const scaleY = minHeight / bitmapHeight;
return isMinScale ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
}
}