This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
## 1.0.12025-07-27
* 兼容鸿蒙
## 1.0.02024-12-18
* 对屏幕截图保存(不需要权限),对指定截图进行保存,安卓,ios支持,web不支持.
+87
View File
@@ -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"
}
}
}
}
}
+101
View File
@@ -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
}
@@ -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("")
}