往期知识点整理
场景描述
添加水印是一个比较常见的功能,下面列举一些遇到的业务需求:
-
场景一:在页面背景上添加全局水印
-
场景二:保存图片添加水印
-
场景三:拍照添加水印
-
场景四:window级别的全局水印
方案描述
场景一:
在页面背景上添加全局水印
效果图:
方案
页面全局水印的实现思路是使用透明背景的canvas画布绘制文字水印,在使用Stack组件叠加在page跟容器上
方式一:使用canvas进行绘制
Stack({ alignContent: Alignment.Center }) {
Column() {
Text("没有数据哦").fontColor("#495057")
Image($r("app.media.empty")).width(300)
}
Canvas(this.context)
.width("100%")
.height("100%")
.hitTestBehavior(HitTestMode.Transparent)
.onReady(() => {
this.context.fillStyle = '#10000000'
this.context.font = "16vp"
this.context.textAlign = "center"
this.context.textBaseline = "middle"
// 在这里绘制文字水印,也可以是图片水印
for (let i = 0; i < this.context.width / 120; i++) {
this.context.translate(120, 0)
let j = 0
for (; j < this.context.height / 120; j++) {
this.context.rotate(-Math.PI / 180 * 30)
// 此处水印数据是写死的,具体请替换为自己的水印
this.context.fillText("水印水印水印", -60, -60)
this.context.rotate(Math.PI / 180 * 30)
this.context.translate(0, 120)
}
this.context.translate(0, -120 * j)
}
})
}.layoutWeight(1)
方式二:使用overlay属性绘制
方式二思路与方式一实现思路一致,该方式可以讲水印组件单独拧出来,在其他使用时使用overlay属性来使用,实现效果与方式一一致
@Builder
Watermark() {
Canvas(this.context)
.width("100%")
.height("100%")
.hitTestBehavior(HitTestMode.Transparent)
.onReady(() => {
this.context.fillStyle = '#10000000'
this.context.font = "16vp"
this.context.textAlign = "center"
this.context.textBaseline = "middle"
// 在这里绘制文字水印,也可以是图片水印
for (let i = 0; i < this.context.width / 120; i++) {
this.context.translate(120, 0)
let j = 0
for (; j < this.context.height / 120; j++) {
this.context.rotate(-Math.PI / 180 * 30)
// 此处水印数据是写死的,具体请替换为自己的水印
this.context.fillText("水印水印水印", -60, -60)
this.context.rotate(Math.PI / 180 * 30)
this.context.translate(0, 120)
}
this.context.translate(0, -120 * j)
}
})
}
Column() {
Text("没有数据哦").fontColor("#495057")
Image($r("app.media.empty")).width(300)
}
.layoutWeight(1)
.overlay(this.Watermark())
.width("100%")
场景二:
保存图片添加水印
方案
使用OffScreenCanvas离屏画布拿到图片源数据,在离屏画布上使用OffscreenCanvasRenderingContext2D.drawImage方法讲图片数据进行绘制,使用OffscreenCanvasRenderingContext2D.fillText方法绘制文字水印,通过 OffscreenCanvasRenderingContext2D.getPixelMap方法获取添加水印后的图片pixelMap数据,再通过安全控件方式拿到图库的uri路径,在这个uri下通过image.createImagePacker方法创建图片文件,通过fs.write方法写入这个图库的文件中,具体实现代码如下所示
核心代码
第一步:使用resourceManager.getMediaContent方法获取当前的图片资源
getContext(this).resourceManager.getMediaContent($r(this.imagePath)).then((data) => {
// ...
}).catch((err: BusinessError) => {
console.log("getPixelMap error" + err.message)
})
第二步:使用image.createImageSource方法创建图片资源,在使用OffscreenCanvasRenderingContext2D.drawImage方法绘制图片在离屏画布上
let arrayBuffer = data.buffer.slice(data.byteOffset, data.byteLength + data.byteOffset)
let imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
imageSource.getImageInfo((err, value) => {
if (err) {
return;
}
let opts: image.DecodingOptions = {
editable: true,
desiredSize: {
height: value.size.height,
width: value.size.width
}
};
imageSource.createPixelMap(opts, async (err, pixelMap) => {
// 这个回调的pixelMap就是当前图片的pixelMap
let offScreenCanvas = new OffscreenCanvas(value.size.width, value.size.height)
let OffScreenContext = offScreenCanvas.getContext("2d")
this.imageScale = offScreenCanvas.width / display.getDefaultDisplaySync().width
OffScreenContext.drawImage(pixelMap, 0, 0, offScreenCanvas.width, offScreenCanvas.height)
// ...
})
})
第三步:使用OffscreenCanvasRenderingContext2D.fillText绘制水印
getContext(this).resourceManager.getMediaContent($r(this.imagePath)).then((data)
let arrayBuffer = data.buffer.slice(data.byteOffset, data.byteLength + data.byteOffset)
let imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
imageSource.getImageInfo((err, value) => {
if (err) {
return;
}
let opts: image.DecodingOptions = {
editable: true,
desiredSize: {
height: value.size.height,
width: value.size.width
}
};
imageSource.createPixelMap(opts, async (err, pixelMap) => {
// ...
OffScreenContext.textAlign = "right"
OffScreenContext.textBaseline = "bottom"
OffScreenContext.fillStyle = '#30000000'
OffScreenContext.font = 32 * this.imageScale + "vp"
OffScreenContext.fillText("初九会画画", offScreenCanvas.width - 20 * this.imageScale, offScreenCanvas.height - 20 * this.imageScale)
// ...
})
})
}).catch((err: BusinessError) => {
console.log("getPixelMap error" + err.message)
})
第四步:使用OffscreenCanvasRenderingContext2D.getPixelMap取出绘制后的图片数据
// 获取新的PixelMap
this.pixelMap = OffScreenContext.getPixelMap(0, 0, offScreenCanvas.width, offScreenCanvas.height)
第五步:使用SaveButton保存到图库中
SaveButton({ icon: SaveIconStyle.FULL_FILLED }).onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
if (result == SaveButtonOnClickResult.SUCCESS) {
try {
let context = getContext(this);
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 创建媒体文件
this.imagePath = "app.media.img1"
this.addWatermark()
if (this.pixelMap) {
saveToFile(this.pixelMap, uri)
}
} catch (err) {
console.error('createAsset failed, message = ', err);
}
} else {
console.error('SaveButtonOnClickResult createAsset failed');
}
})
场景三:
拍照添加水印
方案
使用cameraPicker拉起系统相机拍照,拿到返回的picker.PickerResult.resultUri后使用fs.openSync读出图片文件转化为pixelMap,使用image组件渲染在页面上,在保存图片的时候通过drawing.TextBlob.makeFromString方法绘制水印后保存
核心代码
第一步:使用cameraPicker拉起系统相机
import picker from '@ohos.multimedia.cameraPicker';
import camera from '@ohos.multimedia.camera';
async openCamera() {
try {
let pickerProfile: picker.PickerProfile = {
cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
};
let pickerResult: picker.PickerResult = await picker.pick(getContext(this),
[picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO], pickerProfile);
console.log("the pick pickerResult is:" + JSON.stringify(pickerResult));
// ...
} catch (error) {
let err = error as BusinessError;
console.error(`the pick call failed. error code: ${err.message}`);
}
}
第二步:将相机拍摄的照片渲染在页面中,使用fs.readSync读出文件字节流,再使用image.createImageSource创建图片资源管理器获取到图片的pixelMap
@State pixelMap: image.PixelMap | null = null
async openCamera() {
try {
// ...
if (pickerResult.resultUri) {
this.showImage = true
}
let file = fs.openSync(pickerResult.resultUri)
let buffer = new ArrayBuffer(fs.statSync(file.fd).size)
fs.readSync(file.fd, buffer)
let imageSource: image.ImageSource = image.createImageSource(buffer)
imageSource.getImageInfo((err, value) => {
if (err) {
return;
}
let opts: image.DecodingOptions = {
editable: true,
desiredSize: {
height: value.size.height,
width: value.size.width
}
};
imageSource.createPixelMap(opts, async (err, pixelMap) => {
this.pixelMap = pixelMap
})
})
} catch (error) {
let err = error as BusinessError;
console.error(`the pick call failed. error code: ${err.message}`);
}
}
build() {
// ...
if(this.showImage) {
Image(this.pixelMap).width("100%")
// ...
}
}
第三步:使用drawing.TextBlob.makeFromString绘制水印后 使用SaveButton保存在系统相册中
addWaterMask(pixelMap: image.PixelMap) {
this.imageScale = this.imageWidth / display.getDefaultDisplaySync().width
const canvas = new drawing.Canvas(pixelMap)
const pen = new drawing.Pen()
pen.setColor({ alpha: 255, red: 0, green: 10, blue: 10 })
const font = new drawing.Font()
font.setSize(32 * this.imageScale)
let textWidth = font.measureText("初九会画画", drawing.TextEncoding.TEXT_ENCODING_UTF8)
const brush = new drawing.Brush()
brush.setColor({ alpha: 255, red: 255, green: 0, blue: 0 })
const textBlob = drawing.TextBlob.makeFromString("初九会画画", font, drawing.TextEncoding.TEXT_ENCODING_UTF8)
canvas.attachBrush(brush)
canvas.attachPen(pen)
canvas.drawTextBlob(textBlob, this.imageWidth - 20 * this.imageScale - textWidth, this.imageHeight - 20 * this.imageScale)
canvas.detachBrush();
canvas.detachPen()
return pixelMap
}
SaveButton()
.onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
if (result == SaveButtonOnClickResult.SUCCESS) {
try {
let context = getContext(this);
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png'); // 创建媒体文件
saveToFile(this.addWaterMask(this.pixelMap!), uri);
} catch (err) {
console.error('createAsset failed, message = ', err);
}
} else {
console.error('SaveButtonOnClickResult createAsset failed');
}
})
推荐
经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?
为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
鸿蒙开发面试真题(含参考答案):
《OpenHarmony源码解析》:
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……