1
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
## 1.0.1(2025-07-27)
|
||||
* 兼容鸿蒙
|
||||
## 1.0.0(2024-12-18)
|
||||
* 对屏幕截图保存(不需要权限),对指定截图进行保存,安卓,ios支持,web不支持.
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"id": "x-screenshot-s",
|
||||
"displayName": "应用页截图 页面局部截图 保存页面图片",
|
||||
"version": "1.0.0",
|
||||
"description": "可以对应用页面进行保存,或者对指定节点截图保存图片",
|
||||
"keywords": [
|
||||
"tmui,截图,页面图片"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.8"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "uts",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "10.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "n",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-android": {
|
||||
"minVersion": "21"
|
||||
},
|
||||
"app-ios": {
|
||||
"minVersion": "10"
|
||||
},
|
||||
"app-harmony": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "n",
|
||||
"Android Browser": "n",
|
||||
"微信浏览器(Android)": "n",
|
||||
"QQ浏览器(Android)": "n"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "n",
|
||||
"IE": "n",
|
||||
"Edge": "n",
|
||||
"Firefox": "n",
|
||||
"Safari": "n"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "n",
|
||||
"阿里": "n",
|
||||
"百度": "n",
|
||||
"字节跳动": "n",
|
||||
"QQ": "n",
|
||||
"钉钉": "n",
|
||||
"快手": "n",
|
||||
"飞书": "n",
|
||||
"京东": "n"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "n",
|
||||
"联盟": "n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
# x-screenshot-s
|
||||
### 开发文档
|
||||
|
||||
1. 可以对窗口进行静默截图
|
||||
2. 可以对某一个view节点进行截图
|
||||
|
||||
### 应用场景
|
||||
|
||||
1. 对产品端,给用户反馈保存时,可以对当前反馈的页面进行截图反馈,比如界面异常上传等
|
||||
2. 对局部节点截图保存可以进行对局部的分享保存,比如页面的排版海报
|
||||
3. 对整个页面分享保存
|
||||
|
||||
### 兼容性
|
||||
|
||||
| Harmony | IOS | Android | WEB | 小程序 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 支持 | 支持 | 支持 | x | x |
|
||||
|
||||
### 注意事项
|
||||
|
||||
使用前一定要打基座才可用,一定要在页面上先引用,再去打基座。
|
||||
如果你mac开发。ios可以不用打基座,能直接使用(但前提是你要配置好原生开发环境,否则一样要打包)
|
||||
如果你是开始安卓,不管是mac,win电脑都要打包基座才能使用。
|
||||
|
||||
### API说明
|
||||
|
||||
#### getRootShotImage
|
||||
```ts
|
||||
function getRootShotImage(callback: (str: string) => void): void
|
||||
```
|
||||
|
||||
|
||||
**参数说明:**
|
||||
- callback:截图完成后的回调函数
|
||||
- str:截图保存后的文件路径,如果截图失败则返回空字符串
|
||||
|
||||
#### getElementShotImage
|
||||
```ts
|
||||
function getElementShotImage(ele: UniElement|null, callback: (str: string) => void): void
|
||||
```
|
||||
|
||||
**鸿蒙不支持此方法,原因在于官方的 sdk 无法获取鸿蒙节点的原生 View 实例信息**
|
||||
|
||||
**参数说明:**
|
||||
- ele:要截图的目标元素,UniElement类型
|
||||
- callback:截图完成后的回调函数
|
||||
- str:截图保存后的文件路径,如果截图失败则返回空字符串
|
||||
|
||||
|
||||
### 示例代码
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<x-sheet id="screentIds">
|
||||
<x-button class="mb-16" :block="true" @click="getScreenimg">保存屏幕图片</x-button>
|
||||
<x-button :block="true" @click="getSEleimg">保存节点截图</x-button>
|
||||
</x-sheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {getRootShotImage, getElementShotImage} from "@/uni_modules/x-screenshot-s"
|
||||
|
||||
const getScreenimg = () => {
|
||||
getRootShotImage((str: string) => {
|
||||
if(str == null) return;
|
||||
console.log(str)
|
||||
uni.previewImage({
|
||||
current: str,
|
||||
urls: [str] as string[]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const getSEleimg = () => {
|
||||
let ele = uni.getElementById("screentIds") as UniElement|null
|
||||
getElementShotImage(ele, (str: string) => {
|
||||
if(str == null) return;
|
||||
console.log(str)
|
||||
uni.previewImage({
|
||||
current: str,
|
||||
urls: [str] as string[]
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. 截图黑屏或无内容
|
||||
- 确保已正确打包基座
|
||||
- 检查目标View是否已完成渲染
|
||||
- Android端确保View的宽高不为0
|
||||
|
||||
2. 截图文件路径无效
|
||||
- 检查应用是否有存储权限
|
||||
- 确保缓存目录可写入
|
||||
|
||||
3. 性能注意事项
|
||||
- 大尺寸View截图可能占用较多内存
|
||||
- 建议在截图后及时释放不需要的图片资源
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import ViewGroup from 'android.view.ViewGroup';
|
||||
import Bitmap from 'android.graphics.Bitmap';
|
||||
import View from 'android.view.View';
|
||||
import FileOutputStream from 'java.io.FileOutputStream';
|
||||
import File from 'java.io.File';
|
||||
import Canvas from 'android.graphics.Canvas';
|
||||
function savePhotoTocache(img : Bitmap) : string {
|
||||
let context = UTSAndroid.getAppContext()!
|
||||
let filepath = context.getExternalCacheDir()?.getPath() ?? ""
|
||||
let fileid = Math.random().toString(16).substring(2, 11);
|
||||
let file = new File(filepath, fileid + '.jpg');
|
||||
let fos = new FileOutputStream(file)
|
||||
img.compress(Bitmap.CompressFormat.JPEG, 100, fos);
|
||||
return file.getPath()
|
||||
}
|
||||
function getScreenImage() {
|
||||
let decorView : ViewGroup = UTSAndroid.getUniActivity()!.window.decorView as ViewGroup
|
||||
let rootview : View = decorView.getRootView()
|
||||
// rootview.setDrawingCacheEnabled(true)
|
||||
// let bitmap = Bitmap.createBitmap(rootview.getDrawingCache())
|
||||
// rootview.setDrawingCacheEnabled(false)
|
||||
|
||||
let bitmap = Bitmap.createBitmap(rootview.width, rootview.height, Bitmap.Config.ARGB_8888)
|
||||
let canvas = new Canvas(bitmap)
|
||||
rootview.draw(canvas)
|
||||
|
||||
|
||||
return savePhotoTocache(bitmap);
|
||||
}
|
||||
/**
|
||||
* 获取窗口截图
|
||||
*/
|
||||
export function getRootShotImage(callback : (str : string) => void) {
|
||||
callback(getScreenImage())
|
||||
}
|
||||
/**
|
||||
* 获取指定元素截图
|
||||
*/
|
||||
export function getElementShotImage(ele:UniElement|null, callback : (str : string) => void) {
|
||||
if(ele == null) {
|
||||
callback("")
|
||||
return;
|
||||
}
|
||||
let view = ele.getAndroidView() as View|null;
|
||||
if(view == null) {
|
||||
callback("")
|
||||
return;
|
||||
}
|
||||
let rootview = view as View;
|
||||
// rootview.setDrawingCacheEnabled(true)
|
||||
// let bitmap = Bitmap.createBitmap(rootview.getDrawingCache())
|
||||
// rootview.setDrawingCacheEnabled(false)
|
||||
let bitmap = Bitmap.createBitmap(rootview.width, rootview.height, Bitmap.Config.ARGB_8888)
|
||||
let canvas = new Canvas(bitmap)
|
||||
rootview.draw(canvas)
|
||||
|
||||
let imgpath:string = savePhotoTocache(bitmap);
|
||||
callback(imgpath)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"x_screenshot_s": "./lib/x_screenshot_s.har"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { x_getElementShotImage,x_getRootShotImage } from "x_screenshot_s";
|
||||
/**
|
||||
* 获取窗口截图
|
||||
*/
|
||||
export function getRootShotImage(callback : (str : string) => void) {
|
||||
x_getRootShotImage(UTSHarmony.getCurrentWindow()!.getUIContext(),callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定元素截图
|
||||
*/
|
||||
export function getElementShotImage(ele:UniElement|null, callback : (str : string) => void) {
|
||||
x_getElementShotImage(UTSHarmony.getCurrentWindow()!.getUIContext(),ele?.getAttribute("id"),callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归查询
|
||||
*/
|
||||
function iterateElement(homeElement : UniElement) : UniNativeViewElement | null {
|
||||
if ("NATIVE-VIEW" == homeElement.nodeName) {
|
||||
return homeElement as UniNativeViewElement
|
||||
}
|
||||
for (const perChildEle of homeElement.children) {
|
||||
let findEle = iterateElement(perChildEle)
|
||||
if (findEle != null) {
|
||||
return findEle
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getPageRootView(id : string, ins : ComponentPublicInstance | null = null) : FrameNode | null {
|
||||
if (ins == null) {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
const page = pages[pages.length - 1]
|
||||
const rootViewElement = page.getElementById(id)
|
||||
if (rootViewElement != null) {
|
||||
/**
|
||||
* 找到了root节点,递归检索目标 native-view
|
||||
*/
|
||||
const nativeViewElement = iterateElement(rootViewElement)
|
||||
|
||||
if (nativeViewElement != null) {
|
||||
return nativeViewElement.getHarmonyFrameNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* 尝试迭代遍历
|
||||
*/
|
||||
if (ins.$el != null) {
|
||||
const nativeViewElement = iterateElement(ins.$el as UniElement)
|
||||
if (nativeViewElement != null) {
|
||||
|
||||
return nativeViewElement.getHarmonyFrameNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deploymentTarget": "10"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { UIButton, UIControl, UIImage, UIView, UIImageView, UIImageWriteToSavedPhotosAlbum, UIGraphicsImageRenderer } from "UIKit";
|
||||
import { URL, FileManager, NSAttributedString } from 'Foundation';
|
||||
function savePhotoTocache(image : UIImage) : string {
|
||||
let fileid = Math.random().toString(16).substring(2, 11);
|
||||
let userDir = FileManager.default.urls(for = FileManager.SearchPathDirectory.cachesDirectory, in = FileManager.SearchPathDomainMask.userDomainMask).first!
|
||||
let filename = fileid + '.jpg';
|
||||
let destinationURL = userDir.appendingPathComponent(filename)
|
||||
let imageData = image.jpegData(compressionQuality = 1);
|
||||
let real = UTSiOS.try(imageData!.write(to = destinationURL), "?");
|
||||
if (real == null) return ""
|
||||
return destinationURL.path
|
||||
}
|
||||
function getScreenImage(callback : (str : string) => void) {
|
||||
if (UTSiOS.available("iOS 10.0, *")) {
|
||||
let parent = UTSiOS.getCurrentViewController() as UIViewController;
|
||||
let rootView = parent.view as UIView
|
||||
let renderer = new UIGraphicsImageRenderer(size = rootView.bounds.size);
|
||||
renderer.image(actions = (context : UIGraphicsImageRendererContext) => {
|
||||
rootView.drawHierarchy(in = rootView.bounds, afterScreenUpdates = true)
|
||||
let image = context.currentImage
|
||||
callback(savePhotoTocache(image))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 获取窗口截图
|
||||
*/
|
||||
export function getRootShotImage(callback : (str : string) => void) {
|
||||
getScreenImage((str : string) => {
|
||||
callback(str)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取指定元素截图
|
||||
*/
|
||||
export function getElementShotImage(ele:UniElement|null, callback : (str : string) => void) {
|
||||
if (ele == null) {
|
||||
callback("")
|
||||
return;
|
||||
}
|
||||
let view = ele!.getIOSView() as UIView | null;
|
||||
if (view == null) {
|
||||
callback("")
|
||||
return;
|
||||
}
|
||||
let rootView = view! as UIView;
|
||||
if (UTSiOS.available("iOS 10.0, *")) {
|
||||
let renderer = new UIGraphicsImageRenderer(size = rootView.bounds.size);
|
||||
renderer.image(actions = (context : UIGraphicsImageRendererContext) => {
|
||||
rootView.drawHierarchy(in = rootView.bounds, afterScreenUpdates = true)
|
||||
let image = context.currentImage
|
||||
callback(savePhotoTocache(image))
|
||||
// UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 获取窗口截图
|
||||
*/
|
||||
export function getRootShotImage(callback : (str : string) => void) {
|
||||
callback("")
|
||||
}
|
||||
/**
|
||||
* 获取指定元素截图
|
||||
*/
|
||||
export function getElementShotImage(ele:UniElement|null, callback : (str : string) => void) {
|
||||
callback("")
|
||||
}
|
||||
Reference in New Issue
Block a user