HarmonyOS实战开发-如何实现Web组件中网页长截图。

介绍

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

效果图预览

在这里插入图片描述

实现思路

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

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

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

Web({src: this.webPageUrl,controller: this.webviewController
}).id(Constants.WEB_ID).onAreaChange((oldValue, newValue) => {this.webWidth = newValue.width as number;this.webHeight = newValue.height as number;logger.info(TAG, `Web component width: ${this.webWidth}, height: ${this.webHeight}`);}).onPageEnd(() => {const script = '[document.documentElement.scrollWidth, document.documentElement.scrollHeight]';this.webviewController.runJavaScriptExt(script).then((result) => {switch (result.getType()) {case webview.JsMessageType.ARRAY:this.h5Width = (result.getArray() as number[])[0]; // 这里的单位是vpthis.h5Height = (result.getArray() as number[])[1];logger.info(TAG, `h5Width = ${this.h5Width}, h5Height = ${this.h5Height}`);break;default:logger.error(TAG, `Get web page size tyep error.`);break;}});})

2.创建截图函数,执行滚动截图并拼接。

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

const snipTimes = Math.ceil(this.h5Height / this.webHeight);
for (let i = 0; i < snipTimes; i++) {let curSnip = await componentSnapshot.get(Constants.WEB_ID);// 最后一次截图需要特殊处理,去除重复部分if (i === snipTimes - 1) {let h = this.h5Height % this.webHeight;// 裁剪await curSnip.crop({ x: 0, y: vp2px(this.webHeight - h),size: {height: vp2px(h),width: vp2px(this.webWidth)}});offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, h);} else {offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, this.webHeight);}
}

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

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

而使用画布组件,只需要

// 截图时拼接图片
offCanvasCtx.drawImage(curSnip, 0, this.webHeight * i, this.webWidth, this.webHeight);
...
// 截图完即可输出完整的长截图        
this.mergedImage = offCanvasCtx.getPixelMap(0, 0, this.h5Width, this.h5Height);

3.截图后弹出预览窗口,可以滚动查看完整的截图,并保存图片到用户相册中。

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

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

/*** 设置弹窗居中。*/setPopupCenter() {this.snapPopupPosition = {x: (this.displayWidth - this.snapPopupWidth) / 2,y: (this.displayHeight - this.snapPopupHeight) / 2}}/*** 设置弹窗位置为左下。*/setPopupBottomLeft() {this.snapPopupPosition = {x: Constants.POPUP_MARGIN_LEFT,y: this.displayHeight - this.snapPopupHeight - Constants.POPUP_MARGIN_BOTTOM}}

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

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

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

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

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

高性能知识点

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

工程结构&模块类型

webpagesnapshot                // har类型
├───mainpage
│   ├───MainPage.ets           // ArkTS页面
├───common
│   ├───Constants.ets          // 常量
│   └───Utils.ets              // 通用工具

模块依赖

  • 路由模块
  • utils

如果大家还没有掌握鸿蒙,现在想要在最短的时间里吃透它,我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程》以及《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

在这里插入图片描述

《鸿蒙开发学习手册》:

如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.基本概念
2.构建第一个ArkTS应用
3.……

在这里插入图片描述

开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3017594.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Arduino PlatformIO避坑记

实在受不了Arduino IDE上古时期的界面风格&#xff0c;最要命的是编译速度慢到极点&#xff0c;好在有PlatformIO。VS搭配PlatformIO&#xff0c;有微软加持&#xff0c;界面自然是妥妥的了&#xff0c;编译速度提升也肉眼可见。 至于PlatformIO的安装过程&#xff0c;网上教程…

Day 24 数据库管理及数据类型

数据库管理及数据类型 一&#xff1a;数据类型 1.数值类型 整数类型 ​ 整数类型&#xff1a;TINYINT SMALLINT MEDIUMINT INT BIGINT ​ 作用&#xff1a;用于存储用户的年龄、游戏的Level、经验值等 浮点数类型 ​ 浮点数类型&#xff1a;FLOAT DOUBLE ​ 作用&#xf…

超疏水TiO₂纳米纤维网膜的良好性能

超疏水TiO₂纳米纤维网膜是一种具有特殊性能的材料&#xff0c;它结合了TiO₂的光催化性能和超疏水表面的自清洁、防腐、防污等特性。这种材料在防水、自清洁、油水分离等领域具有广阔的应用前景。 制备超疏水TiO₂纳米纤维网膜的过程中&#xff0c;通过精确控制纺丝溶液的成分…

vue3专栏项目 -- 项目介绍以及准备工作

这是vue3TS的项目&#xff0c;是一个类似知乎的网站&#xff0c;可以展示专栏和文章的详情&#xff0c;可以登录、注册用户&#xff0c;可以创建、删除、修改文章&#xff0c;可以上传图片等等。 这个项目全部采用Composition API 编写&#xff0c;并且使用了TypeScript&#…

PyQt5中重要的概念:信号与槽

PyQt中信号与槽概念定义如下&#xff08;网络上引用的&#xff09;&#xff1a; 信号&#xff08;signal&#xff09;和槽&#xff08;slot&#xff09;是Qt的核心机制&#xff0c;也是在PyQt编程中对象之间进行通信的机制。在创建事件循环之后&#xff0c;通过建立信号和槽的…

3W 1.5KVDC 3KVDC 隔离宽范围输入,单、双输出 DC/DC 电源模块——TP2L-3W 系列

TP2L-3W系列是一款高性能、超小型的电源模块&#xff0c;宽范围2:1,4:1输入&#xff0c;输出有稳压和连续短路保护功能&#xff0c;隔离电压为1.5KVDC、3KVDC工作温度范围为–40℃到85℃。特别适合对输出电压的精度有严格要求的地方&#xff0c;外部遥控功能对您的设计又多一项…

AIGC绘画辅助网站

Midjourney风格样式 Midjourney Style Classifier | Andrei Kovalevs Midlibrary

Python-VBA函数之旅-property函数

目录 一、property函数的常见应用场景 二、property函数使用注意事项 三、如何用好property函数&#xff1f; 1、property函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;神奇夜光杯-CSDN博客 一、prop…

Django 4.x 智能分页get_elided_page_range

Django智能分页 分页效果 第1页的效果 第10页的效果 带输入框的效果 主要函数 # 参数解释 # number: 当前页码&#xff0c;默认&#xff1a;1 # on_each_side&#xff1a;当前页码前后显示几页&#xff0c;默认&#xff1a;3 # on_ends&#xff1a;首尾固定显示几页&#…

多对多字段,执行aerich migrate命令报错

报错状态&#xff1a; 解决&#xff1a; 目前只知道通过修改源码的方式进行更改

【牛客】【模板】差分

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 差分模板。 b[0]a[0]; b[1]a[1]-a[0]; b[2]a[2]-a[1]; ...... b[n-1]a[n-1]-a[n-2]; b[n]a[n]-a[n-1]; 差分标记&#xff1a;b[l]k,b…

一部手机实现全行业的AI实景自动无人直播软件:为商家提供更便捷的推广方式

随着人工智能技术的快速发展&#xff0c;AI实景自动无人直播软件成为了商家推广产品的新宠。这款软件结合了智能讲解、一键开播、智能回复等多项功能&#xff0c;为商家提供了一种全新的直播方式。 首先&#xff0c;智能讲解功能让专业主播录制直播脚本&#xff0c;并通过软件自…

计划订单转采购申请的增强点和可以增强的内容

MD15 MD14 计划订单转采购申请&#xff0c;涉及的增强点和增强内容 对于外协的采购申请&#xff0c;有时候需要对组件的内容做一些特殊的处理&#xff0c;但是处理组件清单的增强ME_COMPONENTS_UPDATE的增强点&#xff08;这个增强点对于手工创建的外协PR、外协PO,外协pr转外协…

【负载均衡在线OJ项目日记】编译与日志功能开发

目录 日志功能开发 常见的日志等级 日志功能代码 编译功能开发 创建子进程和程序替换 重定向 编译功能代码 日志功能开发 日志在软件开发和运维中起着至关重要的作用&#xff0c;目前我们不谈运维只谈软件开发&#xff1b;日志最大的作用就是用于故障排查和调试&#x…

DBCO-PEG-DBCO,聚乙二醇双二苯基环辛炔可以用于功能化修饰生物材料的表面

【试剂详情】 英文名称 DBCO-PEG-DBCO 中文名称 二苯基环辛炔-聚乙二醇-二苯基环辛炔&#xff0c; 聚乙二醇双二苯基环辛炔 外观性状 由分子量决定&#xff0c;粘稠液体或者固体。 分子量 0.4k&#xff0c;0.6k&#xff0c;1k&#xff0c;2k&#xff0c;3.4k&#xff0c…

F.softmax(cls) + 1e-4

这个代码段中的 softmax 操作结合了一个微小的常数&#xff0c;这个常数通常被称为平滑化参数。softmax 函数将原始的分类输出转换为概率分布&#xff0c;其公式如下&#xff1a; 在实践中&#xff0c;当某些分类得分特别大时&#xff0c;softmax 函数会将对应的概率接近于 1&a…

【C++】C++11--- 类的新功能

目录 类的新功能 默认成员函数 示例 类成员变量初始化 强制生成默认函数的关键字default 禁止生成默认函数的关键字delete 类的新功能 默认成员函数 构造函数析构函数拷贝构造函数拷贝赋值重载取地址重载const取地址重载 C11在原先的6个默认成员函数的基础上&#xff0c…

Baidu Comate:你的智能编程伙伴,编程界的AI革命者

文章目录 Baidu Comate 介绍Baidu Comate下载安装Baidu Comate 实操体验代码解释函数注释行间注释调优建议生成单测注释生成实时续写常用快捷方式智能对话问答 Baidu Comate 建议改进Baidu Comate 体验总结 Baidu Comate 介绍 Baidu Comate 智能编码助手 是基于文心大模型&…

【工作记录】openjdk-22基础镜像的构建

背景 近期使用到的框架底层都用的是springboot3.0&#xff0c;要求jdk版本在17甚至更高。 于是决定制作一个基于openjdk22的基础镜像&#xff0c;本文对这一过程进行记录。 作为记录的同时也希望能够帮助到需要的朋友。 期望效果 容器内可以正常使用java相关命令且版本是2…

浅谈操作系统中的重要概念——线程(3)——设计模式

文章目录 一、什么是设计模式&#xff1f;二、单例模式2.1、饿汉模式2.2、懒汉模式2.3、多线程情况下调用 饿汉模式与懒汉模式 谁是安全的&#xff1f;&#xff1f;&#xff08;重点&#xff09; 三、工厂模式 一、什么是设计模式&#xff1f; 设计模式就相当于菜谱&#xff0…