javascript - 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);
}
您可以在此处的问题中看到更多我的代码
解决方案
停止缩放的唯一方法是使用该方法将 3D 位置投影到 2D Vector3.project()
。看看下面的代码示例,我注释了 JavaScript 代码中的关键点,但快速解释是这样的:
- 将您想要热点的 3D 位置复制到一个新的向量中。
- 用于
vector.project(camera)
将该 3D 点转换为 2D 坐标。 - 将二维坐标的范围从 [-1, 1] 转换为 [0, window.width]
- 通过 CSS 将这些坐标应用到您的热点。
奖励:您仍然可以使用.z
2D 矢量的属性来确定它是否在相机的平截头体内。
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>
推荐阅读
- angular - ng-zorro NzDrawerService nzOffsetX 动态
- c# - 不断重复类构造函数 - 类对象似乎正在实例化自己
- excel - 将用户窗体与具有多个显示的活动单元格对齐
- python - 输入所有值后如何打印是/否?
- typescript - 将值分配给属性时出错
- python - Python:如何检查字符串是否包含列表中的元素并带出该元素?
- angular - 使用外部 API 调用更新 Angular Universal 中的元标记
- qt - Qt:如何找到路径的最近的现有祖先,它本身可能存在也可能不存在
- vuex - 从 feathers-vuex 获取所有数据
- c - 释放 malloc 的内存会导致其他 malloc 的内存成为垃圾