vue3使用PhotoSphereViewer实现全景功能

  •  下载插件

npm install photo-sphere-viewer --save

或者 

yarn add photo-sphere-viewer 

  •  引入插件

import * as PhotoSphereViewer from 'photo-sphere-viewer';

import 'photo-sphere-viewer/dist/photo-sphere-viewer.css';

  •  全景遮罩盒子
<!-- 全景图预览 -->
<div v-show="vsible" class="mask">
	<div class="mask-close" @click="closeImg"><close-outlined /></div>
	<div v-if="vsible" id="viewer"></div>
	<ImaTabs ref="imgList" @sendImg="sendImg" />
</div>
// vsible控制显示与否
const vsible: Ref<boolean> = ref(false);
// 全景对象
const Viewer = ref();
// 底部轮播图片
const imgList = ref();
// 设置全景图片
const setImg = (img: AnyObject) => {
	Viewer.value?.destroy();
	imgList.value.activeIndex = imgList.value.progressList.findIndex(
		(item: AnyObject) => item.id == img.features[0].properties.id,
	);
	imgList.value.activeVsib = false;
	vsible.value = true;
	imgList.value.initgoRightArrow();
	setTimeout(() => {
		Viewer.value = new PhotoSphereViewer.Viewer({
			container: document.querySelector('#viewer') as
				| string
				| HTMLElement,
			panorama: '图片路径',
			size: {
				width: document.body.offsetWidth as
					| number
					| string
					| any,
				height: document.body.offsetHeight as
					| number
					| string
					| any,
			},
			navbar: undefined,
		});
		setTimeout(() => {
			Viewer.value?.hideError();
			Viewer.value.resize({
				width: document.documentElement.clientWidth as
					| number
					| string
					| any,
				height: document.documentElement.clientHeight as
					| number
					| string
					| any,
			});
		}, 1000);
		Viewer.value.on('ready', () => {
			imgList.value.activeVsib = true;
			Viewer.value?.hideError();
			Viewer.value.resize({
				width: document.documentElement.clientWidth as
					| number
					| string
					| any,
				height: document.documentElement.clientHeight as
					| number
					| string
					| any,
			});
		});
	}, 200);
};
// 关闭全景
const closeImg = () => {
	vsible.value = false;
	imgList.value.activeVsib = true;
};
// 底部轮播点击显示全景图片方法
const sendImg = (item: AnyObject) => {
	Viewer.value?.destroy();
	imgList.value.activeVsib = false;
	setTimeout(() => {
		const container = document.querySelector('#viewer') as
			| string
			| HTMLElement;
		Viewer.value = new PhotoSphereViewer.Viewer({
			container,
			panorama: `图片路径`,
			size: {
				width: '100%' as number | string | any,
				height: '100%' as number | string | any,
			},
			navbar: undefined,
		});
		setTimeout(() => {
			Viewer.value.resize({
				width: '100%' as number | string | any,
				height: '100%' as number | string | any,
			});
		}, 500);
		Viewer.value.on('ready', () => {
			imgList.value.activeVsib = true;
			Viewer.value?.hideError();
		});
	}, 200);
};
		
  •  ImaTabs组件
<!-- 全景轮播 -->
<template>
	<div v-if="progressList && progressList.length" class="imgTool">
		<div class="imgTool-proce">
			<div
				class="imgTool-proce-arrow"
				:class="
					currentClickNumber > 0 ? '' : 'imgTool-proce-arrowOpacity'
				"
				@click="fnPrev"
			>
				<img src="左箭头图片地址" alt="" />
			</div>
			<div ref="fixedBox" class="imgTool-proce-fixed">
				<div
					class="imgTool-proce-fixed-center"
					:style="`width:${
						signleWidth * progressList.length
					}px;transform:translate(${scrollResultWidth}px,0);transition:1s;`"
				>
					<div
						v-for="(itemP, indexP) in progressList"
						:key="itemP.id"
						class="imgTool-proce-fixed-center-signle"
						:class="
							activeIndex == indexP
								? 'imgTool-proce-fixed-center-signle-active'
								: ''
						"
						@click="activeImg(itemP, indexP)"
					>
						<div class="imgTool-proce-fixed-center-signle-icon">
							<img
								class="imgTool-proce-fixed-center-signle-icon-img"
								:src="src + itemP.compressPath"
								alt=""
							/>
						</div>
						<div
							class="imgTool-proce-fixed-center-signle-title"
							:title="itemP.title"
						>
							{{ itemP.title }}
						</div>
					</div>
				</div>
			</div>

			<div
				class="imgTool-proce-arrow"
				:class="noScrollRight ? '' : 'imgTool-proce-arrowOpacity'"
				@click="fnNext"
			>
				<img src="右箭头图片地址" alt="" />
			</div>
		</div>
	</div>
</template>

<script lang="ts">
import { ref, defineComponent, nextTick } from 'vue';
import { ImgDatas } from './src/interface';
import { ImgList } from './src/hooks';
import { src } from '@api/config/request';
export default defineComponent({
	name: 'ImgTabs',
	setup(prop, { emit }) {
		const progressList = ref<ImgDatas[]>([]);
		const scrollResultWidth = ref<number>(0); //transform滚动的距离
		const signleWidth = ref<number>(140); //单个流程的宽度
		const currentClickNumber = ref<number>(0);
		const noScrollRight = ref<boolean>(true);
		const activeIndex = ref<number>(0);
		const fixedBox = ref();
		const activeVsib = ref(true);
		const { initgoRightArrow, fnPrev, fnNext, activeImg } = ImgList(
			progressList,
			scrollResultWidth,
			signleWidth,
			currentClickNumber,
			noScrollRight,
			activeIndex,
			fixedBox,
			emit,
			activeVsib,
		);
		nextTick(() => {
			setTimeout(() => {
				initgoRightArrow();
			});
		});
		return {
			progressList,
			scrollResultWidth,
			signleWidth,
			currentClickNumber,
			noScrollRight,
			activeIndex,
			fixedBox,
			fnPrev,
			fnNext,
			activeImg,
			initgoRightArrow,
			src: src.value.FILE_URL,
			activeVsib,
		};
	},
});
</script>
  •  轮播方法
import { Ref } from 'vue';
export const ImgList = (
	progressList: Ref<ImgDatas[]>,
	scrollResultWidth: Ref<number>,
	signleWidth: Ref<number>,
	currentClickNumber: Ref<number>,
	noScrollRight: Ref<boolean>,
	activeIndex: Ref<number>,
	fixedBox,
	emit,
	activeVsib: Ref<boolean>,
) => {
	interface ImgDatas {
		id: number;
		compressPath: string;
		title: string;
	}
	//初始化判断是否可以向右滚动
	const initgoRightArrow = () => {
		const currentScrollWidth = fixedBox.value.clientWidth;
		const canNumber = Math.floor(currentScrollWidth / signleWidth.value); //可以放下的个数
		//如果最后一个流程图标已经展示出来,则停止滚动
		if (currentClickNumber.value + canNumber >= progressList.value.length) {
			noScrollRight.value = false;
			return;
		}
	};
	//点击上一个
	const fnPrev = () => {
		//如果右点击的次数大于0,才可以左滚
		if (currentClickNumber.value > 0) {
			currentClickNumber.value -= 1;
			noScrollRight.value = true;
			fnScrollWidth('reduce');
		} else {
			return false;
		}
	};
	//点击下一个
	const fnNext = () => {
		const currentScrollWidth = fixedBox.value.clientWidth;
		const canNumber = Math.floor(currentScrollWidth / signleWidth.value); //可以放下的个数
		//如果最后一个流程图标已经展示出来,则停止滚动
		if (currentClickNumber.value + canNumber >= progressList.value.length) {
			return;
		}
		//说明放不下有滚动条
		if (progressList.value.length > canNumber) {
			currentClickNumber.value += 1;
			if (
				currentClickNumber.value + canNumber >=
				progressList.value.length
			) {
				noScrollRight.value = false;
			}
			fnScrollWidth('add');
		}
	};
	//translate的宽度
	const fnScrollWidth = (type: string) => {
		let result = 0;
		if (type === 'reduce') {
			result = 140;
		} else if (type === 'add') {
			result = -140;
		} else {
			result = 0;
		}
		scrollResultWidth.value += result;
	};
	const activeImg = (item: ImgDatas, index: number) => {
		if (activeIndex.value == index) {
			return;
		}
		if (activeVsib.value) {
			activeIndex.value = index;
			emit('sendImg', item);
		}
	};
	return {
		initgoRightArrow,
		fnPrev,
		fnNext,
		fnScrollWidth,
		activeImg,
	};
};
  •  样式
.mask {
	width: 100%;
	height: 100%;
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	margin: auto;
	z-index: 999;
}
.imgTool {
	width: 100%;
	position: fixed;
	bottom: 10px;
	z-index: 99999;
	background: rgba($color: #000000, $alpha: 0.2);
	padding: 0 13px;
	&-proce {
		display: flex;
		align-items: center;
		justify-content: space-between;
		&-arrow {
			width: 28px;
			height: 52px;
			cursor: pointer;
			&>img{
				width: 100%;
				height: 100%;
			}
		}
		&-arrowOpacity {
			cursor: default;
			opacity: 0.4;
		}
		&-fixed {
			flex: 1;
			overflow: hidden;
			&-center {
				// flex: 1;
				box-sizing: border-box;
				padding: 20px 0;
				white-space: nowrap;
				display: flex;
				&-signle {
					width: 120px;
					position: relative;
					margin: 0 10px;
					border: 2px solid rgba($color: #000000, $alpha: 0);
					box-sizing: border-box;
					&-icon {
						width: 100%;
						height: 100%;
						text-align: center;
						cursor: pointer;
						&-img {
							width: 100%;
							height: 100%;
						}
					}
					&-active {
						border: 2px solid #1f75e5;
					}
					&-title {
						width: 100%;
						position: absolute;
						bottom: 0px;
						left: 0px;
						background: rgba($color: #000000, $alpha: 0.3);
						padding: 1px 5px;
						border-radius: 0 3px 0 0;
						font-size: 12px;
						overflow: hidden; //超出的文本隐藏
						text-overflow: ellipsis; //溢出用省略号显示
						white-space: nowrap; // 默认不换行;
					}
				}
			}
		}
	}
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 很抱歉,我不会说中文。但是,我可以使用英文来帮助您解决问题。 如果您使用的是 Viewer.js,请确保您已经在 Vue 项目中正确地引入了 Viewer.js。您可以在 Vue 组件中使用以下代码来初始化 Viewer.js: ``` import Viewer from 'viewerjs' export default { mounted () { this.viewer = new Viewer(this.$refs.viewer, { inline: false, viewed () { this.viewer.zoomTo(1) } }) }, beforeDestroy () { if (this.viewer) { this.viewer.destroy() } } } ``` 如果您的项目中还有其他问题,请提供更多信息,我将尽力帮助您解决。 ### 回答2: 在Vue项目中使用viewer实现放大功能时出现bug可能有以下几种原因和解决方案。 1. 插件版本不兼容:检查使用viewer插件版本是否与Vue项目的版本相符。如果不兼容,可以尝试更新插件版本或切换到与Vue项目兼容的插件。 2. 引入有误:检查在Vue项目中正确引入插件。通常应在Vue组件中通过import或引入cdn链接的方式引入插件。确保引入路径正确且没有拼写错误,并确认插件已正确安装。 3. 配置错误:检查配置参数是否正确设置。查看文档或示例代码,确认是否有遗漏或错误的配置项。特别是与放大功能相关的配置项,如zoom,minZoom,maxZoom等。 4. DOM元素问题:检查用于展示图片或内容的DOM元素是否正确指定。确保指定正确的元素,通常是一个容器元素,如div,用于包裹需要展示的图片内容。 5. 其他冲突:查看浏览器的控制台输出是否有错误提示。可能是其他第三方库与viewer插件产生冲突,尝试移除其他插件或通过更改加载顺序解决冲突问题。 如果以上解决方法仍然无法解决问题,可以尝试在Vue项目中使用其他类似的放大插件或组件,并进行适当调整和配置,以满足项目的需求。同时,也建议查阅viewer插件的官方文档或社区,寻找其他开发者遇到的类似问题和解决方案。 ### 回答3: 在Vue项目中使用viewer插件实现放大功能时出现bug的可能原因有多种。 1. 插件版本不匹配:需要确认使用viewer插件版本与当前Vue项目的版本是否兼容。如果版本不匹配,可能会导致一些功能无法正常使用或出现bug。 2. 组件引入问题:在Vue项目中使用插件需要正确引入组件。可能是没有正确引入viewer组件或者引入的组件位置不正确导致bug出现。 3. 配置问题:viewer插件一般有许多可配置项,如放大倍数、最大尺寸等。可能是配置参数设置不正确导致的bug出现。需要检查配置参数是否正确传递给了viewer组件。 4. 数据源问题:viewer插件需要提供数据源(如图片地址、视频链接等)作为展示内容。可能是数据源传递不正确或者数据源为空导致的bug。需要确认数据源是否正确传递给了viewer组件。 5. 其他冲突问题:可能是与其他组件或库冲突导致的bug。在Vue项目中可能同时使用了其他的插件或库,可能存在冲突导致的bug。需要检查是否存在其他的冲突因素。 针对以上问题,可以尝试一些解决方案,比如: - 确认插件版本是否匹配当前项目版本,如果不匹配则尝试更新或降低插件版本。 - 检查组件引入位置和代码,确保正确引入,并查看是否有错误的导入路径。 - 仔细检查配置参数,确保正确传递给了viewer组件。 - 确认数据源是否正确传递给了viewer组件,并检查数据源是否存在问题。 - 暂时禁用其他插件或库,检查是否存在冲突问题。 同时,如果仍然无法解决问题,可以尝试向相关技术论坛或社区提问,或者查阅官方文档以及其他开发者遇到类似问题的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小满blue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值