ts/public/GV/thirdParty/Threejs-109/examples/js/renderers/CSS3DRenderer.js
2024-12-09 14:44:52 +08:00

351 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
* @author mrdoob / http://mrdoob.com/
* @author yomotsu / https://yomotsu.net/
*/
THREE.CSS3DObject = function (element) {
THREE.Object3D.call(this);
this.element = element;
this.element.style.position = 'absolute';
this.addEventListener('removed', function () {
if (this.element.parentNode !== null) {
this.element.parentNode.removeChild(this.element);
}
});
};
THREE.CSS3DObject.prototype = Object.create(THREE.Object3D.prototype);
THREE.CSS3DObject.prototype.constructor = THREE.CSS3DObject;
THREE.CSS3DSprite = function (element) {
THREE.CSS3DObject.call(this, element);
};
THREE.CSS3DSprite.prototype = Object.create(THREE.CSS3DObject.prototype);
THREE.CSS3DSprite.prototype.constructor = THREE.CSS3DSprite;
//
THREE.CSS3DRenderer = function () {
console.log('THREE.CSS3DRenderer', THREE.REVISION);
var _width, _height;
var _widthHalf, _heightHalf;
var matrix = new THREE.Matrix4();
// 缓存
var cache = {
camera: { fov: 0, style: '' },
objects: new WeakMap()
};
var domElement = document.createElement('div');
domElement.style.overflow = 'hidden';
this.domElement = domElement;
// 该节点下的子节点都具有3D效果
var cameraElement = document.createElement('div');
// 该属性设置在父级元素中它的子级元素具有3d效果
cameraElement.style.WebkitTransformStyle = 'preserve-3d';
cameraElement.style.transformStyle = 'preserve-3d';
domElement.appendChild(cameraElement);
var isIE = /Trident/i.test(navigator.userAgent);
this.getSize = function () {
return {
width: _width,
height: _height
};
};
this.setSize = function (width, height) {
_width = width;
_height = height;
_widthHalf = _width / 2;
_heightHalf = _height / 2;
domElement.style.width = width + 'px';
domElement.style.height = height + 'px';
cameraElement.style.width = width + 'px';
cameraElement.style.height = height + 'px';
};
function epsilon(value) {
return Math.abs(value) < 1e-10 ? 0 : value;
}
function getCameraCSSMatrix(matrix) {
var elements = matrix.elements;
return 'matrix3d(' +
epsilon(elements[0]) + ',' +
epsilon(- elements[1]) + ',' +
epsilon(elements[2]) + ',' +
epsilon(elements[3]) + ',' +
epsilon(elements[4]) + ',' +
epsilon(- elements[5]) + ',' +
epsilon(elements[6]) + ',' +
epsilon(elements[7]) + ',' +
epsilon(elements[8]) + ',' +
epsilon(- elements[9]) + ',' +
epsilon(elements[10]) + ',' +
epsilon(elements[11]) + ',' +
epsilon(elements[12]) + ',' +
epsilon(- elements[13]) + ',' +
epsilon(elements[14]) + ',' +
epsilon(elements[15]) +
')';
}
function getObjectCSSMatrix(matrix, cameraCSSMatrix, object) {
var elements = matrix.elements;
var matrix3d = 'matrix3d(' +
epsilon(elements[0]) + ',' +
epsilon(elements[1]) + ',' +
epsilon(elements[2]) + ',' +
epsilon(elements[3]) + ',' +
epsilon(- elements[4]) + ',' +
epsilon(- elements[5]) + ',' +
epsilon(- elements[6]) + ',' +
epsilon(- elements[7]) + ',' +
epsilon(elements[8]) + ',' +
epsilon(elements[9]) + ',' +
epsilon(elements[10]) + ',' +
epsilon(elements[11]) + ',' +
epsilon(elements[12]) + ',' +
epsilon(elements[13]) + ',' +
epsilon(elements[14]) + ',' +
epsilon(elements[15]) +
')';
if (isIE) {
return 'translate(-50%,-50%)' +
'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)' +
cameraCSSMatrix +
matrix3d;
}
if (object.userData.anchor) {
return 'translate(-' + object.userData.anchor[0] * 100 + '%,-' + object.userData.anchor[1] * 100 + '%)' + matrix3d;
} else {
return 'translate(-50%,-50%)' + matrix3d;
}
}
function renderObject(object, camera, cameraCSSMatrix) {
if (object instanceof THREE.CSS3DObject) {
var style;
if (object instanceof THREE.CSS3DSprite) {
// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/
matrix.copy(camera.matrixWorldInverse);
matrix.transpose();
matrix.copyPosition(object.matrixWorld);
matrix.scale(object.scale);
matrix.elements[3] = 0;
matrix.elements[7] = 0;
matrix.elements[11] = 0;
matrix.elements[15] = 1;
style = getObjectCSSMatrix(matrix, cameraCSSMatrix, object);
} else {
style = getObjectCSSMatrix(object.matrixWorld, cameraCSSMatrix, object);
}
var element = object.element;
var cachedObject = cache.objects.get(object);
if (cachedObject === undefined || cachedObject.style !== style) {
element.style.WebkitTransform = style;
element.style.transform = style;
if (object.userData.anchor) {
element.style.transformOrigin = object.userData.anchor[0] * 100 + '% ' + object.userData.anchor[1] * 100 + '%';// 有时间考虑一下为什么?
}
var objectData = { style: style };
if (isIE) {
objectData.distanceToCameraSquared = getDistanceToSquared(camera, object);
}
cache.objects.set(object, objectData);
}
if (element.parentNode !== cameraElement) {
cameraElement.appendChild(element);
}
}
for (var i = 0, l = object.children.length; i < l; i++) {
renderObject(object.children[i], camera, cameraCSSMatrix);
}
}
var getDistanceToSquared = function () {
var a = new THREE.Vector3();
var b = new THREE.Vector3();
return function (object1, object2) {
a.setFromMatrixPosition(object1.matrixWorld);
b.setFromMatrixPosition(object2.matrixWorld);
return a.distanceToSquared(b);
};
}();
function filterAndFlatten(scene) {
var result = [];
scene.traverse(function (object) {
if (object instanceof THREE.CSS3DObject) result.push(object);
});
return result;
}
function zOrder(scene) {
var sorted = filterAndFlatten(scene).sort(function (a, b) {
var distanceA = cache.objects.get(a).distanceToCameraSquared;
var distanceB = cache.objects.get(b).distanceToCameraSquared;
return distanceA - distanceB;
});
var zMax = sorted.length;
for (var i = 0, l = sorted.length; i < l; i++) {
sorted[i].element.style.zIndex = zMax - i;
}
}
this.render = function (scene, camera) {
// 计算设置fov
var fov = camera.projectionMatrix.elements[5] * _heightHalf;
if (cache.camera.fov !== fov) {
if (camera.isPerspectiveCamera) {
domElement.style.WebkitPerspective = fov + 'px';
domElement.style.perspective = fov + 'px';
}
cache.camera.fov = fov;
}
// 更新场景树矩阵
scene.updateMatrixWorld();
// 更新相机矩阵
if (camera.parent === null) camera.updateMatrixWorld();
if (camera.isOrthographicCamera) {
var tx = - (camera.right + camera.left) / 2;
var ty = (camera.top + camera.bottom) / 2;
}
// CSS 3D 相机矩阵与ThreeJS相机联动
var cameraCSSMatrix = camera.isOrthographicCamera ?
'scale(' + fov + ')' + 'translate(' + epsilon(tx) + 'px,' + epsilon(ty) + 'px)' + getCameraCSSMatrix(camera.matrixWorldInverse) :
'translateZ(' + fov + 'px)' + getCameraCSSMatrix(camera.matrixWorldInverse);
var style = cameraCSSMatrix +
'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)';
if (cache.camera.style !== style && !isIE) {
cameraElement.style.WebkitTransform = style;
cameraElement.style.transform = style;
cache.camera.style = style;
}
renderObject(scene, camera, cameraCSSMatrix);
if (isIE) {
// IE10 and 11 does not support 'preserve-3d'.
// Thus, z-order in 3D will not work.
// We have to calc z-order manually and set CSS z-index for IE.
// FYI: z-index can't handle object intersection
zOrder(scene);
}
};
};