以下是 jQuery 3D自由立体旋转js特效代码 的示例演示效果:
部分效果截图:
HTML代码(index.html):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>3D自由立体旋转</title>
<style>
body {
background-color: #ffffff;
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script type="text/javascript" src="js/three.min.js"></script>
<script type="text/javascript" src="js/TrackballControls.js"></script>
<script type="text/javascript" src="js/CSS3DRenderer.js"></script>
<script>
var camera, scene, renderer;
var geometry, material, mesh;
var scene2, renderer2;
var controls;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(500, 100, 100);
controls = new THREE.TrackballControls(camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = false;
controls.dynamicDampingFactor = 0.3;
controls.keys = [70, 90, 75];
scene = new THREE.Scene();
geometry = new THREE.CubeGeometry(200, 200, 200);
material = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true, wireframeLinewidth: 1 });
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//
scene2 = new THREE.Scene();
for (var i = 0; i < 20; i++) {
var element = document.createElement('div');
element.style.width = '100px';
element.style.height = '100px';
element.style.background = new THREE.Color(Math.random() * 0xffffff).getStyle();
var object = new THREE.CSS3DObject(element);
object.position.x = Math.random() * 200 - 100;
object.position.y = Math.random() * 200 - 100;
object.position.z = Math.random() * 200 - 100;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
scene2.add(object);
}
//
renderer2 = new THREE.CSS3DRenderer();
renderer2.setSize(window.innerWidth, window.innerHeight);
renderer2.domElement.style.position = 'absolute';
renderer2.domElement.style.top = 0;
document.body.appendChild(renderer2.domElement);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
renderer2.render(scene2, camera);
}
</script>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
</body>
</html>
JS代码(CSS3DRenderer.js):
/** * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs * @author mrdoob / http://mrdoob.com/ */
THREE.CSS3DObject = function ( element ){
THREE.Object3D.call( this );
this.element = element;
this.element.style.position = "absolute";
this.element.style.WebkitTransformStyle = 'preserve-3d';
this.element.style.MozTransformStyle = 'preserve-3d';
this.element.style.oTransformStyle = 'preserve-3d';
this.element.style.transformStyle = 'preserve-3d';
}
;
THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );
THREE.CSS3DSprite = function ( element ){
THREE.CSS3DObject.call( this,element );
}
;
THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );
//THREE.CSS3DRenderer = function (){
console.log( 'THREE.CSS3DRenderer',THREE.REVISION );
var _width,_height;
var _widthHalf,_heightHalf;
var _projector = new THREE.Projector();
var _tmpMatrix = new THREE.Matrix4();
this.domElement = document.createElement( 'div' );
this.domElement.style.overflow = 'hidden';
this.domElement.style.WebkitTransformStyle = 'preserve-3d';
this.domElement.style.WebkitPerspectiveOrigin = '50% 50%';
this.domElement.style.MozTransformStyle = 'preserve-3d';
this.domElement.style.MozPerspectiveOrigin = '50% 50%';
this.domElement.style.oTransformStyle = 'preserve-3d';
this.domElement.style.oPerspectiveOrigin = '50% 50%';
this.domElement.style.transformStyle = 'preserve-3d';
this.domElement.style.perspectiveOrigin = '50% 50%';
// TODO:Shouldn't it be possible to remove cameraElement?this.cameraElement = document.createElement( 'div' );
this.cameraElement.style.WebkitTransformStyle = 'preserve-3d';
this.cameraElement.style.MozTransformStyle = 'preserve-3d';
this.cameraElement.style.oTransformStyle = 'preserve-3d';
this.cameraElement.style.transformStyle = 'preserve-3d';
this.domElement.appendChild( this.cameraElement );
this.setSize = function ( width,height ){
_width = width;
_height = height;
_widthHalf = _width / 2;
_heightHalf = _height / 2;
this.domElement.style.width = width + 'px';
this.domElement.style.height = height + 'px';
this.cameraElement.style.width = width + 'px';
this.cameraElement.style.height = height + 'px';
}
;
var epsilon = function ( value ){
return Math.abs( value ) < 0.000001 ? 0:value;
}
;
var getCameraCSSMatrix = function ( 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 ] ) +')';
}
var getObjectCSSMatrix = function ( matrix ){
var elements = matrix.elements;
return 'translate3d(-50%,-50%,0) 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 ] ) +')';
}
this.render = function ( scene,camera ){
var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * _height;
this.domElement.style.WebkitPerspective = fov + "px";
this.domElement.style.MozPerspective = fov + "px";
this.domElement.style.oPerspective = fov + "px";
this.domElement.style.perspective = fov + "px";
var objects = _projector.projectScene( scene,camera,false ).objects;
var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) + " translate3d(" + _widthHalf + "px," + _heightHalf + "px,0)";
this.cameraElement.style.WebkitTransform = style;
this.cameraElement.style.MozTransform = style;
this.cameraElement.style.oTransform = style;
this.cameraElement.style.transform = style;
for ( var i = 0,il = objects.length;
i < il;
i ++ ){
var object = objects[ i ].object;
if ( object instanceof THREE.CSS3DObject ){
var element = object.element;
if ( object instanceof THREE.CSS3DSprite ){
// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/_tmpMatrix.copy( camera.matrixWorldInverse );
_tmpMatrix.transpose();
_tmpMatrix.extractPosition( object.matrixWorld );
_tmpMatrix.scale( object.scale );
_tmpMatrix.elements[ 3 ] = 0;
_tmpMatrix.elements[ 7 ] = 0;
_tmpMatrix.elements[ 11 ] = 0;
_tmpMatrix.elements[ 15 ] = 1;
style = getObjectCSSMatrix( _tmpMatrix );
}
else{
style = getObjectCSSMatrix( object.matrixWorld );
}
/*element.style.WebkitBackfaceVisibility = 'hidden';
element.style.MozBackfaceVisibility = 'hidden';
element.style.oBackfaceVisibility = 'hidden';
element.style.backfaceVisibility = 'hidden';
*/
element.style.WebkitTransform = style;
element.style.MozTransform = style;
element.style.oTransform = style;
element.style.transform = style;
if ( element.parentNode !== this.cameraElement ){
this.cameraElement.appendChild( element );
}
}
}
}
;
}
;
JS代码(TrackballControls.js):
/** * @author Eberhard Graether / http://egraether.com/ */
THREE.TrackballControls = function ( object,domElement ){
THREE.EventDispatcher.call( this );
var _this = this;
var STATE ={
NONE:-1,ROTATE:0,ZOOM:1,PAN:2,TOUCH_ROTATE:3,TOUCH_ZOOM:4,TOUCH_PAN:5}
;
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement:document;
// APIthis.enabled = true;
this.screen ={
width:0,height:0,offsetLeft:0,offsetTop:0}
;
this.radius = ( this.screen.width + this.screen.height ) / 4;
this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;
this.noRotate = false;
this.noZoom = false;
this.noPan = false;
this.staticMoving = false;
this.dynamicDampingFactor = 0.2;
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = [ 65 /*A*/
,83 /*S*/
,68 /*D*/
];
// internalsthis.target = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var _state = STATE.NONE,_prevState = STATE.NONE,_eye = new THREE.Vector3(),_rotateStart = new THREE.Vector3(),_rotateEnd = new THREE.Vector3(),_zoomStart = new THREE.Vector2(),_zoomEnd = new THREE.Vector2(),_touchZoomDistanceStart = 0,_touchZoomDistanceEnd = 0,_panStart = new THREE.Vector2(),_panEnd = new THREE.Vector2();
// for resetthis.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.up0 = this.object.up.clone();
// eventsvar changeEvent ={
type:'change'}
;
// methodsthis.handleResize = function (){
this.screen.width = window.innerWidth;
this.screen.height = window.innerHeight;
this.screen.offsetLeft = 0;
this.screen.offsetTop = 0;
this.radius = ( this.screen.width + this.screen.height ) / 4;
}
;
this.handleEvent = function ( event ){
if ( typeof this[ event.type ] == 'function' ){
this[ event.type ]( event );
}
}
;
this.getMouseOnScreen = function ( clientX,clientY ){
return new THREE.Vector2(( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,( clientY - _this.screen.offsetTop ) / _this.radius * 0.5);
}
;
this.getMouseProjectionOnBall = function ( clientX,clientY ){
var mouseOnBall = new THREE.Vector3(( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,0.0);
var length = mouseOnBall.length();
if ( length > 1.0 ){
mouseOnBall.normalize();
}
else{
mouseOnBall.z = Math.sqrt( 1.0 - length * length );
}
_eye.copy( _this.object.position ).sub( _this.target );
var projection = _this.object.up.clone().setLength( mouseOnBall.y );
projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
projection.add( _eye.setLength( mouseOnBall.z ) );
return projection;
}
;
this.rotateCamera = function (){
var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
if ( angle ){
var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart,_rotateEnd ).normalize(),quaternion = new THREE.Quaternion();
angle *= _this.rotateSpeed;
quaternion.setFromAxisAngle( axis,-angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_rotateEnd.applyQuaternion( quaternion );
if ( _this.staticMoving ){
_rotateStart.copy( _rotateEnd );
}
else{
quaternion.setFromAxisAngle( axis,angle * ( _this.dynamicDampingFactor - 1.0 ) );
_rotateStart.applyQuaternion( quaternion );
}
}
}
;
this.zoomCamera = function (){
if ( _state === STATE.TOUCH_ZOOM ){
var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
_eye.multiplyScalar( factor );
}
else{
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ){
_eye.multiplyScalar( factor );
if ( _this.staticMoving ){
_zoomStart.copy( _zoomEnd );
}
else{
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
}
}
;
this.panCamera = function (){
var mouseChange = _panEnd.clone().sub( _panStart );
if ( mouseChange.lengthSq() ){
mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
_this.object.position.add( pan );
_this.target.add( pan );
if ( _this.staticMoving ){
_panStart = _panEnd;
}
else{
_panStart.add( mouseChange.subVectors( _panEnd,_panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
}
}
}
;
this.checkDistances = function (){
if ( !_this.noZoom || !_this.noPan ){
if ( _this.object.position.lengthSq() > _this.maxDistance * _this.maxDistance ){
_this.object.position.setLength( _this.maxDistance );
}
if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ){
_this.object.position.addVectors( _this.target,_eye.setLength( _this.minDistance ) );
}
}
}
;
this.update = function (){
_eye.subVectors( _this.object.position,_this.target );
if ( !_this.noRotate ){
_this.rotateCamera();
}
if ( !_this.noZoom ){
_this.zoomCamera();
}
if ( !_this.noPan ){
_this.panCamera();
}
_this.object.position.addVectors( _this.target,_eye );
_this.checkDistances();
_this.object.lookAt( _this.target );
if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ){
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
}
;
this.reset = function (){
_state = STATE.NONE;
_prevState = STATE.NONE;
_this.target.copy( _this.target0 );
_this.object.position.copy( _this.position0 );
_this.object.up.copy( _this.up0 );
_eye.subVectors( _this.object.position,_this.target );
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
;
// listenersfunction keydown( event ){
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown',keydown );
_prevState = _state;
if ( _state !== STATE.NONE ){
return;
}
else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ){
_state = STATE.ROTATE;
}
else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ){
_state = STATE.ZOOM;
}
else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ){
_state = STATE.PAN;
}
}
function keyup( event ){
if ( _this.enabled === false ) return;
_state = _prevState;
window.addEventListener( 'keydown',keydown,false );
}
function mousedown( event ){
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.NONE ){
_state = event.button;
}
if ( _state === STATE.ROTATE && !_this.noRotate ){
_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX,event.clientY );
}
else if ( _state === STATE.ZOOM && !_this.noZoom ){
_zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX,event.clientY );
}
else if ( _state === STATE.PAN && !_this.noPan ){
_panStart = _panEnd = _this.getMouseOnScreen( event.clientX,event.clientY );
}
document.addEventListener( 'mousemove',mousemove,false );
document.addEventListener( 'mouseup',mouseup,false );
}
function mousemove( event ){
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.ROTATE && !_this.noRotate ){
_rotateEnd = _this.getMouseProjectionOnBall( event.clientX,event.clientY );
}
else if ( _state === STATE.ZOOM && !_this.noZoom ){
_zoomEnd = _this.getMouseOnScreen( event.clientX,event.clientY );
}
else if ( _state === STATE.PAN && !_this.noPan ){
_panEnd = _this.getMouseOnScreen( event.clientX,event.clientY );
}
}
function mouseup( event ){
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state = STATE.NONE;
document.removeEventListener( 'mousemove',mousemove );
document.removeEventListener( 'mouseup',mouseup );
}
function mousewheel( event ){
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
var delta = 0;
if ( event.wheelDelta ){
// WebKit / Opera / Explorer 9delta = event.wheelDelta / 40;
}
else if ( event.detail ){
// Firefoxdelta = - event.detail / 3;
}
_zoomStart.y += ( 1 / delta ) * 0.05;
}
function touchstart( event ){
if ( _this.enabled === false ) return;
switch ( event.touches.length ){
case 1:_state = STATE.TOUCH_ROTATE;
_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
case 2:_state = STATE.TOUCH_ZOOM;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
break;
case 3:_state = STATE.TOUCH_PAN;
_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
default:_state = STATE.NONE;
}
}
function touchmove( event ){
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ){
case 1:_rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
case 2:var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )break;
case 3:_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
default:_state = STATE.NONE;
}
}
function touchend( event ){
if ( _this.enabled === false ) return;
switch ( event.touches.length ){
case 1:_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
case 2:_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
break;
case 3:_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX,event.touches[ 0 ].pageY );
break;
}
_state = STATE.NONE;
}
this.domElement.addEventListener( 'contextmenu',function ( event ){
event.preventDefault();
}
,false );
this.domElement.addEventListener( 'mousedown',mousedown,false );
this.domElement.addEventListener( 'mousewheel',mousewheel,false );
this.domElement.addEventListener( 'DOMMouseScroll',mousewheel,false );
// firefoxthis.domElement.addEventListener( 'touchstart',touchstart,false );
this.domElement.addEventListener( 'touchend',touchend,false );
this.domElement.addEventListener( 'touchmove',touchmove,false );
window.addEventListener( 'keydown',keydown,false );
window.addEventListener( 'keyup',keyup,false );
this.handleResize();
}
;