首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

HarmonyOS鸿蒙开发实战(5.0)Web页面长截图实践

  • 25-03-03 06:41
  • 3402
  • 11781
blog.csdn.net

鸿蒙HarmonyOS开发实战往期必看文章:(持续更新......)

HarmonyOS NEXT应用开发性能实践总结(持续更新......)

HarmonyOS NEXT应用开发案例实践总结合集(持续更新......)

一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!

最新版!“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)


介绍

本案例实现了Web组件中网页长截图的方案。支持截图后展示大小浮窗预览、保存图片到相册、手势左滑关闭等功能。

效果图预览

实现思路

本解决方案通过循环滚动Web组件,每次滚动截取当前状态后拼接到离屏画布,最后一次性转为PixelMap图片并显示在全屏模态窗口中。再通过安全控件SaveButton以免权限申请的方式保存到用户的相册中。

  1. 创建Web组件加载指定的网页,获取Web组件和网页的实际尺寸,并给Web组件绑定自定义的id。

由于Web组件为自适应填充剩余空间,所以通过onAreaChange接口来获取Web组件的实际尺寸。 Web网页加载完成后在onPageEnd回调中通过WebviewController的接口runJavaScriptExt执行javascript代码以获取网页的实际大小。

  1. Web({
  2. src: this.webPageUrl,
  3. controller: this.webviewController
  4. })
  5. .id(Constants.WEB_ID)
  6. .onAreaChange((oldValue, newValue) => {
  7. this.webWidth = newValue.width as number;
  8. this.webHeight = newValue.height as number;
  9. logger.info(TAG, `Web component width: ${this.webWidth}, height: ${this.webHeight}`);
  10. })
  11. .onPageEnd(() => {
  12. const script = '[document.documentElement.scrollWidth, document.documentElement.scrollHeight]';
  13. this.webviewController.runJavaScriptExt(script).then((result) => {
  14. switch (result.getType()) {
  15. case webview.JsMessageType.ARRAY:
  16. this.h5Width = (result.getArray() as number[])[0]; // 这里的单位是vp
  17. this.h5Height = (result.getArray() as number[])[1];
  18. logger.info(TAG, `h5Width = ${this.h5Width}, h5Height = ${this.h5Height}`);
  19. break;
  20. default:
  21. logger.error(TAG, `Get web page size tyep error.`);
  22. break;
  23. }
  24. });
  25. })
  1. 创建截图函数,执行滚动截图并拼接。

截图的次数为网页高度/Web组件高度向上取整的结果。 最后一次截图的图片需要特殊处理,去除重复的部分,重复的部分高度即网页高度/Web组件高度取余。通过PixelMap对象的接口crop进行裁剪。

  1. const snipTimes = Math.ceil(this.h5Height / this.webHeight);
  2. for (let i = 0; i < snipTimes; i++) {
  3. let curSnip = await componentSnapshot.get(Constants.WEB_ID);
  4. // 最后一次截图需要特殊处理,去除重复部分
  5. if (i === snipTimes - 1) {
  6. let h = this.h5Height % this.webHeight;
  7. // 裁剪
  8. await curSnip.crop({ x: 0, y: vp2px(this.webHeight - h),
  9. size: {
  10. height: vp2px(h),
  11. width: vp2px(this.webWidth)
  12. }
  13. });
  14. offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, h);
  15. } else {
  16. offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, this.webHeight);
  17. }
  18. }

这里图片拼接的方案选择的是离屏画布渲染对象OffscreenCanvasRenderingContext2D,离屏绘制会将需要绘制的内容先绘制在缓存区,加快绘制速度。

为什么不使用PixelMap首尾拼接?
虽然componentSnapshot.get接口能够直接获取PixelMap对象,但是如果选择直接处理PixelMap需要手动将其转换为ArrayBuffer格式,再转为Uint8Array通过set接口拼接。 整个过程非常繁琐,且消耗资源,并且目前PixelMap接口还有格式限制,影响图片输出。

而使用画布组件,只需要

  1. // 截图时拼接图片
  2. offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, this.webHeight);
  3. ...
  4. // 截图完即可输出完整的长截图
  5. this.mergedImage = offCanvasCtx.getPixelMap(0, 0, this.h5Width, this.h5Height);
  1. 截图后弹出预览窗口,可以滚动查看完整的截图,并保存图片到用户相册中。

本案例使用全屏模态窗口,开始截图后即弹出。截图未完成时,提示用户正在截图,截图完成后转为图片预览窗口,并且支持切换大小窗口。

窗口位置通过position属性设置,结合属性动画可以形成切换时的动画效果。

  1. /**
  2. * 设置弹窗居中。
  3. */
  4. setPopupCenter() {
  5. this.snapPopupPosition = {
  6. x: (this.displayWidth - this.snapPopupWidth) / 2,
  7. y: (this.displayHeight - this.snapPopupHeight) / 2
  8. }
  9. }
  10. /**
  11. * 设置弹窗位置为左下。
  12. */
  13. setPopupBottomLeft() {
  14. this.snapPopupPosition = {
  15. x: Constants.POPUP_MARGIN_LEFT,
  16. y: this.displayHeight - this.snapPopupHeight - Constants.POPUP_MARGIN_BOTTOM
  17. }
  18. }

保存图片相册使用SaveButton安全控件,该接口可以免申请读写相册权限,临时获取存储权限。

  1. // 安全控件的UI有严格的限制,智能使用系统提供的属性
  2. SaveButton({
  3. icon: SaveIconStyle.FULL_FILLED,
  4. text: SaveDescription.SAVE_IMAGE,
  5. buttonType: ButtonType.Capsule
  6. })
  7. .onClick(async (event, result) => {
  8. this.saveSnapshot(result);
  9. })
  10. /**
  11. * 保存图片到相册。
  12. */
  13. async saveSnapshot(result: SaveButtonOnClickResult) {
  14. // TODO: 知识点:使用SaveButton组件可以免申请权限,用户点击后,临时将文件存入系统目录
  15. if (result == SaveButtonOnClickResult.SUCCESS) {
  16. let helper = photoAccessHelper.getPhotoAccessHelper(this.context);
  17. // 使用保存控件
  18. try {
  19. // onClick触发后10秒内通过createAsset接口创建图片文件,10秒后createAsset权限收回。
  20. let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'png');
  21. // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制
  22. let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  23. const imagePackerApi: image.ImagePacker = image.createImagePacker();
  24. let packOpts: image.PackingOption = {
  25. format: Constants.SAVE_IMAGE_FORMAT,
  26. quality: Constants.SAVE_IMAGE_QUALITY,
  27. };
  28. imagePackerApi.packToFile(this.mergedImage, file.fd, packOpts).then(() => {
  29. logger.info(TAG, `Succeeded in packToFile`);
  30. promptAction.showToast({
  31. message: $r('app.string.saved_to_album'),
  32. duration: Constants.SAVED_TO_ALBUM_PROMPT_DURATION
  33. })
  34. }).catch((error: BusinessError) => {
  35. logger.error(TAG, `Failed to packToFile. Error code is ${error.code}, message is ${error.message}`);
  36. })
  37. } catch (error) {
  38. const err: BusinessError = error as BusinessError;
  39. logger.error(TAG, `Failed to save photo. Error code is ${err.code}, message is ${err.message}`);
  40. }
  41. }
  42. this.closeSnapPopup();
  43. }

本案例还支持左滑关闭预览小窗口的手势特性。窗口组件使用gesture通用属性绑定滑动手势,结合属性动画实现滑动窗口效果。

  1. .gesture(
  2. PanGesture(this.panOption)
  3. .onActionStart(() => {
  4. // 保存滑动前的位置,以便用于恢复状态
  5. this.xPositionBefore = this.snapPopupPosition.x as number;
  6. })
  7. .onActionUpdate((event) => {
  8. // 左滑弹窗关闭,但允许向右滑动一小部分,形成弹性效果,提升操作手感
  9. if (event.offsetX < Constants.POPUP_RIGHT_PAN_GESTURE) {
  10. this.snapPopupPosition.x = this.xPositionBefore + event.offsetX;
  11. }
  12. })
  13. .onActionEnd((event) => {
  14. // 左滑超过一定阈值才会触发关闭弹窗,提升用户体验
  15. if (event.offsetX < Constants.POPUP_LEFT_PAN_GESTURE && !this.showPreview) {
  16. // 避免瞬间消失,而是平移动画后消失
  17. this.snapPopupPosition.x = Constants.POPUP_LEFT_SCREEN;
  18. sleep(Constants.ANIMATE_DURATION).then(() => {
  19. this.isShowSnapPopup = false;
  20. })
  21. } else if (!this.showPreview) {
  22. // 小窗预览时,右滑或者左滑没有达到阈值时恢复弹窗位置,结合动画属性形成弹性效果
  23. this.setPopupBottomLeft();
  24. }
  25. })
  26. )

其他窗口UI及效果详见代码。

高性能知识点

  1. 本案例使用了Web预加载接口initializeWebEngine提前加载webview内核,并且使用prepareForPageLoad对目标网页进行预连接,提升打开网页的速度。
  2. 本案例使用了离屏渲染对象来拼接截图,将需要绘制的内容先绘制在缓存区,然后将其转换成图片,加快了绘制速度。
  3. 本案例使用了onAreaChange接口动态获取Web组件的尺寸,该接口属于高频回调接口,避免在该回调中调用冗余和耗时操作。还应尽可能的减少对状态变量的修改以避免触发大量UI重绘和动效耗时。

工程结构&模块类型

  1. webpagesnapshot // har类型
  2. ├───mainpage
  3. │ ├───MainPage.ets // ArkTS页面
  4. ├───common
  5. │ ├───Constants.ets // 常量
  6. │ └───Utils.ets // 通用工具
  7. │

模块依赖

  • 路由模块
  • utils

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)路线图、学习视频、文档用来跟着学习是非常有必要的。 

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员

鸿蒙 NEXT 全栈开发学习笔记  希望这一份鸿蒙学习文档能够给大家带来帮助~

这份鸿蒙(HarmonyOS NEXT)包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。


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

​

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习教程+学习PDF文档

HarmonyOS Next 最新全套视频教程 全球开发者的开源社区,开源代码

  纯血版鸿蒙全套学习文档(面试、文档、全套视频等)  全球开发者的开源社区,开源代码

​​

《鸿蒙大厂面试真题》GitCode - 全球开发者的开源社区,开源代码

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

鸿蒙NEXT全套学习资料
微信名片
注:本文转载自blog.csdn.net的让开,我要吃人了的文章"https://blog.csdn.net/weixin_55362248/article/details/142423577"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top