基于vue3.0实现vr全景编辑器

随着社会的不断发现,现实生活中有很多时候会使用到全景现实,比如房地产行业vr看房,汽车行业vr看车之类的,全景可视化真实还原了现场的场景,真正做到沉浸式体验。

现在我们基于vue3.0版本开发出了一款沉浸式的编辑器,只需要使用全景相机在现场拍摄全景场景,然后将场景倒入编辑器,通过拖动图标和导航的方式绑定数据,从而实现沉浸式场景可视化。

部分洁面:

1、自定义动态添加数据绑定图标,实时监控数据运行状态

2、自定义添加文字标记,绑定文字文本,标识场景设备名称

 3、自定义添加场景标记,点击可以切换不同场景视角

4、自定义添加地图经纬度标记,查看当前标记位置

5、自定义添加音视频标记,点击查看音视频播放

 6、自定义添加网址和富文本标记,点击跳转网址查看富文本内容 

 部分代码如下:

(function (w) { // isFormat 表示是否格式化时间格式,,默认为格式化function $Date (isFormat = true) { // 格式化日期 前台传值方式  引用类.dateFormat(1402233166999,"yyyy-MM-dd hh:mm:ss")this.dateFormat = function (date, fmt = 'yyyy-MM-dd hh:mm:ss') {let getDate = new Date(date);let o = {'M+': getDate.getMonth() + 1,'d+': getDate.getDate(),'h+': getDate.getHours(),'m+': getDate.getMinutes(),'s+': getDate.getSeconds(),'q+': Math.floor((getDate.getMonth() + 3) / 3),'S': getDate.getMilliseconds()};if (/(y+)/.test(fmt)) {fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length));}for (let k in o) {if (new RegExp('(' + k + ')').test(fmt)) {fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));}}return fmt;};// 当前日期时间this.now = isFormat ? this.dateFormat(new Date()) : new Date();// 当前日期this.date = this.dateFormat(new Date()).split(' ')[0];// 当前时间this.time = this.dateFormat(new Date()).split(' ')[1];// 当前月this.month = new Date().getMonth() + 1;// 当前消失this.hours = new Date().getHours();// 当前月天数this.monthDays = (() => {let nowMonth = new Date().getMonth(); // 当前月let nowYear = new Date().getYear(); // 当前年let monthStartDate = new Date(nowYear, nowMonth, 1);let monthEndDate = new Date(nowYear, nowMonth + 1, 1);let days = (monthEndDate - monthStartDate) / (1000 * 60 * 60 * 24);return days;})();// 本周的开始日期和结束日日期this.endDayOfWeek = (() => {let nowMonth = new Date().getMonth(); // 当前月let nowDay = new Date().getDate(); // 当前日let nowDayOfWeek = new Date().getDay(); // 今天本周的第几天let day = nowDayOfWeek || 7;const start = new Date(new Date().getFullYear(), nowMonth, nowDay - day + 1);const starts = new Date(new Date().getFullYear(), nowMonth, nowDay - day + 1);const end = new Date(new Date().getFullYear(), nowMonth, nowDay + 7 - day);const ends = new Date(new Date().getFullYear(), nowMonth, nowDay + 7 - day);starts.setHours(23);starts.setMinutes(59);starts.setSeconds(59);ends.setHours(23);ends.setMinutes(59);ends.setSeconds(59);const firstDay = isFormat ? this.dateFormat(start) : start;const firstDays = isFormat ? this.dateFormat(starts) : starts;const lastDay = isFormat ? this.dateFormat(end) : end;const lastDays = isFormat ? this.dateFormat(ends) : ends;return {firstDay, firstDays, lastDay, lastDays};})();// 当天开始时间this.todayBegin = (() => {const now = new Date();now.setHours(0);now.setMinutes(0);now.setSeconds(0);return isFormat ? this.dateFormat(now) : now;})();// 当天59时59分59秒this.todayEnd = (() => {const now = new Date();now.setHours(23);now.setMinutes(59);now.setSeconds(59);return isFormat ? this.dateFormat(now) : now;})();// 指定月的最后第一天和最后一天this.getNowTheMothEnd = (M) => {if (typeof M !== 'number') {throw new Error('输入数字');}if (M < 0 || M > 12) {console.error('日期大于1小于12');return false;}const now = new Date();let y = now.getFullYear();let m = M - 1;const firstDay = new Date(y, m, 1);const firstDays = new Date(y, m, 1);firstDays.setHours(23);firstDays.setMinutes(59);firstDays.setSeconds(59);const lastDay = new Date(y, m + 1, 0);const lastDays = new Date(y, m + 1, 0);lastDays.setHours(23);lastDays.setMinutes(59);lastDays.setSeconds(59);return {firstDay: isFormat ? this.dateFormat(firstDay) : firstDay,firstDays: isFormat ? this.dateFormat(firstDays) : firstDays,lastDay: isFormat ? this.dateFormat(lastDay) : lastDay,lastDays: isFormat ? this.dateFormat(lastDays) : lastDays};};// 当月的最后第一天和最后一天this.nowMothEnd = (() => {const now = new Date();let y = now.getFullYear();let m = now.getMonth();const firstDay = new Date(y, m, 1);const firstDays = new Date(y, m, 1);firstDays.setHours(23);firstDays.setMinutes(59);firstDays.setSeconds(59);const lastDay = new Date(y, m + 1, 0);const lastDays = new Date(y, m + 1, 0);lastDays.setHours(23);lastDays.setMinutes(59);lastDays.setSeconds(59);return {firstDay: isFormat ? this.dateFormat(firstDay) : firstDay,firstDays: isFormat ? this.dateFormat(firstDays) : firstDays,lastDay: isFormat ? this.dateFormat(lastDay) : lastDay,lastDays: isFormat ? this.dateFormat(lastDays) : lastDays};})();// 今年的第一天和今年的最后一天this.nowYearsEnd = (() => {const now = new Date();let y = now.getFullYear();let m = now.getMonth();console.log(m);const firstDay = new Date(y, 0, 1);const firstDays = new Date(y, 0, 1);firstDays.setHours(23);firstDays.setMinutes(59);firstDays.setSeconds(59);const lastDay = new Date(y, 12, 0);const lastDays = new Date(y, 12, 0);lastDays.setHours(23);lastDays.setMinutes(59);lastDays.setSeconds(59);return {firstDay: isFormat ? this.dateFormat(firstDay) : firstDay,firstDays: isFormat ? this.dateFormat(firstDays) : firstDays,lastDay: isFormat ? this.dateFormat(lastDay) : lastDay,lastDays: isFormat ? this.dateFormat(lastDays) : lastDays};})();// 指定年的第一天和今年的最后一天this.nowTheYearsEnd = (Y) => {const now = new Date();let y = Y;let m = now.getMonth();console.log(m);const firstDay = new Date(y, 0, 1);const firstDays = new Date(y, 0, 1);firstDays.setHours(23);firstDays.setMinutes(59);firstDays.setSeconds(59);const lastDay = new Date(y, 12, 0);const lastDays = new Date(y, 12, 0);lastDays.setHours(23);lastDays.setMinutes(59);lastDays.setSeconds(59);return {firstDay: isFormat ? this.dateFormat(firstDay) : firstDay,firstDays: isFormat ? this.dateFormat(firstDays) : firstDays,lastDay: isFormat ? this.dateFormat(lastDay) : lastDay,lastDays: isFormat ? this.dateFormat(lastDays) : lastDays};};// 当前时间最近前N天this.getNowBeforeNday = (N) => {const now = new Date().getTime();const before = new Date().getTime() - (24 * 60 * 60 * 1000) * N;return {now: isFormat ? this.dateFormat(new Date(now)) : new Date(now),before: isFormat ? this.dateFormat(new Date(before)) : new Date(before)};};// 当前时间后面N天this.getNowAfterNday = (N) => {const now = new Date().getTime();const after = new Date().getTime() + (24 * 60 * 60 * 1000) * N;return {now: isFormat ? this.dateFormat(new Date(now)) : new Date(now),after: isFormat ? this.dateFormat(new Date(after)) : new Date(after)};};}w.$Date = $Date;
})(window);
const date = window.$Date;
export const $Date = date;
<template><div class="edit-hot_wrapper"><el-dialogv-model="dialogVisible":title="`${hotSpot.id ? '修改' : '新增'}${spotTypeName(form.iconType)}标记`":width="'360px'":top="'10px'":modal="false":close-on-click-modal="false":custom-class="'edit-hot_dialog'":before-close="close"><el-form :model="form" ref="ruleForm" :rules="rules"><el-form-item label="标记名称" required prop="name"><el-input v-model="form.name" placeholder="输入名称" clearable/></el-form-item><!--基础图标绑定--><template v-if="form.iconType == 1"><el-form-item label="绑定数据" required><el-selectv-model="form.devices"placeholder="选择设备"filterablemultiplecollapse-tags><el-optionv-for="v in deviveData.data":label="v.name":value="v.id":key="v.id"></el-option></el-select></el-form-item><el-form-item label="标记图标" required prop="spotTypes"><el-image:class="['icon-types', form.spotTypes == v.id ? 'active' : '']"v-for="v in hotTypes":key="v.id":src="v.url"@click="form.spotTypes = v.id"/></el-form-item></template><!-- 文字图标绑定--><template v-if="form.iconType == 2"><el-form-item label="绑定数据" required prop="devices"><el-selectv-model="form.devices"placeholder="选择设备"filterablemultiplecollapse-tags><el-optionv-for="v in deviveData.data":label="v.name":value="v.id":key="v.id"></el-option></el-select></el-form-item><el-form-item label="文字内容" required prop="text"><el-inputv-model="form.text":rows="4"type="textarea"placeholder="输入文字内容"/></el-form-item></template><!-- 场景图标绑定--><template v-if="form.iconType == 3"><el-form-item label="绑定场景" required prop="sceneId"><el-selectv-model="form.sceneId"placeholder="选择场景"><el-optionv-for="v in scenes":label="v.name":value="v.id":key="v.id"></el-option></el-select></el-form-item></template><!-- 位置图标绑定--><template v-if="form.iconType == 4"><el-form-item label="地图位置" required prop="localtion"><el-input v-model="form.localtion" placeholder="输入经纬度" clearable/></el-form-item><el-form-item label="经纬度查询"><el-button type="success">查询</el-button></el-form-item></template><!-- 位置图标绑定--><template v-if="form.iconType == 5"><el-form-item label="媒体地址" required prop="media"><el-input v-model="form.media.url" placeholder="输入媒体地址" clearable/></el-form-item><el-form-item label="媒体类型" required><el-radio-group v-model="form.media.type" class="ml-4"><el-radio :label="0" size="large">音频</el-radio><el-radio :label="1" size="large">视频</el-radio></el-radio-group></el-form-item></template><!-- 网址图标绑定--><template v-if="form.iconType == 6"><el-form-item label="网站地址" required prop="website"><el-input v-model="form.website" placeholder="输入网址" clearable/></el-form-item></template><!-- 网址图标绑定--><template v-if="form.iconType == 7"><el-form-item label="富文本" required prop="html"><el-input v-model="form.html" :rows="5" type="textarea" placeholder="输入网址" clearable/></el-form-item></template></el-form><template #footer><span class="dialog-footer"><el-button @click="close">取消</el-button><el-button type="primary" @click="save(ruleForm)">保存</el-button></span></template></el-dialog></div>
</template><script setup>
import { res } from '../data/res.js';
// import store from '../../../store/index.js';
import { hotTypes } from '../utils/data.js';
import { defineEmits, defineProps } from 'vue';
import { reactive, ref, computed, onMounted } from 'vue';
import { useStore } from 'vuex';
import { ElMessage } from 'element-plus';
import store from '../vuex/vrViewer.js';
import { ICOM_TYPE } from '../utils/data.js';const ruleForm = ref(null);
const rules = reactive({name: [{required: true,trigger: 'change',message: '标记名称必填'}],devices: [{required: true,trigger: 'change',message: '绑定设备必填'}],spotTypes: [{required: true,trigger: 'change',message: '标记图标类型必填'}],text: [{required: true,trigger: 'change',message: '文本内容必填'}],sceneId: [{required: true,trigger: 'change',message: '绑定场景必填'}],localtion: [{required: true,trigger: 'change',message: '绑定位置必填'}],media: [{required: true,trigger: 'change',message: '媒体地址必填'}],website: [{required: true,trigger: 'change',message: '网站地址必填'}],html: [{required: true,trigger: 'change',message: '富文本内容必填'}],
});
const dialogVisible = ref(true);
const deviveData = ref(res);
const props = defineProps({hotSpot: {}, // 当前标记scenes: {}
});let stores = useStore();
// 当前标记类型id
const iconType = computed(() => {return stores.state.vrViewer.type;
});// 当前标记类型名称
const spotTypeName = () => {const list = Object.values(ICOM_TYPE);return list.find(v => v.type == form.value.iconType).name;
};const types = ref(hotTypes);
const emits = defineEmits(['success','close','save'
]);onMounted(() => {// 编辑回显示if (props.hotSpot.id) {form.value = props.hotSpot.data;// store.commit('vrViewer/setIconType', props.hotSpot.data.iconType);}
});const form = ref({id: '', // 标记idname: '',devices: [],spotTypes: types.value[0].id, // 图标类型spotUrl: types.value[0].url, // 标记图表urliconType: stores.state.vrViewer.type,text: '', // 文字绑定sceneId: '', // 场景绑定localtion: '', // 位置经纬度绑定media: {type: 0, // 0代表音频, 1代表视频url: '' // url播放地址},website: '', // 网址html: '', // html绑定
});const close = () => emits('close');const save = async (formEl) => {// if (!form.value.name) {//   // 其他类型报错//   ElMessage({//     showClose: true,//     message: '输入名称',//     type: 'error'//   });//   return;// }// if (!form.value.devices.length) {//   // 其他类型报错//   ElMessage({//     showClose: true,//     message: '选择设备',//     type: 'error'//   });//   return;// }console.log(formEl, 'formEl');await formEl.validate((valid, fields) => {if (valid) {emits('save', form.value, props.hotSpot);close();} else {console.log('error submit!', fields)}})
};const success = () => {emits('success');
};
</script>
<style lang="scss">.edit-hot_dialog {.el-dialog__body {padding: 10px 20px;.el-select {width: 245px;}}}
</style>
<style lang="scss" scoped>.icon-types {width: 36px;height: 35px;margin: 2px;border: 1px solid #ccc;user-select: none;&.active {border: 2px solid blue;}}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/11080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前后端项目分离开发

问题说明&#xff1a; 开发人员同时负责前端和后端代码开发&#xff0c;分工不明确开发效率低前后端代码混合在一个工程中&#xff0c;不便于管理对开发人员要求高&#xff0c;人员招聘困难 解决方法&#xff1a; 前后端分离开发 介绍 前后端分离开发&#xff0c;就是在项…

Asp.Net 6中使用Log4Net

Asp.Net 6中使用Log4Net 1. 先新建一个ASP.NET Core空项目 2. 通过Nuget包管理器安装下面两个包 log4net Microsoft.Extensions.Logging.Log4Net.AspNetCore 3. 在项目根目录下新建log4net的配置文件log4net.config&#xff0c;并将其设置为始终复制。 <?xml version&quo…

【Spring】IOC的原理

一、 IOC 的概念 Spring 的 IOC &#xff0c;即控制反转&#xff0c;所谓控制反转 —— 本来管理业务对象&#xff08;bean&#xff09;的操作是由我们程序员去做的&#xff0c;但是有了 Spring 核心容器后&#xff0c;这些 Bean 对象的创建和管理交给我们Spring容器去做了&am…

如何选择低代码/零代码平台(最全平台总结)

来谈论这个问题之前&#xff0c;我们先来看看到底什么是低代码/零代码—— 低代码 对于“低代码”的宣传其实已经很久很广泛了&#xff0c;但是争议从来都没有停止。 忘记之前在哪里看到过一个“低代码将会取代程序员”之类的说法&#xff0c;觉得很好笑&#xff0c;看了一些…

【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动

[导读]本系列博文内容链接如下&#xff1a; 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动 在【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值一文中介绍了如何利用…

2023年深圳杯数学建模A题影响城市居民身体健康的因素分析

2023年深圳杯数学建模 A题 影响城市居民身体健康的因素分析 原题再现&#xff1a; 以心脑血管疾病、糖尿病、恶性肿瘤以及慢性阻塞性肺病为代表的慢性非传染性疾病&#xff08;以下简称慢性病&#xff09;已经成为影响我国居民身体健康的重要问题。随着人们生活方式的改变&am…

opencv-18 什么是色彩空间?

1.什么是色彩空间类型&#xff1f; 色彩空间类型&#xff0c;也称为颜色空间类型或色彩模型&#xff0c;是一种表示图像中颜色的方式。在计算机图形学和数字图像处理中&#xff0c;有许多种色彩空间类型&#xff0c;每种类型有不同的表达方式和特点。 常见的色彩空间类型包括&a…

maven

一、为什么需要使用maven 如今我们构建一个项目需要用到很多第三方的类库 &#xff0c;例如我们在开发项目中 需要引入 这些依赖jar包 一个项目Jar包的数量之多往往让我们瞠目结舌&#xff0c;并且Jar包之间的关系非常复杂&#xff0c;一个Jar包往往又会引用其他Jar包&#x…

flex 弹性布局学习

一.Flex布局: Flex是Flexible Box 缩写“弹性布局”,用来为盒状模型提供最大得灵活性。 任何一个容器都可以指定为 Flex 布局。 .box{display: flex; } 行内元素也可以使用 Flex 布局。 .box{display: inline-flex; } Webkit 内核的浏览器&#xff0c;必须加上-webkit前缀。…

微服务模式:业务服务模式

无论是单体应用还是微服务&#xff0c;构建企业应用的业务逻辑/服务在更多方面上都有相似之处而不是差异。在两种方法中&#xff0c;都包含服务、实体、仓库等类。然而&#xff0c;也会发现一些明显的区别。在本文中&#xff0c;我将试图以概念性的方式强调这些区别&#xff0c…

火山引擎VeDI最新分享:消费行业的数据飞轮从“四更”开始

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 数据飞轮&#xff0c;正在为消费行业的数字化升级提供一套全新模式。 在刚刚结束的《全链路增长&#xff1a;数据飞轮转动消费新生力》专场活动上&#xff0c;火山引…

MySQL 8.0详细安装配置教程

一. 前言 MySQL是目前最为流行的开源数据库产品&#xff0c;是完全网络化跨平台的关系型数据库系统。它起初是由瑞典MySQLAB公司开发&#xff0c;后来被Oracle公司收购&#xff0c;目前属于Oracle公司。因为开源&#xff0c;所以任何人都能从官网免费下载MySQL软件&#xff0c…

SSIS对SQL Server向Mysql数据转发表数据 (二)

1、在SQL Server数据库创建一个数据库表&#xff1a;users USE [Test1] GO/****** Object: Table [dbo].[users] Script Date: 2023/7/27 16:25:11 ******/ SET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOCREATE TABLE [dbo].[users]([id] [int] IDENTITY(1,1) NOT NUL…

一些有意思的人工智能发展状况数据

随着大型语言模型&#xff08;LLM&#xff09;的引入&#xff0c;机器学习&#xff08;ML&#xff09;和人工智能&#xff08;AI&#xff09;首次被日常开发人员所使用。这些令人感觉很神奇的应用程序&#xff0c;甚至是拥有数十亿研发支出的&#xff0c;在以前连大型科技公司几…

Vue2基础七、refnextTick自定义指令

零、文章目录 Vue2基础七、ref&nextTick&自定义指令 1、ref **作用&#xff1a;**利用 ref 和 $refs 可以用于 获取 dom 元素, 或 组件实例**特点&#xff1a;**查找范围 → 当前组件内 (更精确稳定)&#xff0c;用document.querySelect(‘.box’) 获取的是整个页面…

nacos安装与基础配置

源码 https://github.com/alibaba/nacos https://gitee.com/mirrors/Nacos 编译 git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos -Dmaven.test.skiptrue clean install -U ls -al distribution/target/// change the $version to your ac…

【数据结构】实验八:树

实验八 树 一、实验目的与要求 1&#xff09;理解树的定义&#xff1b; 2&#xff09;掌握树的存储方式及基于存储结构的基本操作实现&#xff1b; 二、 实验内容 题目一&#xff1a;采用树的双亲表示法根据输入实现以下树的存储&#xff0c;并实现输入给定结点的双亲结点…

基于罪名法务智能知识图谱(含码源):基于280万罪名预测、20W法务问答与法律资讯问答功能

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

如何构建适合自己的DevOps软件测试改进方案

​目录 DevOps成熟度模型分析 构建适合企业自身性能的测试过程改进框架 资料获取方法 根据2022年的DevOps全球调查报告显示&#xff0c;主流软件企业采用或部分采用DevOps且已获得良好成效的占比已达70%&#xff0c;DevOps俨然成为当下软件开发研究的重要方向。 测试作为软…

uni-app 微信小程序:启用组件按需注入

uni-app 微信小程序&#xff1a;启用组件按需注入 文章目录 uni-app 微信小程序&#xff1a;启用组件按需注入一、官方文档按需注入注意事项 二、HBuilder X 设置三、效果 一、官方文档 https://developers.weixin.qq.com/miniprogram/dev/framework/ability/lazyload.html 按…