首页 > 解决方案 > CSS3DRenderer 禁用缩放

问题描述

在这里查看我之前的问题,以参考我想要实现的目标

TL;DR: 我试图让 HTML 元素一起旋转,OrbitControls以使这些 HTML 元素看起来好像粘在地球上并随之移动。(想想某些国家上方 3D 地球上的地图标记)

我使用 THREE.js 几乎成功地实现了这一点CSS3DRenderer,并且能够通过纬度/经度计算让 HTML 元素粘在我的 3D 地球仪上的某个位置,并在OrbitControls使用时随地球仪旋转。

问题

我遇到的唯一问题是 HTML 元素的缩放比例与它们与相机的距离/距离成正比。通常我认为这将是帮助感觉更接近/更远的理想效果,但是缩放导致我无法正确和一致地调整我的 HTML 元素的大小,并且还导致元素内的文本和 SVG 模糊/像素化

我想要的是

我正在寻找一种关闭这种缩放的方法,以便 HTML 元素仍然以其他方式转换,但无论它们在哪里,都保持相同的大小(即 scale(1, 1, 1) 或它们的原始比例) 3D 渲染器由CSS3DRenderer.

我假设我必须为此编辑 CSS3DRenderer.js 代码,但我完全不知道从哪里开始,我在其他任何地方都找不到任何信息。

谢谢。

我的一些代码:

创建CSS3DRenderer

//CSS3D Renderer
rendererHTML = new THREE.CSS3DRenderer();
rendererHTML.setSize(WIDTH, HEIGHT);
rendererHTML.domElement.classList.add('CSS3D-container');

containerHTML = document.querySelector('.globe__container');
containerHTML.appendChild(rendererHTML.domElement);

调整大小功能(在窗口调整大小事件上调用)

HEIGHT = sizeControlElem.getBoundingClientRect().width;
WIDTH = sizeControlElem.getBoundingClientRect().width;

renderer.setSize(WIDTH, HEIGHT);
rendererHTML.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();

从 HTML 中的元素创建 CSS3DSprite 对象<li>并设置它们在地球上的初始位置

for (let key in this.locationsObject) {

    _this.locationsObject[key].coordinates = calcPosFromLatLonRad(this.locationsObject[key].lat, this.locationsObject[key].long, 300);

    let CSS3D_Object = new THREE.CSS3DSprite(_this.locationsObject[key].element);
    CSS3D_Object.position.set(_this.locationsObject[key].coordinates[0], _this.locationsObject[key].coordinates[1], _this.locationsObject[key].coordinates[2]);
    CSS3D_Object.receiveShadow = false;
    CSS3D_Object.castShadow = false;
    sceneHTML.add(CSS3D_Object);

    _this.locationsObject[key].CSS_Object = CSS3D_Object;

    console.info(CSS3D_Object);
}

您可以在此处的问题中看到更多我的代码

标签: javascriptthree.js

解决方案


停止缩放的唯一方法是使用该方法将 3D 位置投影到 2D Vector3.project()。看看下面的代码示例,我注释了 JavaScript 代码中的关键点,但快速解释是这样的:

  1. 将您想要热点的 3D 位置复制到一个新的向量中。
  2. 用于vector.project(camera)将该 3D 点转换为 2D 坐标。
  3. 将二维坐标的范围从 [-1, 1] 转换为 [0, window.width]
  4. 通过 CSS 将这些坐标应用到您的热点。

奖励:您仍然可以使用.z2D 矢量的属性来确定它是否在相机的平截头体内。

var camera, controls, scene, renderer;

// This array will hold all positions in 3D space
var posArray3D = [];

// This array will hold all hotspot DIVs
var divArray = [];

// Create temp vector to reuse on loops
var tempVec = new THREE.Vector3();

init();
animate();

function init() {

	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xcccccc );

	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	document.body.appendChild( renderer.domElement );

	camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 3000 );
	camera.position.set( 400, 200, 0 );

	// controls
	controls = new THREE.OrbitControls( camera, renderer.domElement );

	controls.enableDamping = true;
	controls.dampingFactor = 0.25;

	controls.screenSpacePanning = false;

	controls.minDistance = 100;
	controls.maxDistance = 500;

	controls.maxPolarAngle = Math.PI / 2;



	// world
	var geometry = new THREE.CylinderBufferGeometry( 0, 10, 30, 4, 1 );
	var material = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } );

	// This is where all hotspot DIVs will go
	var hotspotBox = document.getElementById("hotspotBox");

	for ( var i = 0; i < 100; i ++ ) {
		var mesh = new THREE.Mesh( geometry, material );
		mesh.position.x = Math.random() * 1600 - 800;
		mesh.position.y = 0;
		mesh.position.z = Math.random() * 1600 - 800;
		mesh.updateMatrix();
		mesh.matrixAutoUpdate = false;
		scene.add( mesh );

		// Populate array of 3D positions
		posArray3D.push(mesh.position);

		// Create 'hotspot' DIV, and place within 'hotspotBox' holder
		divArray.push(document.createElement("div"));
		divArray[i].classList.add("hotspot");
		hotspotBox.appendChild(divArray[i]);
	}


	// lights
	var light = new THREE.DirectionalLight( 0xffffff );
	light.position.set( 1, 1, 1 );
	scene.add( light );

	var light = new THREE.DirectionalLight( 0x002288 );
	light.position.set( - 1, - 1, - 1 );
	scene.add( light );

	var light = new THREE.AmbientLight( 0x222222 );
	scene.add( light );

	window.addEventListener( 'resize', onWindowResize, false );

}

function onWindowResize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	renderer.setSize( window.innerWidth, window.innerHeight );

}

// Loops through all divs and updates their positions based on the camera
function updateDivs() {
	var vectorScreen = new THREE.Vector3();

	// Loop through all positions
	for (var i = 0; i < posArray3D.length; i ++) {
		vectorScreen.copy(worldToScreen(posArray3D[i], camera));

		// Update CSS attributes of each DIV
		divArray[i].style.transform = "translate(" + vectorScreen.x + "px, " + vectorScreen.y + "px)";

		// Checks for depth, hides if it's behind the camera
		if(vectorScreen.z <= 1) {
			divArray[i].style.display = "block";
		} else {
			divArray[i].style.display = "none";
		}
	}
}

// Projects 3D coordinates into 2D space 
function worldToScreen(_position, _cam) {
	tempVec.copy(_position);

	tempVec.project(_cam);

	// Converts range from [-1, 1] to [0, windowWidth]
	tempVec.x = (   tempVec.x + 1 ) * window.innerWidth  / 2;
	tempVec.y = ( - tempVec.y + 1 ) * window.innerHeight / 2;

	return tempVec;
}

function animate() {

	requestAnimationFrame( animate );

	controls.update();
	updateDivs();

	render();

}

function render() {

	renderer.render( scene, camera );

}
body {
	color: #000;
	font-family:Monospace;
	font-size:13px;
	text-align:center;
	font-weight: bold;

	background-color: #fff;
	margin: 0px;
	overflow: hidden;
}
/*hotspotBox holds all .hotspots It's placed on top of WebGL canvas*/
#hotspotBox{
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	border: 1px dashed #f90;
	pointer-events: none;
}
/*100 hotspots to be placed within #hotspotBox */
.hotspot {
	background: #f90;
	width: 10px;
	height: 10px;
	border-radius: 5px;
	position: absolute;
	cursor: pointer;
	pointer-events: auto;
}
<div id="hotspotBox"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>


推荐阅读