【HarmonyOS】组件长截屏方案

news2024/12/23 22:58:22

【HarmonyOS】普通组件与web组件长截屏方案:原则是利用Scroll内的组件可以使用componentSnapshot完整的截屏

【普通组件长截屏】

import { componentSnapshot, promptAction } from '@kit.ArkUI'
import { common } from '@kit.AbilityKit'
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import fs from '@ohos.file.fs';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Page37 {
  @State lineHeight: number = 0 // 单行文本的高度
  @State pageHeight: number = 0 // 每页的最大高度
  @State totalContentHeight: number = 0 // 整个文本内容的高度
  @State textContent: string = " " // 文本内容,默认一个空格是为了计算单行文本的高度
  @State scrollOffset: number = 0 // 当前滚动偏移量
  @State totalPages: number = 1 // 总页数
  @State currentPage: number = 1 // 当前页数
  scroller: Scroller = new Scroller() // 滚动条实例

  resetMaxLineHeight() {
    if (this.lineHeight > 0 && this.pageHeight > 0 && this.totalContentHeight > 0) {
      this.pageHeight = (Math.floor(this.pageHeight / this.lineHeight)) * this.lineHeight
      this.totalPages = Math.ceil(this.totalContentHeight / this.pageHeight) //向上取整得到总页数

    }
  }

  build() {
    Column() {
      SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
        if (result === SaveButtonOnClickResult.SUCCESS) {
          const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
          // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片
          let helper = photoAccessHelper.getPhotoAccessHelper(context);
          try {
            // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。
            let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
            // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制
            let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
            componentSnapshot.get("aaaa").then((pixelMap) => {
              let packOpts: image.PackingOption = { format: 'image/png', quality: 100 }
              const imagePacker: image.ImagePacker = image.createImagePacker();
              return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => {
                imagePacker.release(); //释放
                fs.close(file.fd);
                promptAction.showToast({
                  message: '图片已保存至相册',
                  duration: 2000
                });
              });
            })
          } catch (error) {
            const err: BusinessError = error as BusinessError;
            console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`);
          }

        } else {
          promptAction.showToast({
            message: '设置权限失败!',
            duration: 2000
          });
        }
      })
      Text('第一章')
        .margin({ top: 10, bottom: 10 })
        .backgroundColor(Color.Pink)
        .width('100%')
        .textAlign(TextAlign.Center)

      Column() {
        Scroll(this.scroller) {
          Column() {
            Text(this.textContent)
              .id('aaaa')
              .backgroundColor(Color.Orange)
              .fontSize(20)
              .lineHeight(40)
              .fontColor(Color.Black)// .textOverflow({ overflow: TextOverflow.Clip })
              .margin({ top: this.scrollOffset })
              .onAreaChange((oldArea: Area, newArea: Area) => {
                if (this.lineHeight == 0 && newArea.height > 0) {
                  this.lineHeight = newArea.height as number
                  this.resetMaxLineHeight()
                  //添加数据测试
                  let str = ""
                  for (let i = 1; i <= 20; i++) {
                    str += ` ${i}、荣誉和耻辱,是荣辱观中的一对基本范畴,是指社会对人们行为褒贬评价以及人们对这种评价的自我感受。知荣辱,是人性的标志,是人区别于动物、人之为人的重要标准。`
                  }
                  this.textContent = str
                  return
                }
                if (this.totalContentHeight != newArea.height) {
                  console.info(`newArea.height:${newArea.height}`)
                  this.totalContentHeight = newArea.height as number
                  this.resetMaxLineHeight()
                }
              })
          }.hitTestBehavior(HitTestMode.Block) //禁止滑动
        }.scrollBar(BarState.Off)
        .constraintSize({ maxHeight: this.pageHeight == 0 ? 1000 : this.pageHeight })

      }
      .width('100%')
      .layoutWeight(1)

      .onAreaChange((oldArea: Area, newArea: Area) => {
        if (this.pageHeight == 0 && newArea.height > 0) {
          this.pageHeight = newArea.height as number
          this.resetMaxLineHeight()
        }
      })

      Row() {
        Button('上一页').onClick(() => {
          if (this.currentPage == 1) {
            promptAction.showToast({ message: "没有上一页了" })
            return;
          }
          this.scrollOffset += this.pageHeight
          this.currentPage--;
        })
        Text(`${this.currentPage}/${this.totalPages}`)
        Button('下一页').onClick(() => {
          if (this.currentPage == this.totalPages) {
            promptAction.showToast({ message: "没有下一页了" })
            return;
          }
          this.scrollOffset -= this.pageHeight
          this.currentPage++;
        })
      }.margin({ top: 10, bottom: 10 }).backgroundColor(Color.Pink).width('100%').justifyContent(FlexAlign.SpaceAround)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Gray)
  }
}

【web组件长截屏】

src/main/resources/rawfile/test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <script>
        //用于根据浏览器对 CSS.supports 和 env/constant 的支持情况,动态地调整视口元标签的内容,以达到最佳的页面显示效果。
        var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
            CSS.supports('top: constant(a)'))
        document.write(
            '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
            (coverSupport ? ', viewport-fit=cover' : '') + '" />')
    </script>

    <title></title>
    <!--用于设置浏览器页签上显示的小图标 start-->
    <!-- <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" /> -->
    <link rel="stylesheet" href="mycss.css" />
    <link rel="icon" href="./static/favicon.ico" />
    <!--用于设置浏览器页签上显示的小图标 end-->
    <!--preload-links-->
    <!--app-context-->
</head>
<body>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>测试测试</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>哈哈哈哈</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>1111111</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>2222222</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>aaaaaaa</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>bbbbbbb</div>
<div>到底了</div>
<div id="webBottom"></div>
</body>
<script>

    //Android禁止微信调整字体大小
    (function() {
        if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {
            handleFontSize();
        } else {
            if (document.addEventListener) {
                document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);
            } else if (document.attachEvent) {
                document.attachEvent("WeixinJSBridgeReady", handleFontSize);
                document.attachEvent("onWeixinJSBridgeReady", handleFontSize);
            }
        }

        function handleFontSize() {
            WeixinJSBridge.invoke('setFontSizeCallback', {
                'fontSize': 0
            });
            WeixinJSBridge.on('menu:setfont', function() {
                WeixinJSBridge.invoke('setFontSizeCallback', {
                    'fontSize': 0
                });
            });
        }
    })();
    function setWebHeight() {
        window.hm.setWebHeight(document.getElementById('webBottom').offsetTop);
    }

    // 在文档加载完成后执行 setWebHeight 函数
    window.onload = function() {
        setWebHeight();
    };
</script>

</html>

src/main/ets/pages/Page42.ets

import { webview } from '@kit.ArkWeb';
import web_webview from '@ohos.web.webview';
import dataPreferences from '@ohos.data.preferences';
import { common } from '@kit.AbilityKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { componentSnapshot, promptAction } from '@kit.ArkUI';
import fs from '@ohos.file.fs';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

class WebService {

  setWebHeight = (height: string)  => {
    console.info('web高度:', height);
    getContext().eventHub.emit("设置web高度",height)
  }
}

@Entry
@Component
struct Page42 {
  controller: webview.WebviewController = new webview.WebviewController();
  webService: WebService = new WebService( );
  methodList: Array<string> = []
  @State isShort: boolean = true
  @State webHeight: number | undefined = undefined
  aboutToAppear(): void {
    this.methodList.splice(0) //清空原数组
    console.info('====this.testObjtest', JSON.stringify(this.webService))
    Object.keys(this.webService).forEach((key) => {
      this.methodList.push(key)
      console.info('====key', key)
    });

    getContext().eventHub.on("设置web高度",(height:number)=>{
      this.webHeight = height
    })
  }
  build() {
    Scroll() {
      Column() {
        SaveButton().onClick(async (event: ClickEvent, result: SaveButtonOnClickResult) => {
          if (result === SaveButtonOnClickResult.SUCCESS) {
            const context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
            // 免去权限申请和权限请求等环节,获得临时授权,保存对应图片
            let helper = photoAccessHelper.getPhotoAccessHelper(context);
            try {
              // onClick触发后5秒内通过createAsset接口创建图片文件,5秒后createAsset权限收回。
              let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
              // 使用uri打开文件,可以持续写入内容,写入过程不受时间限制
              let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
              componentSnapshot.get("aaaa").then((pixelMap) => {
                let packOpts: image.PackingOption = { format: 'image/png', quality: 100 }
                const imagePacker: image.ImagePacker = image.createImagePacker();
                return imagePacker.packToFile(pixelMap, file.fd, packOpts).finally(() => {
                  imagePacker.release(); //释放
                  fs.close(file.fd);
                  promptAction.showToast({
                    message: '图片已保存至相册',
                    duration: 2000
                  });
                });
              })
            } catch (error) {
              const err: BusinessError = error as BusinessError;
              console.error(`Failed to save photo. Code is ${err.code}, message is ${err.message}`);
            }

          } else {
            promptAction.showToast({
              message: '设置权限失败!',
              duration: 2000
            });
          }
        })
        Text('1234测试顶部').backgroundColor(Color.Red).width('100%').height('800lpx')
        Web({ src: $rawfile('test.html'), controller: this.controller, renderMode: RenderMode.SYNC_RENDER })
          .width('100%')
          .height(this.webHeight)
          .layoutMode(WebLayoutMode.FIT_CONTENT)
          .javaScriptAccess(true)//设置是否允许执行JavaScript脚本,默认允许执行。
          .mixedMode(MixedMode.All)//HTTP和HTTPS混合
          .javaScriptProxy({
            name: "hm",
            object: this.webService,
            methodList: this.methodList,
            controller: this.controller,
          }).id("aaaa")
        Text('测试底部').backgroundColor(Color.Blue).width('100%').height('800lpx')
      }
    }.width('100%').height('100%').align(Alignment.Top)
  }
}

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

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

相关文章

增量式编码器实现原理

目录 概述 1 认识增量式编码器 1.1 概述 1.2 增量式编码器的特性 1.3 编码器的硬件 2 增量式编码器实现原理 2.1 编码器信号 2.2 正反转判断 概述 本文主要介绍增量式编码器实现原理&#xff0c;包括增量式编码器的特性&#xff0c;信号特性&#xff0c;以及如何使用编…

【稳定且高效的分治排序 —— 归并排序算法】

【稳定且高效的分治排序 —— 归并排序算法】 归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效、稳定的排序算法&#xff0c;采用分治法的典型应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序&#xff0…

深度探索与实战编码:利用Python与AWS签名机制高效接入亚马逊Product Advertising API获取商品详情

亚马逊商品详情接口技术贴及代码示例 在电商数据分析和产品管理中&#xff0c;获取商品的详细信息是至关重要的一环。亚马逊作为全球领先的电商平台&#xff0c;提供了丰富的商品数据和强大的API接口&#xff0c;使得开发者能够轻松获取商品的详细信息。本文将详细介绍如何使用…

NASA:ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006数据集

目录 简介 代码 引用 网址推荐 0代码在线构建地图应用 机器学习 ATLAS/ICESat-2 L3A Along Track Inland Surface Water Data V006 简介 ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006 ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006是指由ATLAS/ICESat-2卫星获取的针对陆地…

矿石运输船数据集、散货船数据集、普通货船数据集、集装箱船数据集、渔船数据集以及客船数据集

海船&#xff1a;用于船只检测的大规模精准标注数据集 我们很高兴地介绍一个新的大规模数据集——海船&#xff0c;该数据集专为训练和评估船只目标检测算法而设计。目前&#xff0c;这个数据集包含31,455张图像&#xff0c;并涵盖了六种常见的船只类型&#xff0c;包括矿石运…

en造数据结构与算法C# 二叉排序树 泛型类的基本构成

前置知识&#xff1a;二叉树 en造数据结构与算法C# 二叉树的顺序存储和前中后序遍历-CSDN博客 二叉排序树基本规则 二叉排序树的特点就是有序&#xff0c;其基本规则分为两个点 1.按照顺序去存储节点(下图绿色的顺序) 2.其次&#xff0c;在第一点的基础上&#xff0c;比…

python之装饰器、迭代器、生成器

装饰器 什么是装饰器&#xff1f; 用来装饰其他函数&#xff0c;即为其他函数添加特定功能的函数。 装饰器的两个基本原则&#xff1a; 装饰器不能修改被装饰函数的源码 装饰器不能修改被装饰函数的调用方式 什么是可迭代对象&#xff1f; 在python的任意对象中&#xff…

Centos 7.9 Kubeadm安装k8s1.20.11

一、环境 主机用途192.168.76.140k8s-master1192.168.76.141k8s-node1 二、设置yum源 由于系统已经关闭&#xff0c;可以用centos9尝试 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak vi /etc/yum.repos.d/CentOS-Base.repo# 使用阿里云的y…

ARM基础架构-文档导读系列

第一章 简介 Introducing the Arm architecture 第二章 指令集 Armv8-A Instruction Set ArchitectureLearn the architecture -A64 Instruction Set Architecture 第三章 指令集 ARM异常模型GICv3 v4 overviewGeneric Interrupt Controller v3 and v4, VirtualizationArm…

皮肤病检测-目标检测数据集(包括VOC格式、YOLO格式)

皮肤病检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1XNTo-HsBCHJp2UA-dpn5Og?pwdlizo 提取码&#xff1a;lizo 数据集信息介绍&#xff1a; 共有 2025 张图像和一一对应的标注文件 标注文件格式提供…

工作安排 - 华为OD统一考试(E卷)

2024华为OD机试(C卷+D卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时长(单位h)和报酬,工作的总报酬为所有已完成工作的报酬之和。那么请你帮小明安排一下工作,保证小明在指定…

说说海外云手机的自动化功能

在全球社交媒体营销中&#xff0c;通过自动化功能&#xff0c;企业不再需要耗费大量时间和精力手动监控和操作每台设备。这意味着&#xff0c;企业可以显著提升效率、节省成本&#xff0c;同时减少对人力资源的依赖。那么&#xff0c;海外云手机的自动化功能具体能带来哪些优势…

Linux云计算 |【第四阶段】NOSQL-DAY3

主要内容&#xff1a; redis主从复制、哨兵服务&#xff08;高可用&#xff09;、数据持久化&#xff08;RDB、AOF&#xff09; 一、Redis主从复制概述 Redis 主从复制是一种数据复制机制&#xff0c;用于在多个 Redis 实例之间同步数据&#xff0c;以提高系统的可用性、可靠…

JVM(HotSpot):方法区(Method Area)

文章目录 一、内存结构图二、方法区定义三、内存溢出问题四、常量池与运行时常量池 一、内存结构图 1.6 方法区详细结构图 1.8方法区详细结构图 1.8后&#xff0c;方法区是JVM内存的一个逻辑结构&#xff0c;真实内存用的本地物理内存。 且字符串常量池从常量池中移入堆中。 …

Actions Speak Louder than Words Meta史诗级的端到端推荐大模型落地

发现好久之前整理的推荐系统被遗忘在了草稿箱&#xff0c;让它出来见见世面。。。后续空了持续更新 文章目录 1.Background2.Related works2.1 典型推荐模型2.1.1 DIN2.1.2 DIEN2.1.3 SIM2.1.4 MMoE2.1.5 其他 2.2. 生成式推荐 3.Method3.1 统一特征空间3.2 重塑召回排序模型3.…

MySQL 面试题及答案

MySQL 面试题及答案&#xff1a; 一、基础问题 什么是数据库索引&#xff1f;有哪些类型&#xff1f; 答&#xff1a;数据库索引是一种数据结构&#xff0c;用于提高数据库查询的效率。它就像一本书的目录&#xff0c;可以快速定位到特定的数据行。 类型主要有&#xff1a; …

C++实现二叉树的创建删除,dfslfs,求叶子结点个数,求叶子结点个数,求树的高度

C实现二叉树的创建删除&#xff0c;dfs/lfs,求叶子结点个数&#xff0c;求树的高度 基本算法&#xff1a; 用链栈建立二叉树&#xff0c;通过递归实现深度优先的三种遍历&#xff0c;用队列实现广度优先层次遍历。借助递归思想求解叶子结点个数和树的深度。 tree.h定义基本的…

sysbench 命令:跨平台的基准测试工具

一、命令简介 sysbench 是一个跨平台的基准测试工具&#xff0c;用于评估系统性能&#xff0c;包括 CPU、内存、文件 I/O、数据库等性能。 ‍ 比较同类测试工具 bench.sh 在上文 bench.sh&#xff1a;Linux 服务器基准测试中介绍了 bench.sh 一键测试脚本&#xff0c;它对…

CAT1 RTU软硬件设计开源资料分析(TCP协议+Modbus协议+GNSS定位版本 )

01 CAT1 RTU方案简介&#xff1a; 远程终端单元( Remote Terminal Unit&#xff0c;RTU)&#xff0c;一种针对通信距离较长和工业现场环境恶劣而设计的具有模块化结构的、特殊的计算机测控单元&#xff0c;它将末端检测仪表和执行机构与远程控制中心相连接。 奇迹TCP RTUGNS…

【MySQL】数据库--索引

索引 1.索引 在数据中索引最核心的作用就是&#xff1a;加速查找 1.1 索引原理 索引的底层是基于BTree的数据存储结构 如图所示&#xff1a; 很明显&#xff0c;如果有了索引结构的查询效率比表中逐行查询的速度要快很多且数据越大越明显。 数据库的索引是基于上述BTree的…