@open-wxa/wxa-runner-core 0.12.4 → 0.13.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.
@@ -1977,7 +1977,7 @@ ${frames}` : "");
1977
1977
  if (warnedOrigins.has(url.origin)) return;
1978
1978
  warnedOrigins.add(url.origin);
1979
1979
  console.warn(
1980
- `[wxa-runner] web-view \u52A0\u8F7D\u7684 ${url.origin} \u4E0E\u6A21\u62DF\u5668\uFF08${location.origin}\uFF09\u4E0D\u662F\u540C\u4E00\u4E2A\u7AD9\u70B9\uFF0C\u6D4F\u89C8\u5668\u6309\u8DE8\u7AD9\u5D4C\u5165\u5904\u7406\uFF0C\u7F51\u9875\u7684 Cookie \u53EF\u80FD\u4E0D\u751F\u6548\uFF08\u5FAE\u4FE1\u91CC\u7684\u5C0F\u7A0B\u5E8F\u6CA1\u6709\u8FD9\u4E2A\u95EE\u9898\uFF09\u3002\u9700\u8981 Cookie \u65F6\u8BA9\u7F51\u9875\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF1A\u547D\u4EE4\u884C\u52A0 --webview-window\uFF0CPlaywright \u7528 test.use({ wxaWebView: 'window' })`
1980
+ `[wxa-runner] web-view \u52A0\u8F7D\u7684 ${url.origin} \u4E0E\u6A21\u62DF\u5668\uFF08${location.origin}\uFF09\u4E0D\u662F\u540C\u4E00\u4E2A\u7AD9\u70B9\uFF0C\u6D4F\u89C8\u5668\u6309\u8DE8\u7AD9\u5D4C\u5165\u5904\u7406\uFF0C\u7F51\u9875\u7684 Cookie \u53EF\u80FD\u4E0D\u751F\u6548\uFF08\u5FAE\u4FE1\u91CC\u7684\u5C0F\u7A0B\u5E8F\u6CA1\u6709\u8FD9\u4E2A\u95EE\u9898\uFF09\u3002\u9700\u8981 Cookie \u65F6\u8BA9\u7F51\u9875\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF1A\u914D\u7F6E\u6587\u4EF6 wxa.config.ts \u91CC\u5199 webViewWindow: true\uFF0CPlaywright \u7528 test.use({ wxaWebView: 'window' })`
1981
1981
  );
1982
1982
  }
1983
1983
  function readableUrl(frame) {
@@ -2157,6 +2157,49 @@ ${frames}` : "");
2157
2157
  return navigator.userActivation?.isActive ?? true;
2158
2158
  }
2159
2159
 
2160
+ // src/runtime/host/ui/pageTransition.ts
2161
+ var DURATION = 300;
2162
+ function canAnimatePageTransition(frame) {
2163
+ return frame.isConnected && !document.hidden && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2164
+ }
2165
+ async function animatePageTransition(previous, current, direction) {
2166
+ const viewport = current.parentElement;
2167
+ const body = current.contentDocument?.body;
2168
+ if (!viewport || previous.parentElement !== viewport || !body) return;
2169
+ const { visibility, zIndex } = previous.style;
2170
+ const outgoing = previous.contentDocument?.documentElement ?? previous;
2171
+ const wasInert = outgoing.inert;
2172
+ const shield = body.ownerDocument.createElement("div");
2173
+ shield.className = "wxa-page-transition-shield";
2174
+ shield.setAttribute("aria-hidden", "true");
2175
+ shield.style.cssText = "position: fixed; inset: 0; z-index: 2147483647; pointer-events: auto; background: transparent;";
2176
+ const sliding = direction === "push" ? current : previous;
2177
+ const transform = direction === "push" ? ["translateX(100%)", "translateX(0)"] : ["translateX(0)", "translateX(100%)"];
2178
+ let animation;
2179
+ let timer;
2180
+ try {
2181
+ outgoing.inert = true;
2182
+ body.append(shield);
2183
+ previous.style.visibility = "visible";
2184
+ if (direction === "pop") previous.style.zIndex = "2";
2185
+ animation = sliding.animate({ transform }, { duration: DURATION, easing: "ease", fill: "both" });
2186
+ await Promise.race([
2187
+ animation.finished.catch(() => void 0),
2188
+ // 切到后台后,浏览器可能暂停动画,路由队列仍需继续。
2189
+ new Promise((resolve) => {
2190
+ timer = setTimeout(resolve, DURATION + 60);
2191
+ })
2192
+ ]);
2193
+ } finally {
2194
+ clearTimeout(timer);
2195
+ animation?.cancel();
2196
+ previous.style.visibility = visibility;
2197
+ previous.style.zIndex = zIndex;
2198
+ outgoing.inert = wasInert;
2199
+ shield.remove();
2200
+ }
2201
+ }
2202
+
2160
2203
  // src/runtime/host/ui/dom.ts
2161
2204
  function element(tag, className, text) {
2162
2205
  const el = document.createElement(tag);
@@ -2475,6 +2518,7 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
2475
2518
  this.pages = root.querySelector(".wxa-pages");
2476
2519
  this.tabBar = root.querySelector(".wxa-tabbar");
2477
2520
  this.overlay = root.querySelector(".wxa-overlay");
2521
+ guardOverlayTouchClicks(this.overlay);
2478
2522
  this.routeLabel = root.querySelector(".wxa-route");
2479
2523
  root.querySelector(".wxa-reload").addEventListener("click", () => location.reload());
2480
2524
  this.scaleButton.addEventListener("click", () => {
@@ -2513,6 +2557,18 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
2513
2557
  onBack(handler) {
2514
2558
  this.backButton.addEventListener("click", handler);
2515
2559
  }
2560
+ async transitionPages(previous, current, direction) {
2561
+ const navWasInert = this.navBar.inert;
2562
+ const tabsWereInert = this.tabBar.inert;
2563
+ this.navBar.inert = true;
2564
+ this.tabBar.inert = true;
2565
+ try {
2566
+ await animatePageTransition(previous, current, direction);
2567
+ } finally {
2568
+ this.navBar.inert = navWasInert;
2569
+ this.tabBar.inert = tabsWereInert;
2570
+ }
2571
+ }
2516
2572
  setTabBar(config, onTap) {
2517
2573
  this.tabBarConfig = config && config.list?.length && !config.custom ? config : null;
2518
2574
  this.onTabTap = onTap;
@@ -2757,6 +2813,24 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
2757
2813
  function maskElement(dim) {
2758
2814
  return element("div", "wxa-mask" + (dim ? " is-dim" : ""));
2759
2815
  }
2816
+ function guardOverlayTouchClicks(overlay) {
2817
+ let press;
2818
+ overlay.addEventListener("pointerdown", (event) => {
2819
+ press = event.pointerType === "touch" && event.target instanceof Node ? { id: event.pointerId, target: event.target } : void 0;
2820
+ }, true);
2821
+ overlay.addEventListener("pointercancel", (event) => {
2822
+ if (press?.id === event.pointerId) press = void 0;
2823
+ }, true);
2824
+ overlay.addEventListener("click", (event) => {
2825
+ if (event.pointerType !== "touch") return;
2826
+ if (press?.id === event.pointerId) {
2827
+ const start = press;
2828
+ press = void 0;
2829
+ if (event.target instanceof Node && event.target.contains(start.target)) return;
2830
+ }
2831
+ event.stopPropagation();
2832
+ }, true);
2833
+ }
2760
2834
  var BACK_ICON = '<svg width="12" height="20" viewBox="0 0 12 20"><path d="M10 2 2 10l8 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
2761
2835
  var MORE_ICON = '<svg width="22" height="8" viewBox="0 0 22 8" fill="currentColor"><circle cx="3.5" cy="4" r="2.3"/><circle cx="11" cy="4" r="3.3"/><circle cx="18.5" cy="4" r="2.3"/></svg>';
2762
2836
  var CLOSE_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8.2" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="10" cy="10" r="3.4" fill="currentColor"/></svg>';
@@ -3013,20 +3087,35 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
3013
3087
  this.stack.reset(page);
3014
3088
  this.activate(page, "appLaunch");
3015
3089
  }
3016
- async navigateTo(url) {
3090
+ async navigateTo(url, { animate = true } = {}) {
3017
3091
  if (!this.stack.canPush()) return fail("navigateTo", "webview count limit exceed");
3018
3092
  const { path, query } = this.resolveUrl(url);
3019
3093
  if (this.tabPaths.has(path)) return fail("navigateTo", "can not navigateTo a tabbar page");
3020
3094
  if (!this.hasPage(path)) return fail("navigateTo", `page "${path}" is not found`);
3021
3095
  const page = await this.openPage(path, query);
3096
+ const previous = this.currentPage();
3022
3097
  this.stack.push(page);
3023
3098
  this.activate(page, "navigateTo");
3099
+ if (animate && this.info.pageTransition && previous && canAnimatePageTransition(previous.frame)) {
3100
+ await this.shell.transitionPages(previous.frame, page.frame, "push");
3101
+ }
3024
3102
  return ok("navigateTo", { webviewId: page.id });
3025
3103
  }
3026
3104
  async navigateBack(delta) {
3027
3105
  if (this.stack.size <= 1) return fail("navigateBack", "cannot navigate back at first page.");
3028
- this.stack.pop(delta).forEach((page) => this.closePage(page));
3029
- this.activate(this.currentPage(), "navigateBack");
3106
+ const popped = this.stack.pop(delta);
3107
+ const outgoing = popped[0];
3108
+ const animate = this.info.pageTransition && outgoing && canAnimatePageTransition(outgoing.frame);
3109
+ for (const page of popped) {
3110
+ if (!animate || page !== outgoing) this.closePage(page);
3111
+ }
3112
+ const target = this.currentPage();
3113
+ try {
3114
+ this.activate(target, "navigateBack");
3115
+ if (animate) await this.shell.transitionPages(outgoing.frame, target.frame, "pop");
3116
+ } finally {
3117
+ if (animate) this.closePage(outgoing);
3118
+ }
3030
3119
  return ok("navigateBack");
3031
3120
  }
3032
3121
  async redirectTo(url) {
@@ -3458,7 +3547,7 @@ ${script}
3458
3547
  await host.enqueueRoute(async () => {
3459
3548
  for (const { path, query } of this.saved.slice(1)) {
3460
3549
  const search = new URLSearchParams(query).toString();
3461
- const result = await host.navigateTo(search ? `${path}?${search}` : path);
3550
+ const result = await host.navigateTo(search ? `${path}?${search}` : path, { animate: false });
3462
3551
  if (!String(result.errMsg).endsWith(":ok")) {
3463
3552
  console.warn(`[wxa-runner] \u6062\u590D\u9875\u9762\u6808\u65F6\u6253\u4E0D\u5F00 ${path}\uFF1A${result.errMsg}`);
3464
3553
  return;