@ericchen1990/pano-view 3.4.1 → 3.4.2

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 CHANGED
@@ -229,6 +229,10 @@ type VideoHotspotProps = HotspotCommonProps & {
229
229
  loop?: boolean;
230
230
  muted?: boolean;
231
231
  volume?: number;
232
+ /**
233
+ * Keep playback in the page. Defaults to true, including the WebKit and
234
+ * X5 attributes for iPhone Safari, WeChat, QQ, and UC.
235
+ */
232
236
  playsInline?: boolean;
233
237
  preload?: "none" | "metadata" | "auto";
234
238
  crossOrigin?: "" | "anonymous" | "use-credentials";
@@ -1298,6 +1302,10 @@ type PanoVideoProps = {
1298
1302
  muted?: boolean;
1299
1303
  volume?: number;
1300
1304
  autoPlay?: boolean;
1305
+ /**
1306
+ * Keep playback in the page. Defaults to true, including the WebKit and
1307
+ * X5 attributes for iPhone Safari, WeChat, QQ, and UC.
1308
+ */
1301
1309
  playsInline?: boolean;
1302
1310
  preload?: "none" | "metadata" | "auto";
1303
1311
  crossOrigin?: "" | "anonymous" | "use-credentials";
package/dist/index.d.ts CHANGED
@@ -229,6 +229,10 @@ type VideoHotspotProps = HotspotCommonProps & {
229
229
  loop?: boolean;
230
230
  muted?: boolean;
231
231
  volume?: number;
232
+ /**
233
+ * Keep playback in the page. Defaults to true, including the WebKit and
234
+ * X5 attributes for iPhone Safari, WeChat, QQ, and UC.
235
+ */
232
236
  playsInline?: boolean;
233
237
  preload?: "none" | "metadata" | "auto";
234
238
  crossOrigin?: "" | "anonymous" | "use-credentials";
@@ -1298,6 +1302,10 @@ type PanoVideoProps = {
1298
1302
  muted?: boolean;
1299
1303
  volume?: number;
1300
1304
  autoPlay?: boolean;
1305
+ /**
1306
+ * Keep playback in the page. Defaults to true, including the WebKit and
1307
+ * X5 attributes for iPhone Safari, WeChat, QQ, and UC.
1308
+ */
1301
1309
  playsInline?: boolean;
1302
1310
  preload?: "none" | "metadata" | "auto";
1303
1311
  crossOrigin?: "" | "anonymous" | "use-credentials";
package/dist/index.mjs CHANGED
@@ -9113,6 +9113,56 @@ function useWebVRPointerLock(enabled, element) {
9113
9113
  // src/webvr/wakelock.ts
9114
9114
  import { useEffect as useEffect19 } from "react";
9115
9115
 
9116
+ // src/video/inline-playback.ts
9117
+ var INLINE_VIDEO_ATTRIBUTES = [
9118
+ ["playsinline", "true"],
9119
+ ["webkit-playsinline", "true"],
9120
+ ["x5-playsinline", "true"],
9121
+ ["x5-video-player-type", "h5-page"],
9122
+ ["x5-video-player-fullscreen", "false"]
9123
+ ];
9124
+ function applyInlineVideoPlayback(video, inline) {
9125
+ video.controls = false;
9126
+ video.playsInline = inline;
9127
+ video.disablePictureInPicture = true;
9128
+ video.disableRemotePlayback = true;
9129
+ video.setAttribute("disablepictureinpicture", "");
9130
+ video.setAttribute("disableremoteplayback", "");
9131
+ video.setAttribute("x-webkit-airplay", "deny");
9132
+ video.setAttribute(
9133
+ "controlsList",
9134
+ "nodownload nofullscreen noremoteplayback"
9135
+ );
9136
+ if (inline) {
9137
+ for (const [name, value] of INLINE_VIDEO_ATTRIBUTES) {
9138
+ video.setAttribute(name, value);
9139
+ }
9140
+ return;
9141
+ }
9142
+ for (const [name] of INLINE_VIDEO_ATTRIBUTES) {
9143
+ video.removeAttribute(name);
9144
+ }
9145
+ }
9146
+ function mountTextureVideo(video, canvas) {
9147
+ const parent = canvas.closest("[data-pano-viewer]") ?? canvas.parentElement;
9148
+ if (!parent) {
9149
+ return;
9150
+ }
9151
+ video.tabIndex = -1;
9152
+ video.setAttribute("aria-hidden", "true");
9153
+ video.setAttribute("data-pano-texture-video", "");
9154
+ video.style.position = "absolute";
9155
+ video.style.top = "0";
9156
+ video.style.left = "0";
9157
+ video.style.width = "1px";
9158
+ video.style.height = "1px";
9159
+ video.style.opacity = "0";
9160
+ video.style.pointerEvents = "none";
9161
+ if (video.parentElement !== parent) {
9162
+ parent.appendChild(video);
9163
+ }
9164
+ }
9165
+
9116
9166
  // src/webvr/wakelock-fallback.ts
9117
9167
  var WEBVR_WAKELOCK_FALLBACK_VIDEO_BASE64 = "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";
9118
9168
 
@@ -9138,17 +9188,12 @@ function decodeBase64(value) {
9138
9188
  function createFallbackVideo() {
9139
9189
  const video = document.createElement("video");
9140
9190
  video.autoplay = true;
9141
- video.controls = false;
9142
9191
  video.defaultMuted = true;
9143
9192
  video.loop = true;
9144
9193
  video.muted = true;
9145
- video.playsInline = true;
9146
9194
  video.tabIndex = -1;
9147
9195
  video.ariaHidden = "true";
9148
- video.disablePictureInPicture = true;
9149
- video.setAttribute("playsinline", "");
9150
- video.setAttribute("webkit-playsinline", "");
9151
- video.setAttribute("disablepictureinpicture", "");
9196
+ applyInlineVideoPlayback(video, true);
9152
9197
  video.style.cssText = "pointer-events:none;position:fixed;bottom:0;left:0;width:1px;height:1px;opacity:0;";
9153
9198
  const src = URL.createObjectURL(
9154
9199
  new Blob([decodeBase64(WEBVR_WAKELOCK_FALLBACK_VIDEO_BASE64)], {
@@ -11132,7 +11177,6 @@ function PanoVideo({
11132
11177
  usePanoMediaActivationIntent(autoPlay);
11133
11178
  const gl = useThree16((state) => state.gl);
11134
11179
  const host = useContext23(PanoVideoHostContext);
11135
- const overlay = useContext23(PanoChromeOverlayContext);
11136
11180
  const variants = useMemo17(
11137
11181
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
11138
11182
  [poster, src, variantsProp]
@@ -11210,9 +11254,7 @@ function PanoVideo({
11210
11254
  }
11211
11255
  const video = document.createElement("video");
11212
11256
  video.muted = true;
11213
- video.playsInline = true;
11214
- video.setAttribute("playsinline", "true");
11215
- video.setAttribute("webkit-playsinline", "true");
11257
+ applyInlineVideoPlayback(video, true);
11216
11258
  video.preload = "metadata";
11217
11259
  const texture = new VideoTexture(video);
11218
11260
  applyEquirectangularLayout(texture, 1);
@@ -11242,22 +11284,15 @@ function PanoVideo({
11242
11284
  }
11243
11285
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
11244
11286
  }, [gl, resource]);
11245
- useEffect25(() => {
11246
- if (!resource || !overlay?.overlayElement) {
11287
+ useLayoutEffect3(() => {
11288
+ if (!resource) {
11247
11289
  return;
11248
11290
  }
11249
- const { video } = resource;
11250
- video.setAttribute("data-pano-video", "");
11251
- video.style.position = "absolute";
11252
- video.style.width = "1px";
11253
- video.style.height = "1px";
11254
- video.style.opacity = "0";
11255
- video.style.pointerEvents = "none";
11256
- overlay.overlayElement.appendChild(video);
11291
+ mountTextureVideo(resource.video, gl.domElement);
11257
11292
  return () => {
11258
- video.remove();
11293
+ resource.video.remove();
11259
11294
  };
11260
- }, [overlay?.overlayElement, resource]);
11295
+ }, [gl, resource]);
11261
11296
  const resolvedVariantId = resolvePanoVideoVariantId(variants, variantId);
11262
11297
  const activeVariant = variants.find((variant) => variant.id === resolvedVariantId) ?? variants[0];
11263
11298
  const mediaKey = activeVariant ? `${activeVariant.id}:${activeVariant.sources.map((source) => source.src).join("|")}:${tracks.map((track) => `${panoVideoTrackId(track)}:${track.src}`).join("|")}:${crossOrigin ?? ""}` : "";
@@ -11547,7 +11582,7 @@ function PanoVideo({
11547
11582
  }
11548
11583
  };
11549
11584
  }, [emitSnapshot, resource]);
11550
- useEffect25(() => {
11585
+ useLayoutEffect3(() => {
11551
11586
  if (!resource) {
11552
11587
  return;
11553
11588
  }
@@ -11557,14 +11592,7 @@ function PanoVideo({
11557
11592
  if (mediaCapabilitiesRef.current.volumeAdjustable) {
11558
11593
  video.volume = clampVolume(volume);
11559
11594
  }
11560
- video.playsInline = playsInline;
11561
- if (playsInline) {
11562
- video.setAttribute("playsinline", "true");
11563
- video.setAttribute("webkit-playsinline", "true");
11564
- } else {
11565
- video.removeAttribute("playsinline");
11566
- video.removeAttribute("webkit-playsinline");
11567
- }
11595
+ applyInlineVideoPlayback(video, playsInline);
11568
11596
  video.preload = preload;
11569
11597
  }, [loop, muted, playsInline, preload, resource, volume]);
11570
11598
  useEffect25(() => {
@@ -15960,7 +15988,7 @@ function SequenceHotspot({
15960
15988
 
15961
15989
  // src/hotspot/video-hotspot.tsx
15962
15990
  import { useThree as useThree27 } from "@react-three/fiber";
15963
- import { useEffect as useEffect38, useRef as useRef39, useState as useState20 } from "react";
15991
+ import { useEffect as useEffect38, useLayoutEffect as useLayoutEffect7, useRef as useRef39, useState as useState20 } from "react";
15964
15992
  import {
15965
15993
  ClampToEdgeWrapping as ClampToEdgeWrapping6,
15966
15994
  LinearFilter as LinearFilter9,
@@ -16036,6 +16064,7 @@ function useVideoResource({
16036
16064
  poster,
16037
16065
  src
16038
16066
  }) {
16067
+ const gl = useThree27((state) => state.gl);
16039
16068
  const [resource, setResource] = useState20(null);
16040
16069
  const onEndedRef = useRef39(onEnded);
16041
16070
  const onErrorRef = useRef39(onError);
@@ -16053,12 +16082,7 @@ function useVideoResource({
16053
16082
  if (crossOrigin !== void 0) {
16054
16083
  video.crossOrigin = crossOrigin;
16055
16084
  }
16056
- const inline = playsInline ?? true;
16057
- video.playsInline = inline;
16058
- if (inline) {
16059
- video.setAttribute("playsinline", "true");
16060
- video.setAttribute("webkit-playsinline", "true");
16061
- }
16085
+ applyInlineVideoPlayback(video, playsInline ?? true);
16062
16086
  video.poster = poster ?? "";
16063
16087
  const texture = new VideoTexture2(video);
16064
16088
  texture.colorSpace = SRGBColorSpace8;
@@ -16100,6 +16124,7 @@ function useVideoResource({
16100
16124
  video.addEventListener("error", reportError);
16101
16125
  video.addEventListener("pause", handlePause);
16102
16126
  video.addEventListener("play", handlePlay);
16127
+ mountTextureVideo(video, gl.domElement);
16103
16128
  setResource({ video, texture, ready: false });
16104
16129
  video.src = src;
16105
16130
  video.load();
@@ -16113,9 +16138,19 @@ function useVideoResource({
16113
16138
  video.pause();
16114
16139
  video.removeAttribute("src");
16115
16140
  video.load();
16141
+ video.remove();
16116
16142
  texture.dispose();
16117
16143
  };
16118
- }, [crossOrigin, id, playsInline, poster, src]);
16144
+ }, [crossOrigin, gl, id, playsInline, poster, src]);
16145
+ useLayoutEffect7(() => {
16146
+ if (!resource) {
16147
+ return;
16148
+ }
16149
+ mountTextureVideo(resource.video, gl.domElement);
16150
+ return () => {
16151
+ resource.video.remove();
16152
+ };
16153
+ }, [gl, resource]);
16119
16154
  return resource;
16120
16155
  }
16121
16156
  function VideoHotspot({
@@ -16154,21 +16189,14 @@ function VideoHotspot({
16154
16189
  const onPlaybackStateChangeRef = useRef39(onPlaybackStateChange);
16155
16190
  onPlaybackErrorRef.current = onPlaybackError;
16156
16191
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
16157
- useEffect38(() => {
16192
+ useLayoutEffect7(() => {
16158
16193
  if (!resource) {
16159
16194
  return;
16160
16195
  }
16161
16196
  resource.video.loop = loop;
16162
16197
  resource.video.muted = muted;
16163
16198
  resource.video.volume = clampVolume2(volume);
16164
- resource.video.playsInline = playsInline;
16165
- if (playsInline) {
16166
- resource.video.setAttribute("playsinline", "true");
16167
- resource.video.setAttribute("webkit-playsinline", "true");
16168
- } else {
16169
- resource.video.removeAttribute("playsinline");
16170
- resource.video.removeAttribute("webkit-playsinline");
16171
- }
16199
+ applyInlineVideoPlayback(resource.video, playsInline);
16172
16200
  resource.video.preload = preload;
16173
16201
  }, [loop, muted, playsInline, preload, resource, volume]);
16174
16202
  useEffect38(() => {