HarmonyOS鸿蒙开辟:基于原生的全局水印添加场景

打印 上一主题 下一主题

主题 702|帖子 702|积分 2106

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
场景描述
添加水印是一个比较常见的功能,下面枚举一些遇到的业务需求:


  • 场景一:在页面配景上添加全局水印
  • 场景二:生存图片添加水印
  • 场景三:照相添加水印
  • 场景四:window级别的全局水印
方案描述
场景一:
在页面配景上添加全局水印
效果图:


方案
页面全局水印的实现思路是利用透明配景的canvas画布绘制文字水印,在利用Stack组件叠加在page跟容器上
方式一:利用canvas进行绘制
  1. Stack({ alignContent: Alignment.Center }) {
  2. Column() {
  3. Text("没有数据哦").fontColor("#495057")
  4. Image($r("app.media.empty")).width(300)
  5. }
  6. Canvas(this.context)
  7. .width("100%")
  8. .height("100%")
  9. .hitTestBehavior(HitTestMode.Transparent)
  10. .onReady(() => {
  11. this.context.fillStyle = '#10000000'
  12. this.context.font = "16vp"
  13. this.context.textAlign = "center"
  14. this.context.textBaseline = "middle"
  15. // 在这里绘制文字水印,也可以是图片水印
  16. for (let i = 0; i < this.context.width / 120; i++) {
  17. this.context.translate(120, 0)
  18. let j = 0
  19. for (; j < this.context.height / 120; j++) {
  20. this.context.rotate(-Math.PI / 180 * 30)
  21. // 此处水印数据是写死的,具体请替换为自己的水印
  22. this.context.fillText("水印水印水印", -60, -60)
  23. this.context.rotate(Math.PI / 180 * 30)
  24. this.context.translate(0, 120)
  25. }
  26. this.context.translate(0, -120 * j)
  27. }
  28. })
  29. }.layoutWeight(1)
复制代码
方式二:利用overlay属性绘制
方式二思路与方式一实现思路一致,该方式可以讲水印组件单独拧出来,在其他利用时利用overlay属性来利用,实现效果与方式一一致
  1. @Builder
  2. Watermark() {
  3. Canvas(this.context)
  4. .width("100%")
  5. .height("100%")
  6. .hitTestBehavior(HitTestMode.Transparent)
  7. .onReady(() => {
  8. this.context.fillStyle = '#10000000'
  9. this.context.font = "16vp"
  10. this.context.textAlign = "center"
  11. this.context.textBaseline = "middle"
  12. // 在这里绘制文字水印,也可以是图片水印
  13. for (let i = 0; i < this.context.width / 120; i++) {
  14. this.context.translate(120, 0)
  15. let j = 0
  16. for (; j < this.context.height / 120; j++) {
  17. this.context.rotate(-Math.PI / 180 * 30)
  18. // 此处水印数据是写死的,具体请替换为自己的水印
  19. this.context.fillText("水印水印水印", -60, -60)
  20. this.context.rotate(Math.PI / 180 * 30)
  21. this.context.translate(0, 120)
  22. }
  23. this.context.translate(0, -120 * j)
  24. }
  25. })
  26. }
  27. Column() {
  28. Text("没有数据哦").fontColor("#495057")
  29. Image($r("app.media.empty")).width(300)
  30. }
  31. .layoutWeight(1)
  32. .overlay(this.Watermark())
  33. .width("100%")
复制代码
场景二:
生存图片添加水印
方案
利用OffScreenCanvas离屏画布拿到图片源数据,在离屏画布上利用OffscreenCanvasRenderingContext2D.drawImage方法讲图片数据进行绘制,利用OffscreenCanvasRenderingContext2D.fillText方法绘制文字水印,通过 OffscreenCanvasRenderingContext2D.getPixelMap方法获取添加水印后的图片pixelMap数据,再通过安全控件方式拿到图库的uri路径,在这个uri下通过image.createImagePacker方法创建图片文件,通过fs.write方法写入这个图库的文件中,具体实当代码如下所示
核心代码
第一步:利用resourceManager.getMediaContent方法获取当前的图片资源
  1. getContext(this).resourceManager.getMediaContent($r(this.imagePath)).then((data) => {
  2. // ...
  3. }).catch((err: BusinessError) => {
  4. console.log("getPixelMap error" + err.message)
  5. })
复制代码
第二步:利用image.createImageSource方法创建图片资源,在利用OffscreenCanvasRenderingContext2D.drawImage方法绘制图片在离屏画布上
  1. let arrayBuffer = data.buffer.slice(data.byteOffset, data.byteLength + data.byteOffset)
  2. let imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
  3. imageSource.getImageInfo((err, value) => {
  4. if (err) {
  5. return;
  6. }
  7. let opts: image.DecodingOptions = {
  8. editable: true,
  9. desiredSize: {
  10. height: value.size.height,
  11. width: value.size.width
  12. }
  13. };
  14. imageSource.createPixelMap(opts, async (err, pixelMap) => {
  15. // 这个回调的pixelMap就是当前图片的pixelMap
  16. let offScreenCanvas = new OffscreenCanvas(value.size.width, value.size.height)
  17. let OffScreenContext = offScreenCanvas.getContext("2d")
  18. this.imageScale = offScreenCanvas.width / display.getDefaultDisplaySync().width
  19. OffScreenContext.drawImage(pixelMap, 0, 0, offScreenCanvas.width, offScreenCanvas.height)
  20. // ...
  21. })
  22. })
复制代码
第三步:利用OffscreenCanvasRenderingContext2D.fillText绘制水印
  1. getContext(this).resourceManager.getMediaContent($r(this.imagePath)).then((data)
  2. let arrayBuffer = data.buffer.slice(data.byteOffset, data.byteLength + data.byteOffset)
  3. let imageSource: image.ImageSource = image.createImageSource(arrayBuffer);
  4. imageSource.getImageInfo((err, value) => {
  5. if (err) {
  6. return;
  7. }
  8. let opts: image.DecodingOptions = {
  9. editable: true,
  10. desiredSize: {
  11. height: value.size.height,
  12. width: value.size.width
  13. }
  14. };
  15. imageSource.createPixelMap(opts, async (err, pixelMap) => {
  16. // ...
  17. OffScreenContext.textAlign = "right"
  18. OffScreenContext.textBaseline = "bottom"
  19. OffScreenContext.fillStyle = '#30000000'
  20. OffScreenContext.font = 32 * this.imageScale + "vp"
  21. OffScreenContext.fillText("初九会画画", offScreenCanvas.width - 20 * this.imageScale, offScreenCanvas.height - 20 * this.imageScale)
  22. // ...
  23. })
  24. })
  25. }).catch((err: BusinessError) => {
  26. console.log("getPixelMap error" + err.message)
  27. })
复制代码
第四步:利用OffscreenCanvasRenderingContext2D.getPixelMap取出绘制后的图片数据
  1. // 获取新的PixelMap
  2. this.pixelMap = OffScreenContext.getPixelMap(0, 0, offScreenCanvas.width, offScreenCanvas.height)
复制代码
第五步:利用SaveButton生存到图库中
  1. SaveButton({ icon: SaveIconStyle.FULL_FILLED }).onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
  2. if (result == SaveButtonOnClickResult.SUCCESS) {
  3. try {
  4. let context = getContext(this);
  5. let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
  6. let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 创建媒体文件
  7. this.imagePath = "app.media.img1"
  8. this.addWatermark()
  9. if (this.pixelMap) {
  10. saveToFile(this.pixelMap, uri)
  11. }
  12. } catch (err) {
  13. console.error('createAsset failed, message = ', err);
  14. }
  15. } else {
  16. console.error('SaveButtonOnClickResult createAsset failed');
  17. }
  18. })
复制代码
场景三:
照相添加水印
方案
利用cameraPicker拉起系统相机照相,拿到返回的picker.PickerResult.resultUri后利用fs.openSync读出图片文件转化为pixelMap,利用image组件渲染在页面上,在生存图片的时候通过drawing.TextBlob.makeFromString方法绘制水印后生存
核心代码
第一步:利用cameraPicker拉起系统相机
  1. import picker from '@ohos.multimedia.cameraPicker';
  2. import camera from '@ohos.multimedia.camera';
  3. async openCamera() {
  4. try {
  5. let pickerProfile: picker.PickerProfile = {
  6. cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
  7. };
  8. let pickerResult: picker.PickerResult = await picker.pick(getContext(this),
  9. [picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO], pickerProfile);
  10. console.log("the pick pickerResult is:" + JSON.stringify(pickerResult));
  11. // ...
  12. } catch (error) {
  13. let err = error as BusinessError;
  14. console.error(`the pick call failed. error code: ${err.message}`);
  15. }
  16. }
复制代码
第二步:将相机拍摄的照片渲染在页面中,利用fs.readSync读出文件字节流,再利用image.createImageSource创建图片资源管理器获取到图片的pixelMap
  1. @State pixelMap: image.PixelMap | null = null
  2. async openCamera() {
  3. try {
  4. // ...
  5. if (pickerResult.resultUri) {
  6. this.showImage = true
  7. }
  8. let file = fs.openSync(pickerResult.resultUri)
  9. let buffer = new ArrayBuffer(fs.statSync(file.fd).size)
  10. fs.readSync(file.fd, buffer)
  11. let imageSource: image.ImageSource = image.createImageSource(buffer)
  12. imageSource.getImageInfo((err, value) => {
  13. if (err) {
  14. return;
  15. }
  16. let opts: image.DecodingOptions = {
  17. editable: true,
  18. desiredSize: {
  19. height: value.size.height,
  20. width: value.size.width
  21. }
  22. };
  23. imageSource.createPixelMap(opts, async (err, pixelMap) => {
  24. this.pixelMap = pixelMap
  25. })
  26. })
  27. } catch (error) {
  28. let err = error as BusinessError;
  29. console.error(`the pick call failed. error code: ${err.message}`);
  30. }
  31. }
  32. build() {
  33. // ...
  34. if(this.showImage) {
  35. Image(this.pixelMap).width("100%")
  36. // ...
  37. }
  38. }
复制代码
第三步:利用drawing.TextBlob.makeFromString绘制水印后 利用SaveButton生存在系统相册中
  1. addWaterMask(pixelMap: image.PixelMap) {
  2. this.imageScale = this.imageWidth / display.getDefaultDisplaySync().width
  3. const canvas = new drawing.Canvas(pixelMap)
  4. const pen = new drawing.Pen()
  5. pen.setColor({ alpha: 255, red: 0, green: 10, blue: 10 })
  6. const font = new drawing.Font()
  7. font.setSize(32 * this.imageScale)
  8. let textWidth = font.measureText("初九会画画", drawing.TextEncoding.TEXT_ENCODING_UTF8)
  9. const brush = new drawing.Brush()
  10. brush.setColor({ alpha: 255, red: 255, green: 0, blue: 0 })
  11. const textBlob = drawing.TextBlob.makeFromString("初九会画画", font, drawing.TextEncoding.TEXT_ENCODING_UTF8)
  12. canvas.attachBrush(brush)
  13. canvas.attachPen(pen)
  14. canvas.drawTextBlob(textBlob, this.imageWidth - 20 * this.imageScale - textWidth, this.imageHeight - 20 * this.imageScale)
  15. canvas.detachBrush();
  16. canvas.detachPen()
  17. return pixelMap
  18. }
  19. SaveButton()
  20. .onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
  21. if (result == SaveButtonOnClickResult.SUCCESS) {
  22. try {
  23. let context = getContext(this);
  24. let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
  25. let uri = await phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png'); // 创建媒体文件
  26. saveToFile(this.addWaterMask(this.pixelMap!), uri);
  27. } catch (err) {
  28. console.error('createAsset failed, message = ', err);
  29. }
  30. } else {
  31. console.error('SaveButtonOnClickResult createAsset failed');
  32. }
  33. })
复制代码
最后

小编在之前的鸿蒙系统扫盲中,有很多朋侪给我留言,差异的角度的问了一些问题,我明显感觉到一点,那就是很多人参与鸿蒙开辟,但是又不知道从那里动手,因为资料太多,太杂,传授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开辟技术?不知道需要重点把握哪些鸿蒙应用开辟知识点?而且学习时频繁踩坑,最终浪费大量时间。以是有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习黑白常有必要的。 
为了确保高效学习,发起规划清楚的学习路线,涵盖以下关键阶段:
 →【纯血版鸿蒙全套最新学习文档】渴望这一份鸿蒙学习文档能够给大家带来帮助~



 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级装备开辟,不仅增补了华为官网未涉及的办理方案
路线图得当人群:
IT开辟职员:想要拓展职业边界
零基础小白:鸿蒙爱好者,渴望从0到1学习,增加一项技能。
技术提拔/进阶跳槽:发展瓶颈期,提拔职场竞争力,快速把握鸿蒙技术
2.学习视频+学习PDF文档
HarmonyOS Next 最新全套视频教程 (鸿蒙语法ArkTS、TypeScript、ArkUI教程……)
​​
 纯血版鸿蒙全套学习文档(面试、文档、全套视频等)
                   

​​​​鸿蒙APP开辟必备
​​
总结
【纯血版鸿蒙全套最新学习文档】
总的来说,华为鸿蒙不再兼容安卓,对步调员来说是一个挑衅,也是一个机会。只有积极应对变革,不停学习和提拔本身,才华在这个变革的时代中立于不败之地。 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦应逍遥

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表