orbitcontrols.js: How to Zoom In/ Zoom Out - javascript

Using orbitcontrols.js (with THREE.js), I want to achieve the same effect in code as rotating the mouse wheel. For example, I want to call something like camera.zoomIn() and have it move a set distance toward the target. Anyone know how to do this?

At least for simple cases, you can change the position of the camera (e.g. multiply x, y and z by a factor), which automagically updates the OrbitControls.
Example with a <input type="range"> slider:
zoomer.addEventListener('input', function(e) {
var zoomDistance = Number(zoomer.value),
currDistance = camera.position.length(),
factor = zoomDistance/currDistance;
camera.position.x *= factor;
camera.position.y *= factor;
camera.position.z *= factor;
});
https://codepen.io/Sphinxxxx/pen/yPZQMV

From looking at the source code you can call .dollyIn(dollyScale) and .dollyOut(dollyScale) on the OrbitalControls object.
Edit: these aren't public methods; one can access them by editing OrbitalControls.js though.
I added this.dIn = dollyIn; to the THREE.OrbitControls function, I can now zoom in by calling controls.dIn(1.05); controls.update(); from outside.

You can set values on OrbitControls.maxDistance and OrbitControls.minDistance to zoom in and out programmatically.
For example, if you wanted to animate a zoom out with gsap you could do something like this:
gsap.to(myOrbitControls, {
minDistance: 20, // target min distance
duration: 1,
overwrite: 'auto',
ease: 'power1.inOut',
onComplete: () => {
myOrbitControls.minDistance = 0 // reset to initial min distance
},
})

Related

How to use .onclick event for multiple times?

For example, I have a rectangle on my html page, and want to rotate it for 90deg by clicking on it:
rectangle.onclick = () => {
rectangle.style.transform = "rotate(90deg)";
When I click once - it rotates,but when i wanna rotate it for the second time - it does not work. Is there any way to to use .onclick event for two or more times?
CSS is not stateful; that is, when you set "rotate(90)" it doesn't rotate the item 90 degrees further, it rotates it to exactly 90 degrees from 0. So, if you want to rotate it, you need to set the correct angle: 90, 180, 270, &c. You can use a simple state variable to keep track of your angle, like this:
let rotation = 0 // rotation angle variable
rectangle.onclick = () => {
if (rotation >= 360) {
rotation = 0
} else {
rotation += 90;
}
rectangle.style.transform = `rotate(${rotation}deg)`;
}
When you set pentangle.style.transform, you're setting a style. That transform is from the baseline, unrotated shape. Your onclick event is likely getting fired multiple times, but each time, it does the same thing: cause the pentangle to be rotated from its default orientation.
You'll need something more like this, which advances the rotation amount based on the current rotation.
pentangle.onclick = () => {
if(pentangle.style.transform == "rotate(90deg)")
pentangle.style.transform = "rotate(180deg)";
else if(pentangle.style.transform == "rotate(180deg)")
pentangle.style.transform = "rotate(270deg)";
else if(pentangle.style.transform == "rotate(270deg)")
pentangle.style.transform = "";
else
pentangle.style.transform = "rotate(90deg)";

fabricjs - creating endless animation loop without lags

I am trying to create an endless animation loop of rotation with FabricJS. It works like this: the rotate function gets animation details (object to animate, duration of one animation loop, starting angle of rotation) as parameters, and invokes inner function rotateInner to launch the animation itself. rotateInner rotates given object using fabric.util.animate with linear easing function (to make rotation speed constant) to run one animation loop and to invoke itself after the loop ends, which results in an infinite animation loop.
The problem is that rotateInner lags every time between animation loops when it invokes itself through fabric.util.animate.
For example, I use animation loop with duration = 1 second and rotation angle = 180 degrees. In that case, animation makes lag every 1 second after rotating by 180 degrees.
I've found a solution to make lag occur N times less. Instead of rotating by 180 degrees for 1 second, I rotate object by 180*N degrees for N seconds. If I pick enough big N, the user will meet lag potentially never. But only potentially.
What are reasons of lags and is it possible to remove lags between animation loops completely? Or, perhaps, I went wrong way, and I should use something completely different to create endless animation loop?
Here's example which demonstrates lag (on jsfiddle). (The rotate function has additional argument - multiplier. It's N I've just wrote about above) Left rectangle rotates 180 degrees per 800 milliseconds, and the right rectangle rotates 180*10 degrees per 800*10 milliseconds, and thus the right one rotates quicker than the left one.
And, if you don't want to use jsfiddle or if example became inaccessible for some reason, here's the example itself in code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.min.js"></script>
<style>canvas {border: 5px dotted black;}</style>
<title>FabricJS animation loop lag</title>
</head>
<body>
<canvas id="mycanvas" width="700" height="300"></canvas>
<script>
var canvas = new fabric.StaticCanvas('mycanvas');
var rectCreationOptions = {
left: canvas.getWidth()/4,
top: canvas.getHeight()/2,
fill: '#0bb',
width: canvas.getWidth()/5,
height: canvas.getHeight()/5,
originX: 'center',
originY: 'center'
};
// creating left rectangle:
var rect1 = new fabric.Rect(rectCreationOptions);
rectCreationOptions.left *= 3;
// creating right rectangle:
var rect2 = new fabric.Rect(rectCreationOptions);
canvas.add(rect1);
canvas.add(rect2);
function rotate(animatedObject, duration = 1000, multiplier = 1, startAngle = 0) {
duration *= multiplier;
var addAngle = 180 * multiplier;
(function rotateInner(startAngle) {
fabric.util.animate({
startValue: startAngle,
endValue: startAngle+addAngle,
duration: duration,
// linear easing function:
easing: function(t, b, c, d) { return c*t/d + b; },
onChange: function (value) {
animatedObject.angle = value;
canvas.renderAll();
},
onComplete: function() {
rotateInner(startAngle+addAngle);
}
});
})(startAngle);
}
rotate(rect1, 800, 1); // lags every 800 ms
rotate(rect2, 800, 10); // lags every 800*10 ms
</script>
</body>
</html>
I will be thankful for help.
Please, don't throw rotten tomatoes at me if I made tiny mistake. This is my first question on stackoverflow. :)
Best wishes everyone.

Three.js OrbitControls tween animation to face(front) of the target object

When I click a button, I want OrbitControls(camera) be in front of the object(face side) smoothly by using tween.js.
I use this code but find that after changing controls.target and panning far from the target object, I only can zoom in a little level, and after I zoom in, I can't pan.
Is there another way to look at a object? Thanks!
var from = {
x: controls.target.x,
y: controls.target.y,
z: controls.target.z
};
var to = {
x: object.getWorldPosition().x,
y: object.getWorldPosition().y,
z: object.getWorldPosition().z
};
var tween = new TWEEN.Tween(from)
.to(to, 1000)
.easing(TWEEN.Easing.Quadratic.InOut) // | TWEEN.Easing.Linear.None
.onUpdate(function () {
controls.target.set( this.x, this.y, this.z )
})
.onComplete(function () {
controls.target.set( this.x, this.y, this.z )
})
.start();
Try the following: When the animation starts, disable the controls via controls.enabled = false; to prevent any interference with your animation. Next, implement the onComplete() callback of one of your tweens like so:
.onComplete(function() {
controls.enabled = true;
camera.getWorldDirection( lookDirection );
controls.target.copy( camera.position ).add( lookDirection.multiplyScalar( 10 ) );
})
The idea is enabling the controls again, computing the current look direction of your view and then setting a new target for the controls. You can also set a predefined target if you need an exact target spot for each camera position. The value 10 is in world units and determines how far away the target should be along the look direction.
Updated fiddle: https://jsfiddle.net/ckgo7qu8/

jQuery "slime menu" - pixel perfect rounding

I've successfully programmed my jQuery slime menu, and now it works beautiful. My only problem is that I move the arrow and the ending circle (the icon) of the slime separately, so if you move the cursor too away from the starting point, it will not connect to the slime precisely. It must be a rounding issue. Here's an image of what I'm talking about:
And here's the fiddle I'm working on:
http://jsfiddle.net/41Lcj653/4/
EDIT (Working): http://jsfiddle.net/41Lcj653/7/
Can anyone help with this issue? I use CSS transformations, and JS for update the CSS rules in each step.
The JS part of the code:
$(function(){
$(window).mousemove(function(e){
moveslime(e);
});
});
function moveslime(e){
var rad = $('.arrow').height() / 2;
var ofs = $('.arrow').offset();
var mPos = {x: e.pageX-25-rad, y: e.pageY-25-rad};
var dir = Math.round( -(Math.atan2(mPos.x, mPos.y)) / (Math.PI/180) );
$('.arrow').css('transform','rotate('+dir+'deg)');
$('.arrow').css('border-width','0 25px '+pdist(0, 0, mPos.x, mPos.y)+'px 25px');
$('.bubble').css('left', mPos.x+'px').css('top', mPos.y+'px');
}
function pdist(x1,y1,x2,y2) {
return Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2));
}
the reason you see this 'not precisely connected' is that rotate goes in 360 steps (the 360 degrees of a circle). when you are near the center the effect can be neglected, but the more you get away from the center (increasing circle circumference!) the visible gap between the degrees increases.
Maybe try this with svg?
actually you can use float numbers a degree .. so dont round the deg!
https://jsfiddle.net/41Lcj653/5/
// without math.round
var dir = -(Math.atan2(mPos.x, mPos.y)) / (Math.PI/180);
Some jQuery method like height rounds decimals. You could try this:
$(".arrow")[0].getBoundingClientRect().height
More information: https://developer.mozilla.org/es/docs/Web/API/Element/getBoundingClientRect

How to make a realistic roulette ball spinning animation

I'm using PhysicsJS to make a 2D roulette ball spinning animation.
So far, I've implemented the following:
used a constraint so that the ball wouldn't "fly away":
rigidConstraints.distanceConstraint( wheel, ball, 1 );
used drag to slow down the ball:
world.add(Physics.integrator('verlet', { drag: 0.9 }));
made the wheel attract the ball, so that it would fall towards it when the drag has slowed down the ball enough
My questions:
how do I gradually slow down the ball spinning?
I have already a very high drag value, but it doesn't look like it's doing anything
how do I make attraction towards the wheel work?
The distance constraint should keep the ball from escaping, not from getting closer to the wheel.
why does angularVelocity: -0.005 not work at all on the wheel?
My code, also on JSfiddle
Physics(function (world) {
var viewWidth = window.innerWidth
,viewHeight = window.innerHeight
,renderer
;
world.add(Physics.integrator('verlet', {
drag: 0.9
}));
var rigidConstraints = Physics.behavior('verlet-constraints', {
iterations: 10
});
// create a renderer
renderer = Physics.renderer('canvas', {
el: 'viewport'
,width: viewWidth
,height: viewHeight
});
// add the renderer
world.add(renderer);
// render on each step
world.on('step', function () {
world.render();
});
// create some bodies
var ball = Physics.body('circle', {
x: viewWidth / 2
,y: viewHeight / 2 - 300
,vx: -0.05
,mass: 0.1
,radius: 10
,cof: 0.99
,styles: {
fillStyle: '#cb4b16'
,angleIndicator: '#72240d'
}
})
var wheel = Physics.body('circle', {
x: viewWidth / 2
,y: viewHeight / 2
,angularVelocity: -0.005
,radius: 100
,mass: 100
,restitution: 0.35
// ,cof: 0.99
,styles: {
fillStyle: '#6c71c4'
,angleIndicator: '#3b3e6b'
}
,treatment: "static"
});
world.add(ball);
world.add(wheel);
rigidConstraints.distanceConstraint( wheel, ball, 1 );
world.add( rigidConstraints );
// add things to the world
world.add([
Physics.behavior('interactive', { el: renderer.el })
,Physics.behavior('newtonian', { strength: 5 })
,Physics.behavior('body-impulse-response')
,Physics.behavior('body-collision-detection')
,Physics.behavior('sweep-prune')
]);
// subscribe to ticker to advance the simulation
Physics.util.ticker.on(function( time ) {
world.step( time );
});
// start the ticker
Physics.util.ticker.start();
});
Drag has a bug in that version of PhysicsJS, try using the most updated version from github. https://github.com/wellcaffeinated/PhysicsJS/issues/94
Unfortunately the distance constraint imposes a fixed distance. So to prevent the ball's escape in that way, you'd need to implement your own behavior. (more below)
You'll have to change behavior: "static" to be behavior: "kinematic". Static bodies don't ever move on their own.
To create a custom behavior check out the documentation here: https://github.com/wellcaffeinated/PhysicsJS/wiki/Behaviors#creating-a-custom-behavior
In order to get the functionality you're describing, you'll need to do something like this:
// in the behave method
// calculate the displacement of the ball from the wheel... something like....
disp.clone( wheel.state.pos ).vsub( ball.state.pos );
// if it's greater than max distance, then move it back inside the max radius
if ( disp.norm() > maxDist ){
var moveBy = disp.norm() - maxDist;
disp.normalize(); // unit vector towards the wheel
disp.mult( moveBy );
ball.state.pos.vadd( disp ); // move it back inside the max radius
}
Of course, this is a "just get it done" way of doing this but it should work.

Categories

Resources