【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)
}
}