vr图片展示

1.安装

npm install pannellum

  

 

2.组件

  • 动态加载 Pannellum 库,避免模块导入错误
  • 支持全景图切换(示例中提供了三个测试全景图)
  • 提供完整控制界面(全屏、自动旋转、视角重置、缩放)
  • 加载状态显示与错误处理

view-vr.vue

<template>
    <div class="vr-player-container">
      <!-- 加载状态 -->
      <div v-if="loading" class="loading-overlay">
        <div class="spinner"></div>
        <p>加载全景图中...</p>
      </div>
     
      <!-- 全景图容器 -->
      <div ref="imgpanoramaContainer" class="panorama-container"></div>
   
    </div>
  </template>
 
<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import 'pannellum/build/pannellum.css';
 
const props = defineProps({
  imageUrl: {
    type: String,
    required: true
  },
  initialView: {
    type: Object,
    default: () => ({
      hfov: 110,
      pitch: 0,
      yaw: 0,
      autoRotate: 0
    })
  },
  showControls: {
    type: Boolean,
    default: true
  }
});
 
const imgpanoramaContainer = ref(null);
const viewer = ref(null);
const loading = ref(true);
//const autoRotate = ref(props.initialView.autoRotate !== 0);
 
// 动态加载 Pannellum
const loadPannellum = async () => {
  try {
    // 先加载 CSS
    await import('pannellum/build/pannellum.css');
    // 再加载 JS 并返回全局对象
    await import('pannellum');
    return window.pannellum;
  } catch (error) {
    console.error('加载 Pannellum 失败:', error);
    throw error;
  }
};
 
// 初始化全景图
const initPanorama = async () => {
  if (!imgpanoramaContainer.value) return;
   
  try {
    loading.value = true;
    const pannellum = await loadPannellum();
     
    viewer.value = pannellum.viewer(imgpanoramaContainer.value, {
      type: 'equirectangular',
      panorama: props.imageUrl,
      autoLoad: true,
     
     
    });
    viewer.value.on('load', () => {
      loading.value = false;
    })
    viewer.value.on('error', (err) => {
      console.error('全景图加载错误:', err);
      loading.value = false;
    })
  } catch (error) {
    console.error('初始化全景图失败:', error);
    loading.value = false;
  }
};
 
// 生命周期钩子
onMounted(() => {
  initPanorama();
});
 
onUnmounted(() => {
  if (viewer.value) {
    viewer.value.destroy();
    viewer.value = null;
  }
});
 
// 监听图片 URL 变化
watch(() => props.imageUrl, (newUrl) => {
  if (viewer.value && newUrl) {
    loading.value = true;
    viewer.value.loadScene({
      type: 'equirectangular',
      panorama: newUrl
    });
    viewer.value.setHfov(props.initialView.hfov);
    viewer.value.setPitch(props.initialView.pitch);
    viewer.value.setYaw(props.initialView.yaw);
    viewer.value.setAutoRotate(props.initialView.autoRotate);
     
    setTimeout(() => {
      loading.value = false;
    }, 1000);
  }
});
 

</script>
 
  <style scoped>
  .vr-player-container {
    position: relative;
    width: 100%;
    height: 500px;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  }
 
  .panorama-container {
    width: 100%;
    height: 100%;
  }
 
  .loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 10;
    color: white;
  }
 
  .spinner {
    width: 50px;
    height: 50px;
    border: 3px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    border-top-color: #fff;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
  }
 
  @keyframes spin {
    to { transform: rotate(360deg); }
  }
 
  .control-panel {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 10px;
    z-index: 5;
    background-color: rgba(0, 0, 0, 0.5);
    padding: 8px 15px;
    border-radius: 25px;
  }
 
  .control-button {
    background-color: rgba(255, 255, 255, 0.2);
    border: none;
    color: white;
    width: 35px;
    height: 35px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color 0.3s;
    font-size: 14px;
  }
 
  .control-button:hover {
    background-color: rgba(255, 255, 255, 0.4);
  }
 
  .zoom-controls {
    display: flex;
    gap: 5px;
  }
  </style>    

  

3.使用

import PannellumViewer from './components/view-vr.vue'
<div style="width:600px;height:300px">
    <PannellumViewer 
    :imageUrl="panoramaUrl"
      :initialView="{ hfov: 100, pitch: 0, yaw: 0, autoRotate: 1 }"
      :showControls="true" 
    />

  </div>
<script setup>
import PannellumViewer from './components/view-vr.vue'
const panoramaUrl = 'https://pannellum.org/images/alma.jpg'
</script>

  

posted @ 2025-06-10 09:13  枫若  阅读(24)  评论(0)    收藏  举报