基于threejs,完成一个简单的全景图播放器

article/2025/8/22 3:33:16

直接上代码

CameraControls.js相机控制器

import * as THREE from 'three';function CameraControls(object, domElement, cb, update) {this.object = object;this.domElement = domElement !== undefined ? domElement : document;this.enabled = true;this.lookSpeed = 0.1;this.activeLook = true;this.cameraFov = object.fov;this.minPolarAngle = 0; // radiansthis.maxPolarAngle = Math.PI; // radiansthis.minAzimuthAngle = -Infinity; // radiansthis.maxAzimuthAngle = Infinity; // radiansconst lookDirection = new THREE.Vector3();const target = new THREE.Vector3();const near = 40; //最小范围const far = object.fov; //最大范围// 只要无状态和旋转的两个状态。const STATE = {NONE: -1,ROTATE: 0,};let state = STATE.NONE;const rotateStart = new THREE.Vector2();const rotateEnd = new THREE.Vector2();const rotateDelta = new THREE.Vector2();const spherical = new THREE.Spherical();const sphericalDelta = new THREE.Spherical(); // 球面function rotateLeft(angle) {sphericalDelta.theta -= angle;}function rotateUp(angle) {sphericalDelta.phi += angle;}this.handleMouseDownRotate = function (event) {rotateStart.set(event.clientX, event.clientY);};this.handleMouseMoveRotate = function (event) {rotateEnd.set(event.clientX, event.clientY);rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(this.lookSpeed);const element = this.domElement === document ? this.domElement.body : this.domElement;rotateLeft((2 * Math.PI * rotateDelta.x) / element.clientHeight); // yes, heightrotateUp((2 * Math.PI * rotateDelta.y) / element.clientHeight);rotateStart.copy(rotateEnd);if (cb) cb();this.update();};//鼠标按下执行的事件this.onMouseDown = function (event) {if (this.enabled === false) return;event.preventDefault();event.stopPropagation();if (this.domElement !== document) {this.domElement.focus();} else {window.focus();}if (this.activeLook && event.button === 0) {this.handleMouseDownRotate(event);state = STATE.ROTATE;}if (state !== STATE.NONE) {document.addEventListener('mousemove', _onMouseMove, false);document.addEventListener('mouseup', _onMouseUp, false);}};this.onMouseUp = function (event) {if (this.enabled === false) return;event.preventDefault();event.stopPropagation();document.removeEventListener('mousemove', _onMouseMove, false);document.removeEventListener('mouseup', _onMouseUp, false);state = STATE.NONE;};this.onMouseMove = function (event) {if (this.enabled === false) return;event.preventDefault();event.stopPropagation();if (state === STATE.ROTATE) {this.handleMouseMoveRotate(event);}};this.onMouseWheel = function (event) {if (this.enabled === false) return;event.preventDefault();event.stopPropagation();if (event.wheelDelta) {if (event.wheelDelta < 0) {this.cameraFov -= near < this.cameraFov ? 2 : 0;}if (event.wheelDelta > 0) {this.cameraFov += this.cameraFov < far ? 2 : 0;}} else if (event.detail) {if (event.detail < 0) {this.cameraFov -= near < this.cameraFov ? 2 : 0;}if (event.detail > 0) {this.cameraFov += this.cameraFov < far ? 2 : 0;}}this.object.fov = this.cameraFov;this.object.updateProjectionMatrix();};this.lookAt = function (x, y, z) {if (x.isVector3) {target.copy(x);} else {target.set(x, y, z);}this.object.lookAt(target);setOrientation(this);return this;};this.update = (function () {const targetPosition = new THREE.Vector3();return function update() {if (this.enabled === false) return;spherical.theta -= sphericalDelta.theta;spherical.phi -= sphericalDelta.phi;spherical.theta = Math.max(this.minAzimuthAngle,Math.min(this.maxAzimuthAngle, spherical.theta));spherical.phi = Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle, spherical.phi));spherical.makeSafe();const { position } = this.object;targetPosition.setFromSphericalCoords(1, spherical.phi, spherical.theta).add(position);this.object.lookAt(targetPosition);this.object.target = targetPosition;sphericalDelta.set(0, 0, 0);return false;};})();function contextmenu(event) {event.preventDefault();}this.dispose = function () {this.domElement.removeEventListener('contextmenu', contextmenu, false);this.domElement.removeEventListener('mousedown', _onMouseDown, false);this.domElement.removeEventListener('mousewheel', _onMouseWheel, false);document.removeEventListener('mousemove', _onMouseMove, false);document.removeEventListener('mouseup', _onMouseUp, false);};const _onMouseMove = bind(this, this.onMouseMove);const _onMouseDown = bind(this, this.onMouseDown);const _onMouseUp = bind(this, this.onMouseUp);const _onMouseWheel = bind(this, this.onMouseWheel);this.domElement.addEventListener('contextmenu', contextmenu, false);this.domElement.addEventListener('mousedown', _onMouseDown, false);this.domElement.addEventListener('mousewheel', _onMouseWheel, false);document.addEventListener('mousemove', _onMouseMove, false);document.addEventListener('mouseup', _onMouseUp, false);function bind(scope, fn) {return function () {fn.apply(scope, arguments);};}function setOrientation(controls) {const { quaternion } = controls.object;lookDirection.set(0, 0, -1).applyQuaternion(quaternion);spherical.setFromVector3(lookDirection);}setOrientation(this);
}THREE.CameraControls = CameraControls;

Player.js 最终导出的api

import * as THREE from 'three';import './CameraControls';class Player {scene = null;camera = null;renderer = null;controls = null;parentDom = null;panoramaMesh = null;clientWidth = 0;clientHeight = 0;cameraFov = 80;T0 = new Date();constructor(config) {const { parentDom, cameraFov } = config;if (cameraFov) {if (cameraFov <= 40) {this.cameraFov = 40;} else if (cameraFov >= 120) {this.cameraFov = 120;} else {this.cameraFov = cameraFov;}} else {this.cameraFov = 80;}this.parentDom = parentDom || document.body;this.clientWidth = this.parentDom.clientWidth;this.clientHeight = this.parentDom.clientHeight;this.init();}init() {this.initScene();this.initRenderer();this.initCamera();this.initLight();this.initControls();this.animate();}initScene() {this.scene = new THREE.Scene();}initControls() {this.controls = new THREE.CameraControls(this.camera,this.renderer.domElement);this.controls.lookSpeed = 0.2; // 鼠标移动查看的速度// this.controls.minPolarAngle = Math.PI / 6; // radians// this.controls.maxPolarAngle = (Math.PI / 6) * 5; // radians}updateFov() {console.log(1);}initCamera() {this.camera = new THREE.PerspectiveCamera(this.cameraFov,this.clientWidth / this.clientHeight,1,10000);this.camera.position.x = 0;this.camera.position.y = 0;this.camera.position.z = 0;}initRenderer() {this.renderer = new THREE.WebGLRenderer();this.renderer.setSize(this.clientWidth, this.clientHeight);this.parentDom.appendChild(this.renderer.domElement);}initLight() {const ambientLight = new THREE.AmbientLight(0xffffff);ambientLight.name = 'ambientLight';const directionalLight = new THREE.DirectionalLight(0xffffff);// 把平行光放在y轴正方向上的无穷远处directionalLight.position.set(0, 1, 0);directionalLight.name = 'directionalLight';// 把平行光加到场景中this.scene.add(ambientLight);this.scene.add(directionalLight);}animate = () => {requestAnimationFrame(this.animate);this.renderer && this.renderer.render(this.scene, this.camera);};createPanorama = imageUrl => {const geometry = new THREE.SphereGeometry(100, 64, 32);geometry.scale(-1, 1, 1);const material = new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load(imageUrl), // 加载一整张纹理图片});this.panoramaMesh = new THREE.Mesh(geometry, material);this.scene.add(this.panoramaMesh);};changePanorama = imageUrl => {if (!this.panoramaMesh) {this.createPanorama(imageUrl);return;}this.panoramaMesh.material.map.dispose();this.panoramaMesh.material.dispose();this.panoramaMesh.material.map = new THREE.TextureLoader().load(imageUrl);};clearRenderer = () => {this.renderer.clear();this.renderer.dispose();this.renderer.forceContextLoss();this.renderer.domElement.remove();this.renderer = null;};dispose() {this.controls.dispose();this.clearRenderer();this.scene.children.forEach(c => c.dispose && c.dispose());this.scene.clear();}
}export default Player;

使用方法

//<div id="app"></div>
const parentDom = document.getElementById('app');
const app = new Player({parentDom});
app.createPanorama('https://img.zcool.cn/community/019d425a5c4793a8012113c7dac382.jpg@1280w_1l_2o_100sh.jpg')

效果图


http://chatgpt.dhexx.cn/article/Q2XhM4VG.shtml

相关文章

使用SceneKit编写VR全景播放器

最近用SceneKit做了全景看房的功能&#xff0c;现总结下如何实现的。 先看下最终的效果&#xff1a; gif1.gif VR图片全景播放器有以下功能: 360度手势滑动&#xff0c;缩放陀螺仪分屏&#xff08;VR眼镜&#xff09;热点hotpot头控/eyepick 手势滑动&#xff0c;缩放&#…

AVProVideo☀️五、播放全景视频

🎊 商务合作:https://skode.cn/file/businesscard/wechat.jpg 🎥 本文由 星河造梦坊公司官方 原创! 🏅 如果你有技术问题或项目开发,都可以加上方的联系方式,和我聊一聊你的故事🧡 文章目录 🟥 360球形全景视频🟧 360立方体全景视频🟨 360天空盒全景视频🟩…

[OpenGL]从零开始写一个Android平台下的全景视频播放器——3.1 全景视频是如何实现的

Github项目地址 为了方便没有准备好梯子的同学&#xff0c;我把项目在CSDN上打包下载&#xff0c;不过更新会慢一些 回到目录 恭喜Martin同学获得由CSDN颁发的“更新慢慢慢”荣誉称号 全景视频有很多种类&#xff0c;例如Sphere全景&#xff0c;Skybox&#xff08;Cubemap&…

VR全景播放器 AvPro Video

最近项目需要&#xff0c;使用Unity开发VR全景播放器&#xff0c;包括PC端和VR一体机端&#xff08;Android&#xff09;。Unity5.6开始支持VideoPlayer&#xff0c;使用自带的VideoPlayer&#xff0c;很顺利把播放器完成了&#xff0c;使用了很长时间&#xff0c;一直没什么性…

FFmpeg 开发(07):FFmpeg + OpenGLES 实现 3D 全景播放器

该文章首发于微信公众号:字节流动 FFmpeg 开发系列连载: FFmpeg 开发(01):FFmpeg 编译和集成 FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放 FFmpeg 开发(03):FFmpeg + OpenSLES 实现音频解码播放 FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放 FFm…

html全景直播播放器,Insta360 Player(全景视频播放器) V2.3.6 官方版

Insta360 Player是一款性能强劲且专业的全景视频播放器&#xff0c;它支持播放Insta360全景相机拍摄的全景视频和图片&#xff0c;并且支持本地视频的播放&#xff0c;操作非常的简单&#xff0c;有需要的用户可以下载来使用&#xff0c;此软件支持播放 Insta360全景相机产生的…

全景播放器,免安装支持全景视频

全景图片播放器&#xff0c;同时支持全景视频&#xff0c;可直接拖入页面查看。免安装全景播放器,文件不要大于20M Hpano 3D全景播放器是一款免安装可以720度互动浏览的全景图片查看器&#xff0c;通过拖球形全景图片或视频文件进行预览&#xff0c;720度的全方位图片查看器让你…

android 简单的exoplayer全景播放器

全景播放器网上一搜也是一堆一堆的&#xff0c;还有google推出的vrsdk&#xff0c;所以也没啥好说的&#xff0c;就简单记录一下 实现全景主要用到的还是opengles&#xff0c;只要用的开源播放器有setSurface(Surface surface)这个函数&#xff0c;就可以不改播放器源码实现 …

全景播放器

在github上发现的&#xff0c;绿色免安装&#xff0c;完全免费&#xff0c;只有一个界面三个按键&#xff0c;超级简单。里面带来几个全景视频和图片的demo&#xff0c;想体验全景的可以直接打开看看。想换片的时候按键盘的空格键既可以返回主界面。视角查看用鼠标左键点击拖动…

OpenGL ES_手把手教你打造VR全景播放器

OpenGL ES _ 入门_01OpenGL ES _ 入门_02OpenGL ES _ 入门_03OpenGL ES _ 入门_04OpenGL ES _ 入门_05OpenGL ES _ 入门练习_01OpenGL ES _ 入门练习_02OpenGL ES _ 入门练习_03OpenGL ES _ 入门练习_04OpenGL ES _ 入门练习_05OpenGL ES _ 入门练习_06OpenGL ES _ 着色器 _ 介…

播放全景视频【一】:用unity Video Player视频播放器来播放360全景视频

先上图为敬 本文测试环境&#xff1a; Win10 Unity 2020.3.40 Pico G2 4k VR一体机 一、使用Video Player心理负担比较小 使用Unity自带的【视频播放器&#xff08;Video Player&#xff09;】来播放360全景视频的【好处】&#xff1a; 1、控制逻辑与2D视频相同&#xff0c…

利用FFmpeg和OpenGL ES 实现 3D 全景播放器

前言 我们已经利用 FFmpeg OpenGLES OpenSLES 实现了一个多媒体播放器&#xff0c;本文将基于此播放器实现一个酷炫的 3D 全景播放器。 全景播放器原理 全景视频是由多台摄像机在一个位置同时向四面八方拍摄&#xff0c;最后经过后期拼接处理生成的。 用普通的多媒体播放器播…

航空客运订票系统(C语言,软件用的DEV)

这两天整理之前的作业代码&#xff0c;把自己一点一点敲出来的系统又看了一下&#xff0c;挑几个发出来供大家参考。想要源码、报告可以找我啦&#xff0c;代码的注释之前写的都是非常详细的&#xff01; 但是不是无偿的啦&#xff08;不坑&#xff0c;一杯奶茶喽&#xff0c;不…

数据结构_航空客运订票系统(C实现)

文章目录 总述代码粗糙版修理版 还可以修正的点:余票不足时仍然提示还剩下几张(而不是直接拒绝该用户的订票操作)对于购票者的id 不单单是说约定一个可以不重复的主键(命名规则),而且还要辅以必要的检查违约功能 总述 在这里插入代码片 1&#xff0e; 问题描述&#xff1a;(题…

航空机票订票系统

项目介绍 主要功能是使订票系统可以录入航班情况&#xff0c;查询某个航线的情况、办理订票、办理退票、修改航班信息、查询订票信息等。完成此系统&#xff0c;需要综合运用数据结构课程中学到的几种典型数据结构&#xff0c;以及程序设计语言&#xff08;C语言&#xff09;&…

【数据结构应用】航空客运订票系统

目录 前言 一、作业要求介绍 二、各个函数的实现 1.头文件总结需要的功能 &#xff08;1&#xff09;结构体的定义 &#xff08;2&#xff09;各个功能的函数 2.各个函数的具体实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;打印航班信息表 &#xff08;4&…

Java实现航空机票订票系统

1、要求&#xff1a; &#xff08;1&#xff09;设计每条航线所涉及的信息&#xff0c;如终点站名、航班号、飞机号、飞机周日&#xff08;星期几&#xff09;、乘员定额、余票量、订定票的客户名单&#xff08;包括姓名、订票量、舱位等级1&#xff0c;2或3&#xff09;等&…

c语言航空订票系统程序设计,C语言航空订票系统

C语言航空订票系统 这 是 一 篇 用 C 语 言 编 写 的 航 空 订 票 系 统 的 论 文 。 该 系 统 使 用 的 是十 字 链 表 结 构 &#xff0c; 包 含 有 订 票 &#xff0c; 退 票 &#xff0c; 录 入 航 班 信 息 &#xff0c; 查 询 航 班 余 票 &#xff0c; 查询 个 人 订 票…

航空订票系统C++课程设计

航空订票系统 项目实践完整源码 前言一、功能演示二、代码总结 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 编写程序模拟航空订票系统&#xff0c;要求实现以下功能&#xff1a; ① 允许增、删、改航班信息&#…

数据结构课设-航空客运订票系统(C语言实现)

航空客运订票系统&#xff08;C语言实现&#xff09; 系统框架已完成功能用户功能管理员功能其他 运行结果管理员添加航班客户订票添加候补客户退票 代码 系统框架 已完成功能 用户功能 查询航线&#xff1a;根据旅客提出的终点站名输出航班的信息。订票业务&#xff1a;根据…