@open-wxa/wxa-runner-core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +28 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -10
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/frame.js +49 -1
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/host.js +153 -36
- package/dist/runtime/host.js.map +1 -1
- package/package.json +3 -3
package/dist/runtime/host.js
CHANGED
|
@@ -433,8 +433,11 @@
|
|
|
433
433
|
var SOURCES = ["camera", "album"];
|
|
434
434
|
var SOURCE_LABELS = { camera: "\u62CD\u6444", album: "\u4ECE\u624B\u673A\u76F8\u518C\u9009\u62E9" };
|
|
435
435
|
var DEFAULT_COUNT = 9;
|
|
436
|
+
var MEDIA_KINDS = ["image", "video"];
|
|
437
|
+
var FIRST_FRAME_TIMEOUT = 1e3;
|
|
438
|
+
var countOf = (args) => Math.max(1, Number(args.count) || DEFAULT_COUNT);
|
|
436
439
|
function createMediaApis({ showActionSheet, chooseFiles: chooseFiles2, hasUserActivation: hasUserActivation2, showImagePreview }) {
|
|
437
|
-
async function
|
|
440
|
+
async function pickFiles(args, accept) {
|
|
438
441
|
const allowed = SOURCES.filter((source2) => args.sourceType?.includes(source2));
|
|
439
442
|
const sources = allowed.length ? allowed : SOURCES;
|
|
440
443
|
let source = sources[0];
|
|
@@ -443,28 +446,33 @@
|
|
|
443
446
|
if (index === null) return null;
|
|
444
447
|
source = sources[index];
|
|
445
448
|
}
|
|
446
|
-
|
|
447
|
-
const files = await chooseFiles2({ accept: "image/*", multiple: count > 1, capture: source === "camera" });
|
|
448
|
-
return files && files.slice(0, count);
|
|
449
|
+
return chooseFiles2({ accept, multiple: countOf(args) > 1, capture: source === "camera" });
|
|
449
450
|
}
|
|
450
|
-
const
|
|
451
|
+
const saveImage = (file) => saveTempFile(file, tempFileType(file.name, file.type, "jpg"));
|
|
451
452
|
return {
|
|
452
453
|
chooseImage: async (args, { api }) => {
|
|
453
|
-
const files = await
|
|
454
|
+
const files = (await pickFiles(args, "image/*"))?.slice(0, countOf(args));
|
|
454
455
|
if (!files) return fail(api, "cancel");
|
|
455
|
-
const paths = await
|
|
456
|
+
const paths = await Promise.all(files.map(saveImage));
|
|
456
457
|
return ok(api, { tempFilePaths: paths, tempFiles: files.map((file, i) => ({ path: paths[i], size: file.size })) });
|
|
457
458
|
},
|
|
458
459
|
chooseMedia: async (args, { api }) => {
|
|
459
|
-
const mediaType = args.mediaType?.length ? args.mediaType :
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
return
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
460
|
+
const mediaType = args.mediaType?.length ? args.mediaType : MEDIA_KINDS;
|
|
461
|
+
const mix = mediaType.includes("mix");
|
|
462
|
+
const requested = MEDIA_KINDS.filter((kind) => mix || mediaType.includes(kind));
|
|
463
|
+
const kinds = requested.length ? requested : MEDIA_KINDS;
|
|
464
|
+
const picked = await pickFiles(args, kinds.map((kind) => `${kind}/*`).join(","));
|
|
465
|
+
if (!picked) return fail(api, "cancel");
|
|
466
|
+
const kindOf = (file) => kinds.length === 1 ? kinds[0] : file.type.startsWith("video/") ? "video" : "image";
|
|
467
|
+
const videos = picked.filter((file) => kindOf(file) === "video");
|
|
468
|
+
const files = (mix || !videos.length ? picked : videos).slice(0, countOf(args));
|
|
469
|
+
const tempFiles = await Promise.all(files.map(async (file) => kindOf(file) === "video" ? { ...await saveVideo(file), fileType: "video" } : { tempFilePath: await saveImage(file), size: file.size, fileType: "image" }));
|
|
470
|
+
return ok(api, { type: mix ? "mix" : tempFiles[0].fileType, tempFiles });
|
|
471
|
+
},
|
|
472
|
+
chooseVideo: async (args, { api }) => {
|
|
473
|
+
const file = (await pickFiles({ sourceType: args.sourceType, count: 1 }, "video/*"))?.[0];
|
|
474
|
+
if (!file) return fail(api, "cancel");
|
|
475
|
+
return ok(api, await saveVideo(file));
|
|
468
476
|
},
|
|
469
477
|
// 基础库已经把 current 换成了 urls 里的地址;也兼容直接传下标
|
|
470
478
|
previewImage: (args) => {
|
|
@@ -493,6 +501,45 @@
|
|
|
493
501
|
}
|
|
494
502
|
};
|
|
495
503
|
}
|
|
504
|
+
async function saveVideo(file) {
|
|
505
|
+
const [tempFilePath, { duration, width, height, thumb }] = await Promise.all([
|
|
506
|
+
saveTempFile(file, tempFileType(file.name, file.type, "mp4")),
|
|
507
|
+
readVideo(file)
|
|
508
|
+
]);
|
|
509
|
+
const thumbTempFilePath = thumb ? await saveTempFile(thumb, "jpg") : "";
|
|
510
|
+
return { tempFilePath, size: file.size, duration, height, width, thumbTempFilePath };
|
|
511
|
+
}
|
|
512
|
+
async function readVideo(file) {
|
|
513
|
+
const video = document.createElement("video");
|
|
514
|
+
video.muted = true;
|
|
515
|
+
video.preload = "auto";
|
|
516
|
+
video.src = URL.createObjectURL(file);
|
|
517
|
+
try {
|
|
518
|
+
await new Promise((resolve, reject) => {
|
|
519
|
+
video.addEventListener("loadedmetadata", resolve, { once: true });
|
|
520
|
+
video.addEventListener("error", reject, { once: true });
|
|
521
|
+
});
|
|
522
|
+
const { videoWidth: width, videoHeight: height } = video;
|
|
523
|
+
const duration = Number.isFinite(video.duration) ? video.duration : 0;
|
|
524
|
+
return { duration, width, height, thumb: width && height ? await firstFrame(video) : null };
|
|
525
|
+
} catch {
|
|
526
|
+
return { duration: 0, width: 0, height: 0, thumb: null };
|
|
527
|
+
} finally {
|
|
528
|
+
URL.revokeObjectURL(video.src);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async function firstFrame(video) {
|
|
532
|
+
const loaded = video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA || await Promise.race([
|
|
533
|
+
new Promise((resolve) => video.addEventListener("loadeddata", () => resolve(true), { once: true })),
|
|
534
|
+
new Promise((resolve) => setTimeout(() => resolve(false), FIRST_FRAME_TIMEOUT))
|
|
535
|
+
]);
|
|
536
|
+
if (!loaded) return null;
|
|
537
|
+
const canvas = document.createElement("canvas");
|
|
538
|
+
canvas.width = video.videoWidth;
|
|
539
|
+
canvas.height = video.videoHeight;
|
|
540
|
+
canvas.getContext("2d").drawImage(video, 0, 0);
|
|
541
|
+
return new Promise((resolve) => canvas.toBlob(resolve, "image/jpeg"));
|
|
542
|
+
}
|
|
496
543
|
async function fetchFile(path) {
|
|
497
544
|
const url = /^[a-z][a-z\d+.-]*:/i.test(path) ? path : "/" + path.replace(/^\//, "");
|
|
498
545
|
const res = await fetch(url);
|
|
@@ -798,11 +845,10 @@
|
|
|
798
845
|
});
|
|
799
846
|
return regionTree;
|
|
800
847
|
};
|
|
801
|
-
const showCascade = (model
|
|
848
|
+
const showCascade = (model) => {
|
|
802
849
|
const session = showPicker({
|
|
803
850
|
columns: model.columns(),
|
|
804
851
|
current: model.current(),
|
|
805
|
-
headerText,
|
|
806
852
|
onColumnChange(column, index) {
|
|
807
853
|
model.select(column, index);
|
|
808
854
|
const columns = model.columns();
|
|
@@ -816,8 +862,7 @@
|
|
|
816
862
|
showPickerView: async (args, { api }) => {
|
|
817
863
|
const result = await showPicker({
|
|
818
864
|
columns: [labels(args.array)],
|
|
819
|
-
current: [Number(args.current) || 0]
|
|
820
|
-
headerText: args.headerText
|
|
865
|
+
current: [Number(args.current) || 0]
|
|
821
866
|
}).result;
|
|
822
867
|
return result ? ok(api, { index: result[0] }) : fail(api, "cancel");
|
|
823
868
|
},
|
|
@@ -825,7 +870,6 @@
|
|
|
825
870
|
const session = showPicker({
|
|
826
871
|
columns: (args.array || []).map(labels),
|
|
827
872
|
current: (args.current || []).map((index) => Number(index) || 0),
|
|
828
|
-
headerText: args.headerText,
|
|
829
873
|
onColumnChange: (column, current) => emitToWebview(webviewId, "onMultiPickerViewChange", { column, current })
|
|
830
874
|
});
|
|
831
875
|
multiPicker = session;
|
|
@@ -840,7 +884,7 @@
|
|
|
840
884
|
},
|
|
841
885
|
showDatePickerView: async (args, { api }) => {
|
|
842
886
|
const model = new DateCascade(args.mode === "time" ? "time" : "date", args.fields, args.range || {}, String(args.current || ""));
|
|
843
|
-
const result = await showCascade(model
|
|
887
|
+
const result = await showCascade(model);
|
|
844
888
|
return result ? ok(api, { value: model.value() }) : fail(api, "cancel");
|
|
845
889
|
},
|
|
846
890
|
showAddressPickerView: async (args, { api }) => {
|
|
@@ -920,7 +964,7 @@
|
|
|
920
964
|
}
|
|
921
965
|
label(column, value) {
|
|
922
966
|
if (this.mode === "time") return pad(value);
|
|
923
|
-
return value +
|
|
967
|
+
return column === 0 ? value + "\u5E74" : pad(value) + ["\u6708", "\u65E5"][column - 1];
|
|
924
968
|
}
|
|
925
969
|
};
|
|
926
970
|
function parseParts(text) {
|
|
@@ -1578,6 +1622,18 @@
|
|
|
1578
1622
|
if (this.bridge && this.win) fn(this.bridge, this.win);
|
|
1579
1623
|
else this.pending.push(fn);
|
|
1580
1624
|
}
|
|
1625
|
+
/**
|
|
1626
|
+
* 把逻辑层的画布放进这一页编号为 canvasNumber 的 wx-canvas,盖在它自己的 canvas 上(样式见 webviewHtml 的 .wxa-service-canvas)。
|
|
1627
|
+
* 编号在 wx-canvas 的组件实例上,DOM 节点经 __wxElement 找到组件实例
|
|
1628
|
+
*/
|
|
1629
|
+
placeCanvas(canvas, canvasNumber) {
|
|
1630
|
+
const target = Array.from(this.win?.document.querySelectorAll("wx-canvas") ?? []).find((element2) => element2.__wxElement?._canvasNumber === canvasNumber);
|
|
1631
|
+
const own = target?.querySelector(":scope > canvas");
|
|
1632
|
+
if (!own) return false;
|
|
1633
|
+
canvas.classList.add("wxa-service-canvas");
|
|
1634
|
+
own.after(canvas);
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1581
1637
|
/** 页面样式里的 env(safe-area-inset-*) 构建时已改写成这些变量,见 rewriteSafeAreaInsets */
|
|
1582
1638
|
setSafeAreaInsets(insets) {
|
|
1583
1639
|
this.dispatch((_bridge, win) => {
|
|
@@ -1694,7 +1750,7 @@
|
|
|
1694
1750
|
var DEVICE_BEZEL = 13;
|
|
1695
1751
|
var STAGE_PADDING = 24;
|
|
1696
1752
|
var STAGE_GAP = 16;
|
|
1697
|
-
var PICKER_ITEM_HEIGHT =
|
|
1753
|
+
var PICKER_ITEM_HEIGHT = 48;
|
|
1698
1754
|
var SHELL_CSS = `
|
|
1699
1755
|
html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50% 0%, #f6f7f9 0%, #e6e9ee 100%); font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; }
|
|
1700
1756
|
.wxa-stage { min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: ${STAGE_GAP}px; padding: ${STAGE_PADDING}px; box-sizing: border-box; }
|
|
@@ -1748,18 +1804,23 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
1748
1804
|
.wxa-actionsheet-alert { padding: 16px; font-size: 13px; color: #888; text-align: center; background: #fff; }
|
|
1749
1805
|
.wxa-actionsheet button { display: block; width: 100%; height: 56px; border: 0; border-top: 1px solid rgba(0,0,0,.06); background: #fff; font-size: 17px; cursor: pointer; }
|
|
1750
1806
|
.wxa-actionsheet .wxa-actionsheet-cancel { margin-top: 8px; border-top: 0; }
|
|
1751
|
-
.wxa-picker {
|
|
1752
|
-
.wxa-picker
|
|
1753
|
-
.wxa-picker-header
|
|
1754
|
-
.wxa-picker-
|
|
1755
|
-
.wxa-picker-
|
|
1756
|
-
.wxa-picker-
|
|
1757
|
-
.wxa-picker-
|
|
1807
|
+
.wxa-picker-layer .wxa-mask.is-dim { background: rgba(0,0,0,.6); animation: wxa-fade-in .3s ease forwards; }
|
|
1808
|
+
.wxa-picker { position: absolute; left: 0; right: 0; bottom: 0; padding-bottom: var(--wxa-safe-bottom, 0px); background: #fff; pointer-events: auto; user-select: none; animation: wxa-slide-up .3s ease forwards; }
|
|
1809
|
+
.wxa-picker-header { position: relative; display: flex; padding: 16px; font-size: 17px; line-height: 1.4; }
|
|
1810
|
+
.wxa-picker-header::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1px solid rgba(0,0,0,.1); transform: scaleY(.5); transform-origin: 0 100%; }
|
|
1811
|
+
.wxa-picker-header button { flex: 1; padding: 0; border: 0; background: none; font: inherit; cursor: pointer; }
|
|
1812
|
+
.wxa-picker-cancel { color: rgba(0,0,0,.5); text-align: left; }
|
|
1813
|
+
.wxa-picker-confirm { color: #07c160; text-align: right; }
|
|
1814
|
+
.wxa-picker-body { position: relative; display: flex; height: ${PICKER_ITEM_HEIGHT * 5}px; overflow: hidden; }
|
|
1758
1815
|
.wxa-picker-column { flex: 1; min-width: 0; overflow-y: auto; padding: ${PICKER_ITEM_HEIGHT * 2}px 0; box-sizing: border-box; scroll-snap-type: y mandatory; overscroll-behavior: contain; scrollbar-width: none; }
|
|
1759
1816
|
.wxa-picker-column::-webkit-scrollbar { display: none; }
|
|
1760
|
-
|
|
1761
|
-
.wxa-picker-item
|
|
1762
|
-
.wxa-picker-
|
|
1817
|
+
/* \u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u9009\u9879\u6CA1\u6709\u5355\u72EC\u8BBE\u7F6E\u5B57\u53F7\uFF0C\u7EE7\u627F\u5230\u7684\u662F 11px\uFF08\u6309\u5B83\u7684\u622A\u56FE\u91CF\u5F97\uFF09 */
|
|
1818
|
+
.wxa-picker-item { height: ${PICKER_ITEM_HEIGHT}px; line-height: ${PICKER_ITEM_HEIGHT}px; overflow: hidden; font-size: 11px; color: rgba(0,0,0,.9); text-align: center; white-space: nowrap; text-overflow: ellipsis; scroll-snap-align: center; cursor: pointer; }
|
|
1819
|
+
.wxa-picker-mask { position: absolute; inset: 0; z-index: 1; pointer-events: none; background-image: linear-gradient(180deg, rgba(255,255,255,.95), rgba(255,255,255,.6)), linear-gradient(0deg, rgba(255,255,255,.95), rgba(255,255,255,.6)); background-position: top, bottom; background-size: 100% 92px; background-repeat: no-repeat; }
|
|
1820
|
+
.wxa-picker-indicator { position: absolute; left: 0; right: 0; top: 92px; z-index: 1; height: 56px; pointer-events: none; }
|
|
1821
|
+
.wxa-picker-indicator::before, .wxa-picker-indicator::after { content: ''; position: absolute; left: 0; right: 0; height: 1px; transform: scaleY(.5); }
|
|
1822
|
+
.wxa-picker-indicator::before { top: 0; border-top: 1px solid rgba(0,0,0,.1); transform-origin: 0 0; }
|
|
1823
|
+
.wxa-picker-indicator::after { bottom: 0; border-bottom: 1px solid rgba(0,0,0,.1); transform-origin: 0 100%; }
|
|
1763
1824
|
.wxa-preview { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: #000; pointer-events: auto; }
|
|
1764
1825
|
.wxa-preview-image { max-width: 100%; max-height: 100%; object-fit: contain; }
|
|
1765
1826
|
.wxa-preview-position { position: absolute; top: 60px; left: 0; right: 0; color: #fff; font-size: 15px; text-align: center; pointer-events: none; }
|
|
@@ -1778,6 +1839,8 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
1778
1839
|
.wxa-notice-close { flex: none; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%; background: none; color: #999; font-size: 16px; line-height: 20px; cursor: pointer; }
|
|
1779
1840
|
.wxa-notice-close:hover { background: rgba(0,0,0,.06); color: #111214; }
|
|
1780
1841
|
@keyframes wxa-spin { to { transform: rotate(360deg); } }
|
|
1842
|
+
@keyframes wxa-fade-in { from { opacity: 0; } }
|
|
1843
|
+
@keyframes wxa-slide-up { from { transform: translateY(100%); } }
|
|
1781
1844
|
@keyframes wxa-notice-in { from { opacity: 0; transform: translateY(-8px); } }
|
|
1782
1845
|
`;
|
|
1783
1846
|
|
|
@@ -1795,10 +1858,10 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
1795
1858
|
const header = element("div", "wxa-picker-header");
|
|
1796
1859
|
const cancel = element("button", "wxa-picker-cancel", "\u53D6\u6D88");
|
|
1797
1860
|
const confirm = element("button", "wxa-picker-confirm", "\u786E\u5B9A");
|
|
1798
|
-
header.append(cancel,
|
|
1861
|
+
header.append(cancel, confirm);
|
|
1799
1862
|
const body = element("div", "wxa-picker-body");
|
|
1800
1863
|
const columns = options.columns.map((_, index) => new PickerColumn((next) => options.onColumnChange?.(index, next)));
|
|
1801
|
-
body.append(...columns.map((column) => column.el), element("div", "wxa-picker-indicator"));
|
|
1864
|
+
body.append(...columns.map((column) => column.el), element("div", "wxa-picker-mask"), element("div", "wxa-picker-indicator"));
|
|
1802
1865
|
sheet.append(header, body);
|
|
1803
1866
|
layer.append(mask, sheet);
|
|
1804
1867
|
container.appendChild(layer);
|
|
@@ -2200,6 +2263,8 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2200
2263
|
// src/runtime/host/Host.ts
|
|
2201
2264
|
var ROUTE_DONE_DELAY = 300;
|
|
2202
2265
|
var PAGE_LOAD_TIMEOUT = 15e3;
|
|
2266
|
+
var BINDING_CANVAS_TYPES = /* @__PURE__ */ new Set(["2d", "webgl", "webgl2"]);
|
|
2267
|
+
var PROBE_TOP = -54321;
|
|
2203
2268
|
var Host = class {
|
|
2204
2269
|
constructor(info, root) {
|
|
2205
2270
|
this.info = info;
|
|
@@ -2222,6 +2287,8 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2222
2287
|
this.nextWebviewId = 1;
|
|
2223
2288
|
this.routeChain = Promise.resolve();
|
|
2224
2289
|
this.routeListeners = [];
|
|
2290
|
+
/** 渲染层报来的 2d、webgl 画布,按编号记下所在页面和最近的位置,供 placeCanvas 认出逻辑层的画布元素 */
|
|
2291
|
+
this.canvases = /* @__PURE__ */ new Map();
|
|
2225
2292
|
this.device = info.device;
|
|
2226
2293
|
this.shell = new PhoneShell(root, info.device);
|
|
2227
2294
|
this.tabPaths = new Set((info.wxConfig.tabBar?.list || []).map((item) => stripHtml(item.pagePath)));
|
|
@@ -2260,6 +2327,30 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2260
2327
|
registerAppContext(win) {
|
|
2261
2328
|
this.appContext = win;
|
|
2262
2329
|
}
|
|
2330
|
+
/**
|
|
2331
|
+
* 逻辑层的画布元素上没有编号,只能问基础库:给每个还没放好的同类型画布发一条 top 带记号的 canvasUpdated,
|
|
2332
|
+
* 基础库把位置写到那个编号的元素上,记号落在这个元素上就是它;随后用原来的位置再发一次,恢复原样。
|
|
2333
|
+
* 基础库只在类型与画布一致时才调到元素的 getContext,按类型筛选不会漏掉
|
|
2334
|
+
*/
|
|
2335
|
+
placeCanvas(canvas, type) {
|
|
2336
|
+
const service = this.service;
|
|
2337
|
+
if (!service) return;
|
|
2338
|
+
const update = (canvasNumber, record, top) => service.bridge.__dispatchSub(
|
|
2339
|
+
"canvasUpdated",
|
|
2340
|
+
this.cloneForService({ data: { canvasNumber, ...record.box, top }, options: { timestamp: Date.now() } }),
|
|
2341
|
+
record.webviewId
|
|
2342
|
+
);
|
|
2343
|
+
for (const [canvasNumber, record] of this.canvases) {
|
|
2344
|
+
if (record.placed || record.type !== type) continue;
|
|
2345
|
+
update(canvasNumber, record, PROBE_TOP);
|
|
2346
|
+
const matched = canvas.style.top === `${PROBE_TOP}px`;
|
|
2347
|
+
update(canvasNumber, record, record.box.top);
|
|
2348
|
+
if (matched) {
|
|
2349
|
+
record.placed = this.pages.get(record.webviewId)?.placeCanvas(canvas, canvasNumber) ?? false;
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2263
2354
|
frameLoaded(kind, webviewId) {
|
|
2264
2355
|
if (kind === "service") this.serviceLoaded();
|
|
2265
2356
|
else this.pages.get(webviewId)?.loaded();
|
|
@@ -2283,11 +2374,27 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2283
2374
|
}
|
|
2284
2375
|
if (event === "operateMapCallback" && this.mapOperations.receive(data)) return;
|
|
2285
2376
|
const fromId = this.webviewIdOf(win);
|
|
2377
|
+
this.trackCanvas(event, data?.data, fromId);
|
|
2286
2378
|
const service = this.service;
|
|
2287
2379
|
if (!service) return;
|
|
2288
2380
|
const payload = this.cloneForService(data);
|
|
2289
2381
|
queueMicrotask(() => service.bridge.__dispatchSub(event, payload, fromId));
|
|
2290
2382
|
}
|
|
2383
|
+
/**
|
|
2384
|
+
* xr-frame 的画布不记:基础库收到它的 canvasUpdated 会派发 resize 给 xr-frame,探测会带来多余的 resize,
|
|
2385
|
+
* 而 xr-frame 在 wxa-runner 里没有验证过,保持原样
|
|
2386
|
+
*/
|
|
2387
|
+
trackCanvas(event, data, webviewId) {
|
|
2388
|
+
if (!data) return;
|
|
2389
|
+
const record = this.canvases.get(data.canvasNumber);
|
|
2390
|
+
if (event === "canvasInserted" && data.type && BINDING_CANVAS_TYPES.has(data.type) && !data.isXrFrame) {
|
|
2391
|
+
this.canvases.set(data.canvasNumber, { webviewId, type: data.type, box: { top: 0, left: 0, width: 0, height: 0, ...boxOf(data) }, placed: false });
|
|
2392
|
+
} else if (event === "canvasUpdated" && record) {
|
|
2393
|
+
record.box = { ...record.box, ...boxOf(data) };
|
|
2394
|
+
} else if (event === "canvasRemoved") {
|
|
2395
|
+
this.canvases.delete(data.canvasNumber);
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2291
2398
|
/** 不指定目标时发给所有页面:旧版 canvas 的绘制指令就是这样发的,由 canvasNumber 找到对应画布 */
|
|
2292
2399
|
publishToPages(event, data, webviewIds) {
|
|
2293
2400
|
const targets = webviewIds?.length ? webviewIds.map((id) => this.pages.get(Number(id))) : this.livePages();
|
|
@@ -2445,6 +2552,9 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2445
2552
|
if (this.tabPages.get(page.path) === page) this.tabPages.delete(page.path);
|
|
2446
2553
|
this.pages.delete(page.id);
|
|
2447
2554
|
page.destroy();
|
|
2555
|
+
for (const [canvasNumber, record] of this.canvases) {
|
|
2556
|
+
if (record.webviewId === page.id) this.canvases.delete(canvasNumber);
|
|
2557
|
+
}
|
|
2448
2558
|
}
|
|
2449
2559
|
activate(page, openType) {
|
|
2450
2560
|
for (const other of this.pages.values()) {
|
|
@@ -2593,6 +2703,13 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2593
2703
|
}
|
|
2594
2704
|
};
|
|
2595
2705
|
}
|
|
2706
|
+
function boxOf(data) {
|
|
2707
|
+
const box = {};
|
|
2708
|
+
for (const key of ["top", "left", "width", "height"]) {
|
|
2709
|
+
if (data[key] !== void 0) box[key] = data[key];
|
|
2710
|
+
}
|
|
2711
|
+
return box;
|
|
2712
|
+
}
|
|
2596
2713
|
function stripHtml(path) {
|
|
2597
2714
|
return path.replace(/\.html$/, "");
|
|
2598
2715
|
}
|