<html lang="en">
<head>
<title>three.js webgl - transform controls</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
color: #fff;
font-family:Monospace;
text-align: center;
font-size: 15px;
line-height: 30px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.min.js"></script>
<script>
/**control**/
function ObjectControl(camera, scene, canvas) {
this.object = null;
this.firstAttach = true;
this.camera = camera;
this.scene = scene;
this.helpPlane = null;
this.canvas = null;
this.canvas = canvas;
this.raycaster = new THREE.Raycaster();
this.isMouseDown = false;
this.mousemoveHandle = function(x, y, evt, prevX, prevY) {
var a = 2 * x / this.canvas.width - 1;
var b = 1 - 2 * y / this.canvas.height;
this.raycaster.setFromCamera(new THREE.Vector2(a, b), this.camera);
const intersects = this.raycaster.intersectObject(this.helpPlane);
if (intersects.length == 0) {
return;
}
var pos = intersects[0].point;
this.helpPlane.position.set(pos.x, pos.y, pos.z)
this.object.position.set(pos.x, pos.y, pos.z);
};
}
ObjectControl.prototype = {
attach: function(object, point) {
this.object = object;
var size = new THREE.Box3().setFromObject(object).getSize();
if (this.firstAttach) {
this.helpPlane = this.createHelpPlane(size, point);
}
this.scene.add(this.helpPlane);
this.onMouseDown();
this.onMouseUp();
this.onMouseMove();
this.firstAttach = false;
},
detach:function(){
this.scene.remove(this.helpPlane);
},
createHelpPlane: function(size, point) {
const geo = new THREE.BoxGeometry(2 * size.x, 0.01, 2 * size.z);
const mat = new THREE.MeshBasicMaterial({ color: "red" });
const targetPlane = new THREE.Mesh(geo, mat);
// targetPlane.visible = false;
targetPlane.name = "HELP";
targetPlane.position.copy(point);
return targetPlane;
},
onMouseMove: function() {
this.canvas.addEventListener("mousemove", e => {
if (!this.isMouseDown) return;
this.mousemoveHandle(e.clientX, e.clientY, event);
});
},
onMouseDown: function() {
this.canvas.addEventListener(
"mousedown",
() => (this.isMouseDown = true),
false
);
},
onMouseUp: function() {
this.canvas.addEventListener(
"mouseup",
() => (this.isMouseDown = false),
false
);
}
};
/**main**/
var renderer, camera, scene, control, raycaster, canvas,MovingCube;
init();
render();
function init() {
// SCENE
scene = new THREE.Scene();
window.scene = scene;
// CAMERA
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
NEAR = 0.1,
FAR = 20000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0, 150, 400);
camera.lookAt(scene.position);
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer({ antialias: true });
canvas = renderer.domElement;
control = new ObjectControl(camera, scene,canvas);
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
document.body.appendChild(renderer.domElement);
// EVENTS
// LIGHT
var light = new THREE.PointLight(0xffffff);
light.position.set(0, 250, 0);
scene.add(light);
var floorMaterial = new THREE.MeshBasicMaterial({
color: 0x9999ff,
side: THREE.DoubleSide
});
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = -0.5;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
// scene.add(skyBox);
// scene.fog = new THREE.FogExp2(0x9999ff, 0.00025);
var MovingCubeMat = new THREE.MeshBasicMaterial({
color: 0x8c4f13
});
var MovingCubeGeom = new THREE.CubeGeometry(50, 50, 50);
MovingCube = new THREE.Mesh(MovingCubeGeom, MovingCubeMat);
MovingCube.position.set(0, 25.1, 0);
scene.add(MovingCube);
canvas.addEventListener('click',function(e){
doMouseDown(e.clientX,e.clientY)
})
}
function doMouseDown(x, y) {
var a = 2 * x / canvas.width - 1;
var b = 1 - 2 * y / canvas.height;
raycaster.setFromCamera(new THREE.Vector2(a, b), camera);
var intersects = raycaster.intersectObject(MovingCube); // no need for recusion since all objects are top-level
if(intersects.length>0){
control.attach(MovingCube,intersects[0].point);
}else {
control.detach();
}
}
function render(){
renderer.render(scene,camera)
requestAnimationFrame(render)
}
</script>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |