diff --git a/entry/src/main/ets/pages/BridgeGameContainer.ets b/entry/src/main/ets/pages/BridgeGameContainer.ets index 53d4049..7cddd5d 100644 --- a/entry/src/main/ets/pages/BridgeGameContainer.ets +++ b/entry/src/main/ets/pages/BridgeGameContainer.ets @@ -347,6 +347,7 @@ export struct BridgeGameContainer { Stack() { // 大厅 Web(常驻) Web({ src: this.lobbySrc(), controller: this.controllerL }) + .id('web_lobby') .javaScriptAccess(true).domStorageAccess(true).fileAccess(true) .mixedMode(MixedMode.All).cacheMode(CacheMode.None) .geolocationAccess(true).zoomAccess(false) @@ -372,6 +373,7 @@ export struct BridgeGameContainer { // 子游戏 Web(临时,subgameUrl 非空时存在) if (this.subgameUrl !== '' && this.slotS !== undefined) { Web({ src: this.subgameUrl, controller: this.slotS.controller }) + .id('web_subgame') .javaScriptAccess(true).domStorageAccess(true).fileAccess(true) .mixedMode(MixedMode.All).cacheMode(CacheMode.None) .geolocationAccess(true).zoomAccess(false) diff --git a/entry/src/main/ets/web/WebSlot.ets b/entry/src/main/ets/web/WebSlot.ets index 15256ce..ccbe0e5 100644 --- a/entry/src/main/ets/web/WebSlot.ets +++ b/entry/src/main/ets/web/WebSlot.ets @@ -1,4 +1,7 @@ import { webview } from '@kit.ArkWeb'; +import { componentSnapshot } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; +import { util } from '@kit.ArkTS'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { BridgeController, WebviewControllerAdapter, BridgeJsLoader } from 'feature_bridge'; @@ -225,46 +228,33 @@ export class WebSlot { } /** - * 截取本槽 Web 内的 canvas(截图分享)。脚本在 H5 内 toDataURL,返回 dataURL; - * runJavaScript 回调的 result 是 JSON 字符串(外层带引号),需 JSON.parse 去引号还原。 - * 非激活/出错一律返回空串(由 ShareProvider 降级网页分享,绝不抛错)。 + * 截取本槽 Web 组件已渲染像素(截图分享)。改用 componentSnapshot 抓组件合成结果, + * 规避 WebGL canvas.toDataURL 黑屏问题;打包为 image/jpeg 的 dataURL 返回。 + * 非激活/出错一律返回空串(由 ShareProvider 降级,绝不抛错)。canvasId 参数保留兼容、当前忽略。 */ private captureCanvas(canvasId: string): Promise { if (!this.active || this.disposed) { return Promise.resolve(''); } - const sel: string = canvasId !== '' - ? `document.getElementById(${JSON.stringify(canvasId)}).toDataURL('image/png')` - : `document.querySelector('canvas').toDataURL('image/jpeg',0.8)`; - const script: string = `(function(){try{return ${sel};}catch(e){return '';}})()`; - return new Promise((resolve: (v: string) => void) => { - try { - this.controller.runJavaScript(script, (err: BusinessError, result: string) => { - if (err) { - WebSlot.log.w(`[${this.role}] captureCanvas runJavaScript error: ${err.message}`); - resolve(''); - return; - } - resolve(WebSlot.parseJsString(result)); - }); - } catch (e) { - WebSlot.log.w(`[${this.role}] captureCanvas failed: ${(e as BusinessError).message}`); - resolve(''); - } - }); - } - - /** runJavaScript 返回值是 JSON 字符串(字符串结果外层带引号);去引号还原,异常返回空串。 */ - private static parseJsString(result: string): string { - if (result === undefined || result === null || result === '' || result === 'null') { - return ''; - } - try { - const parsed: Object = JSON.parse(result) as Object; - return typeof parsed === 'string' ? parsed as string : ''; - } catch (e) { - return ''; - } + const compId: string = `web_${this.role}`; + return componentSnapshot.get(compId, { waitUntilRenderFinished: true }) + .then((pm: image.PixelMap): Promise => { + const packer: image.ImagePacker = image.createImagePacker(); + return packer.packToData(pm, { format: 'image/jpeg', quality: 90 }) + .then((buf: ArrayBuffer): string => { + const b64: string = new util.Base64Helper().encodeToStringSync(new Uint8Array(buf)); + WebSlot.log.i(`[${this.role}] snapshot ok bytes=${buf.byteLength}`); + return `data:image/jpeg;base64,${b64}`; + }) + .finally(() => { + pm.release(); + packer.release(); + }); + }) + .catch((e: Object): string => { + WebSlot.log.w(`[${this.role}] componentSnapshot failed: ${(e as Error).message}`); + return ''; + }); } private flushPending(): void { diff --git a/feature_capabilities/src/main/ets/providers/ShareProvider.ets b/feature_capabilities/src/main/ets/providers/ShareProvider.ets index 6bdc4bb..68161f7 100644 --- a/feature_capabilities/src/main/ets/providers/ShareProvider.ets +++ b/feature_capabilities/src/main/ets/providers/ShareProvider.ets @@ -15,7 +15,7 @@ */ import { systemShare } from '@kit.ShareKit'; import { uniformTypeDescriptor as utd } from '@kit.ArkData'; -import { fileUri } from '@kit.CoreFileKit'; +import { fileUri, fileIo } from '@kit.CoreFileKit'; import { common, bundleManager } from '@kit.AbilityKit'; import { BusinessError, pasteboard } from '@kit.BasicServicesKit'; import { image } from '@kit.ImageKit'; @@ -316,7 +316,14 @@ export class ShareProvider implements CapabilityProvider { /** 图片写剪贴板(尽力;对方能否粘贴 PIXELMAP 不可保证,systemImage 才是可靠交付)。 */ private writeClipboardImage(filePath: string): void { - const source: image.ImageSource = image.createImageSource(filePath); + let file: fileIo.File; + try { + file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY); + } catch (e) { + this.log.w(`clipboard image open failed: ${(e as Error).message}`); + return; + } + const source: image.ImageSource = image.createImageSource(file.fd); source.createPixelMap().then((pm: image.PixelMap) => { let data: pasteboard.PasteData; try { @@ -326,14 +333,15 @@ export class ShareProvider implements CapabilityProvider { pm.release(); return; } - // pm 须存活到 setData 完成(createData 可能持引用),故在 setData settle 后再 release。 pasteboard.getSystemPasteboard().setData(data) + .then(() => this.log.i('clipboard image set ok')) .catch((e: BusinessError) => this.log.w(`clipboard image failed: ${e.code} ${e.message}`)) .finally(() => pm.release()); }).catch((e: BusinessError) => { this.log.w(`createPixelMap failed: ${e.code} ${e.message}`); }).finally(() => { source.release(); + fileIo.closeSync(file); }); }