feat: 视频水印固定位置显示;允许截屏禁止录屏

This commit is contained in:
2026-06-24 14:56:10 +08:00
parent a0b4b1a53e
commit e8542f99dc
13 changed files with 580 additions and 47 deletions

View File

@@ -0,0 +1,29 @@
export type CaptureScreenSuccess = {
/**
* 截图结果,格式为 data URLdata:image/jpeg;base64,xxxx
*/
base64 : string
}
export type CaptureScreenSuccessCallback = (res : CaptureScreenSuccess) => void
export type CaptureScreenFail = {
errCode : number,
errMsg : string
}
export type CaptureScreenFailCallback = (res : CaptureScreenFail) => void
export type CaptureScreenCompleteCallback = (res : any) => void
export type CaptureScreenOptions = {
success ?: CaptureScreenSuccessCallback,
fail ?: CaptureScreenFailCallback,
complete ?: CaptureScreenCompleteCallback
}
/**
* 截取当前应用窗口画面(含视频等硬件加速渲染内容),通过 base64 返回。
* 仅 App-Android 生效,基于 Android PixelCopy 实现。
*/
export declare const captureScreen : (options : CaptureScreenOptions) => void

View File

@@ -0,0 +1,85 @@
{
"id": "yb-screen-capture",
"displayName": "yb-screen-capture",
"version": "1.0.0",
"description": "截取当前应用窗口(含硬件加速渲染的视频画面),返回 base64",
"keywords": [
"截图",
"截屏",
"PixelCopy"
],
"repository": "",
"engines": {
"HBuilderX": "^3.7.7"
},
"dcloudext": {
"type": "uts",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-android": "y",
"app-ios": "n",
"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"
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
# yb-screen-capture
截取当前应用窗口画面(含视频等硬件加速渲染内容),通过 base64 返回。
-**App-Android** 生效,基于 Android `PixelCopy` 实现Android 8.0 及以上能正确截取视频画面;低于 8.0 降级为 View 截图,视频区域可能为黑屏)。
- 截图范围为「当前屏幕可见区域」,不是长页面截图。
## 使用
```js
uni.captureScreen({
success: (res) => {
// res.base64 形如data:image/jpeg;base64,xxxx
console.log(res.base64)
},
fail: (err) => {
console.error(err.errCode, err.errMsg)
}
})
```
> 注意UTS 插件需要打自定义基座(标准基座无法运行)才能调试/运行。

View File

@@ -0,0 +1,127 @@
import { UTSAndroid } from "io.dcloud.uts";
import PixelCopy from "android.view.PixelCopy";
import OnPixelCopyFinishedListener from "android.view.PixelCopy.OnPixelCopyFinishedListener";
import Bitmap from "android.graphics.Bitmap";
import Canvas from "android.graphics.Canvas";
import Handler from "android.os.Handler";
import Looper from "android.os.Looper";
import Build from "android.os.Build";
import ByteArrayOutputStream from "java.io.ByteArrayOutputStream";
import Base64 from "android.util.Base64";
import {
CaptureScreen,
CaptureScreenOptions,
CaptureScreenSuccess,
CaptureScreenFail
} from "../interface.uts";
/**
* 将 Bitmap 压缩为 JPEG 并编码为 data URL 形式的 base64
*/
function encodeBitmapToBase64(bitmap : Bitmap) : string {
const baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
const bytes = baos.toByteArray();
const base64 = Base64.encodeToString(bytes, Base64.NO_WRAP);
bitmap.recycle();
return "data:image/jpeg;base64," + base64;
}
/**
* PixelCopy 完成监听器
*/
class PixelCopyListener extends OnPixelCopyFinishedListener {
private bitmap : Bitmap;
private options : CaptureScreenOptions;
constructor(bitmap : Bitmap, options : CaptureScreenOptions) {
super();
this.bitmap = bitmap;
this.options = options;
}
override onPixelCopyFinished(copyResult : Int) : void {
if (copyResult == PixelCopy.SUCCESS) {
const base64 = encodeBitmapToBase64(this.bitmap);
const res : CaptureScreenSuccess = { base64: base64 };
this.options.success?.(res);
this.options.complete?.(res);
} else {
this.bitmap.recycle();
const res : CaptureScreenFail = {
errCode: 12010,
errMsg: "captureScreen:fail PixelCopy copyResult=" + copyResult
};
this.options.fail?.(res);
this.options.complete?.(res);
}
}
}
/**
* 在 UI 线程执行实际截图
*/
class CaptureRunnable extends Runnable {
private options : CaptureScreenOptions;
constructor(options : CaptureScreenOptions) {
super();
this.options = options;
}
override run() : void {
try {
const activity = UTSAndroid.getUniActivity();
if (activity == null) {
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail activity is null" };
this.options.fail?.(res);
this.options.complete?.(res);
return;
}
// 注意UTS 中 getWindow() 返回可空类型,必须用 ! 断言,否则编译失败
const window = activity!.getWindow()!;
const decorView = window.getDecorView()!;
const width = decorView.getWidth();
const height = decorView.getHeight();
if (width <= 0 || height <= 0) {
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail invalid window size" };
this.options.fail?.(res);
this.options.complete?.(res);
return;
}
const bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Android 8.0+:使用 PixelCopy 可以正确截取 SurfaceView/视频等硬件加速画面
const handler = new Handler(Looper.getMainLooper());
PixelCopy.request(window, bitmap, new PixelCopyListener(bitmap, this.options), handler);
} else {
// 低版本降级:仅能截取 View 层级内容,视频区域可能为黑屏
const canvas = new Canvas(bitmap);
decorView.draw(canvas);
const base64 = encodeBitmapToBase64(bitmap);
const res : CaptureScreenSuccess = { base64: base64 };
this.options.success?.(res);
this.options.complete?.(res);
}
} catch (e) {
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail " + e.toString() };
this.options.fail?.(res);
this.options.complete?.(res);
}
}
}
export const captureScreen : CaptureScreen = function (options : CaptureScreenOptions) {
const activity = UTSAndroid.getUniActivity();
if (activity == null) {
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail activity is null" };
options.fail?.(res);
options.complete?.(res);
return;
}
activity!.runOnUiThread(new CaptureRunnable(options));
}

View File

@@ -0,0 +1,73 @@
/**
* uni.captureScreen 成功回调参数
*/
export type CaptureScreenSuccess = {
/**
* 截图结果,格式为 data URLdata:image/jpeg;base64,xxxx
*/
base64 : string
}
/**
* uni.captureScreen 成功回调函数定义
*/
export type CaptureScreenSuccessCallback = (res : CaptureScreenSuccess) => void
/**
* uni.captureScreen 失败回调参数
*/
export type CaptureScreenFail = {
errCode : number,
errMsg : string
}
/**
* uni.captureScreen 失败回调函数定义
*/
export type CaptureScreenFailCallback = (res : CaptureScreenFail) => void
/**
* uni.captureScreen 完成回调函数定义(成功、失败都会执行)
*/
export type CaptureScreenCompleteCallback = (res : any) => void
/**
* uni.captureScreen 参数
*/
export type CaptureScreenOptions = {
/**
* 接口调用成功的回调函数
*/
success ?: CaptureScreenSuccessCallback,
/**
* 接口调用失败的回调函数
*/
fail ?: CaptureScreenFailCallback,
/**
* 接口调用结束的回调函数(调用成功、失败都会执行)
*/
complete ?: CaptureScreenCompleteCallback
}
export type CaptureScreen = (options : CaptureScreenOptions) => void
export interface Uni {
/**
* 截取当前应用窗口画面(含视频等硬件加速渲染内容),通过 base64 返回。
* 仅 App-Android 生效,基于 Android PixelCopy 实现。
*
* @param {CaptureScreenOptions} options
* @uniPlatform {
* "app": {
* "android": {
* "osVer": "8.0",
* "uniVer": "3.7.7",
* "unixVer": "3.9.0"
* }
* }
* }
* @uniVersion 3.7.7
* @uniVueVersion 2,3
*/
captureScreen(options : CaptureScreenOptions) : void
}