关于代码
基于threejs的全景照片示例改了一下,并做简单封装。threejs的全景示例有cube和equirectangular两种,这里因为全景图片是一整张而不是多张图片拼合,故使用equirectangular。
关于全景图片
全景照片的来源是mapillary,访问需要梯子。 通过mapillary的#api爬取了照片文件和信息,相关教程有空发。 重点是这两个字段,一个是点位坐标一个是初始角度,可以用于照片的拍摄点定位和全景展示初始镜头朝向 图片本身是jpg,注意不是多张图片拼合的全景,而是一整张照片。
show me the code
使用TS编写,js自行更改
import * as THREE from "three";
const Panorama = {
scene: new THREE.Scene(),
updateMesh: function (imgPath: string) {
this.resetParams();
const mesh = this.scene.getObjectByName("pano") as THREE.Mesh;
const material = mesh.material as THREE.MeshBasicMaterial;
material.map = null;
const texture = new THREE.TextureLoader().load(imgPath);
material.map = texture;
},
init: function (
container: HTMLElement,
imgPath: string,
compassAngle: number
) {
const _t = this;
const scene = this.scene;
const camera = new THREE.PerspectiveCamera(
95,
container.offsetWidth / container.offsetHeight,
1,
1100
);
const texture = new THREE.TextureLoader().load(imgPath);
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.SphereGeometry(500, 60, 40);
geometry.scale(-1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
mesh.name = "pano";
scene.add(mesh);
const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild(renderer.domElement);
container.style.touchAction = "none";
container.addEventListener("pointerdown", onPointerDown);
container.addEventListener("wheel", onDocumentMouseWheel);
container.addEventListener("resize", onWindowResize);
function onWindowResize() {
camera.aspect = container.offsetWidth / container.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.offsetWidth, container.offsetHeight);
}
function onPointerDown(event: PointerEvent) {
if (event.isPrimary === false) return;
_t.isUserInteracting = true;
_t.onPointerDownMouseX = event.clientX;
_t.onPointerDownMouseY = event.clientY;
_t.onPointerDownLon = _t.lon;
_t.onPointerDownLat = _t.lat;
container.addEventListener("pointermove", onPointerMove);
container.addEventListener("pointerup", onPointerUp);
}
function onPointerMove(event: PointerEvent) {
if (event.isPrimary === false) return;
_t.lon =
(_t.onPointerDownMouseX - event.clientX) * 0.1 + _t.onPointerDownLon;
_t.lat =
(_t.onPointerDownMouseY - event.clientY) * 0.1 + _t.onPointerDownLat;
}
function onPointerUp(event: PointerEvent) {
if (event.isPrimary === false) return;
_t.isUserInteracting = false;
container.removeEventListener("pointermove", onPointerMove);
container.removeEventListener("pointerup", onPointerUp);
}
function onDocumentMouseWheel(event: WheelEvent) {
const fov = camera.fov + event.deltaY * 0.05;
camera.fov = THREE.MathUtils.clamp(fov, 10, 75);
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
update();
}
function update() {
_t.lat = Math.max(-85, Math.min(85, _t.lat));
_t.phi = THREE.MathUtils.degToRad(compassAngle - _t.lat);
_t.theta = THREE.MathUtils.degToRad(_t.lon);
const x = 500 * Math.sin(_t.phi) * Math.cos(_t.theta);
const y = 500 * Math.cos(_t.phi);
const z = 500 * Math.sin(_t.phi) * Math.sin(_t.theta);
camera.lookAt(x, y, z);
renderer.render(scene, camera);
}
animate();
},
resetParams: function () {
this.isUserInteracting = false;
this.onPointerDownMouseX = 0;
this.onPointerDownMouseY = 0;
this.lon = 0;
this.onPointerDownLon = 0;
this.lat = 0;
this.onPointerDownLat = 0;
this.phi = 0;
this.theta = 0;
},
isUserInteracting: false,
onPointerDownMouseX: 0,
onPointerDownMouseY: 0,
lon: 0,
onPointerDownLon: 0,
lat: 0,
onPointerDownLat: 0,
phi: 0,
theta: 0,
};
export { Panorama };
使用示例
找个合适的地方放一个容器
<div id="pano-container"></div>
引入刚才的全景工具(ThreeExt.ts)
import { Panorama as PanoFn } from "./ThreeExt.ts";
为了节约资源,只有第一次加载的时候会初始化容器,后面更换照片会调用updateMesh方法。
初始化
const container = document.getElementById(
"pano-container"
) as HTMLDivElement;
PanoFn.init(container, 你的照片路径, 你的照片初始角度);
刷新照片
PanoFn.updateMesh(新的照片路径);
清除
先清空场景,再清空容器元素
PanoFn.scene.clear();
const container = document.getElementById(
"pano-container"
) as HTMLDivElement;
container.innerHTML = "";
|