3 回复
可以试下这个demo:
import { image } from '[@kit](/user/kit).ImageKit';
import { ArrayList, util } from '[@kit](/user/kit).ArkTS';
[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
[@State](/user/State) message: string = 'Hello World';
private signSettings: RenderingContextSettings = new RenderingContextSettings(true)
private signContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.signSettings)
[@State](/user/State) transcriptionMark: number = 0
private copySettings: RenderingContextSettings = new RenderingContextSettings(true)
private copyContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.copySettings)
[@State](/user/State) copyImage: string[] = []
private timerId: number = -1
private lastX: number = 0;
private lastY: number = 0;
private isDown: Boolean = false;
[@State](/user/State) copyContent: string = '本人已阅读保险条款'
[@State](/user/State) imagePixelMap: image.PixelMap | undefined = undefined
[@State](/user/State) orgImage: image.PixelMap | undefined = undefined
[@State](/user/State) list: ArrayList<image.ImageSource> = new ArrayList<image.ImageSource>()
aboutToAppear(): void {
this.list.clear()
}
draw(context: CanvasRenderingContext2D, startX: number, startY: number, endX: number, endY: number) {
// 起点
context.moveTo(startX, startY);
// 终点
context.lineTo(endX, endY);
// 调用 stroke,即可看到绘制的线条
context.stroke();
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Column({ space: '10vp' }) {
Row({ space: '9vp' }) {
Column() {
Grid() {
ForEach(this.copyImage, (image: string) => {
GridItem() {
Image(image)
.width('100%')
.height('100%')
}
}, (day: string) => day)
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.width('98%')
.height('98%')
}
.layoutWeight(3)
.backgroundColor('#EEEEEE')
.borderRadius('10vp')
.height('100%')
Stack() {
Canvas(this.signContext)
.width('100%')
.height('100%')
.onReady(() => {
this.signContext.beginPath()
this.signContext.moveTo(0, 0)
this.signContext.lineTo(this.signContext.width, this.signContext.height)
this.signContext.moveTo(this.signContext.width, 0)
this.signContext.lineTo(0, this.signContext.height)
this.signContext.moveTo(this.signContext.width / 2, 0)
this.signContext.lineTo(this.signContext.width / 2, this.signContext.height)
this.signContext.moveTo(0, this.signContext.height / 2)
this.signContext.lineTo(this.signContext.width, this.signContext.height / 2)
this.signContext.setLineDash([10, 10])
this.signContext.stroke()
})
Text(this.copyContent.substring(this.transcriptionMark, this.transcriptionMark + 1))
.width('100%')
.height('100%')
.fontSize('130fp')
.fontColor('#F0F0F0')
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
Canvas(this.copyContext)
.width('100%')
.height('100%')
.onReady(() => {
this.copyContext.strokeStyle = "#ff000000"
this.copyContext.lineWidth = 5
})
.gesture(PanGesture().onActionStart(event => {
clearTimeout(this.timerId);
this.isDown = true;
// 按下时的点作为起点
this.lastX = event.fingerList[0].localX;
this.lastY = event.fingerList[0].localY
// 创建一个新的路径
this.copyContext.beginPath();
// console.log("onActionStart() ")
})
.onActionUpdate(event => {
// console.log("onActionUpdate() ")
clearTimeout(this.timerId);
// 没有按下就不管
if (!this.isDown) {
return;
}
const offsetX = event.fingerList[0].localX
const offsetY = event.fingerList[0].localY
// 调用绘制方法
this.draw(this.copyContext, this.lastX, this.lastY, offsetX, offsetY);
// 把当前移动时的坐标作为下一次的绘制路径的起点
this.lastX = offsetX;
this.lastY = offsetY;
})
.onActionEnd(event => {
// console.log("onActionEnd() ")
this.isDown = false;
// 关闭路径
this.copyContext.closePath();
this.timerId = setInterval(() => {
clearTimeout(this.timerId);
let image = this.signContext.getImageData(0, 0, this.copyContext.width, this.copyContext.height)
let url = this.copyContext.toDataURL("image/png", 0.5)
this.copyImage[this.transcriptionMark] = url
this.copyContext.clearRect(0, 0, this.copyContext.width, this.copyContext.height)
this.transcriptionMark++
}, 2000)
}), GestureMask.Normal)
}
.layoutWeight(2)
.borderRadius('10vp')
.borderColor('#CECECE')
.height('100%')
.borderWidth('3vp')
.id('ban')
}
.height('58%')
Row() {
Row({ space: '10vp' }) {
Button('完成')
.width('80vp')
.height('35vp')
.fontSize('14fp')
.backgroundColor('#fffe0403')
.onClick(async () => {
this.imagePixelMap = await this.combinePic(this.copyImage)
})
}
.alignSelf(ItemAlign.End)
.margin({ right: '20vp', bottom: '20vp' })
}
.width('100%')
.justifyContent(FlexAlign.End)
}
.height('50%')
Image(this.imagePixelMap)
.width(200)
.height(200)
.backgroundColor(Color.Yellow)
.objectFit(ImageFit.Contain)
Image(this.orgImage)
.enableAnalyzer(true)
.width(200)
.height(200)
.border({ width: 1 })
}
.height('100%')
.width('100%')
}
async combinePic(pics: string[]) {
if (pics.length < 2) {
let helper = new util.Base64Helper();
let buffer: ArrayBuffer = helper.decodeSync(pics[0].substring(22), util.Type.MIME).buffer as ArrayBuffer;
let imageSource = image.createImageSource(buffer);
const imageInfo = await imageSource.getImageInfo();
let opts: image.DecodingOptions = {
editable: true,
desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
desiredSize: { width: imageInfo.size.width, height: imageInfo.size.height }
};
let pixelMap = await imageSource.createPixelMap(opts);
return pixelMap;
}
const tempPath = pics[0];
const imageSource = image.createImageSource(tempPath);
const imageInfo = await imageSource.getImageInfo();
const singleWidth = imageInfo.size.width;
const singleHeight = imageInfo.size.height;
const combineOpts: image.InitializationOptions = {
alphaType: 0,
editable: true,
pixelFormat: image.PixelMapFormat.BGRA_8888,
size: { width: singleWidth * pics.length, height: singleHeight }
}
const singleOpts: image.DecodingOptions = {
editable: true,
desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
desiredSize: { width: singleWidth, height: singleHeight }
};
const combineColor = new ArrayBuffer(combineOpts.size.width * combineOpts.size.height * 4);
let singleColor = new ArrayBuffer(singleOpts.desiredSize!.width * singleOpts.desiredSize!.height * 4);
const newPixelMap = await image.createPixelMap(combineColor, combineOpts);
for (let i = 0; i < pics.length; i++) {
let singlePath = pics[i];
let imageSource = image.createImageSource(singlePath);
const singlePixelMap = await imageSource.createPixelMap(singleOpts);
await singlePixelMap.readPixelsToBuffer(singleColor);
let area: image.PositionArea = {
pixels: singleColor,
offset: 0,
stride: singleWidth * 4,
region: {
size: { height: singleHeight, width: singleWidth },
x: singleWidth * i,
y: 0
}
}
await newPixelMap.writePixels(area);
}
let combinePixelMap = newPixelMap;
return combinePixelMap
}
}
更多关于HarmonyOS 鸿蒙Next 多张string图片合并成一张的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next系统中,将多张string图片(假设这里指的是包含文本信息的图片,即图片格式的字符串内容,而非纯文本字符串)合并成一张图片,通常涉及图像处理技术。以下是一个简要的技术实现思路:
-
加载图片:首先,使用系统提供的图像加载库(如BitmapFactory)加载所有待合并的string图片。
-
创建画布:根据合并后图片的预期尺寸,创建一个Canvas对象。此画布将作为合并后图片的容器。
-
绘制图片:利用Canvas的drawBitmap方法,将每张图片按指定位置和大小绘制到画布上。注意调整图片的排列方式(如水平或垂直排列)。
-
保存结果:使用Bitmap类的compress方法,将合并后的画布内容保存为图片文件。
-
优化与调整:根据需要,对图片进行裁剪、缩放或添加边框等优化处理。
请注意,上述过程需确保所有图片已正确加载,且Canvas的尺寸足够容纳所有图片。此外,还需处理可能出现的内存溢出问题,特别是在处理大图片时。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html