I have a few divs that follow the mouse position slowly. In the begenning it starts off fine but the closer it gets to the mouse position the slower it gets. I have a very efficient code below but I want to improve it so that the div will always follow the mouse with a constant speed rather than a changing one.
var xp = x, yp = y;
var loop = setInterval(function(){
xp += ((mouseX - xp) ) / 100;
yp += ((mouseY - yp)) / 100;
object.css({left:xp, top:yp});
},20);}
since its diving it by a 100 when it gets closer the math gets smaller causing the X/Y to move slower. I want it to stay the same speed regardless where its coming from.
Here is the solution:
var loop = setInterval(
function()
{
speed = 20;
xp += (mouseX - xp)>speed ? speed : ((mouseX - xp)<-speed ? -speed : (mouseX - xp));
yp += (mouseY - yp)>speed ? speed : ((mouseY - yp)<-speed ? -speed : (mouseY - yp));
object.css({left:xp, top:yp});
},20
);
Have you tried using Web workers?
Using a web worker, you can send 'heavy' tasks to complete in a background-thread, so that your UI-thread does not become sluggish and your web application stays responsive.
It's very simple to set-up.
var worker = new Worker('someWorker.js');
Have a look:
https://developer.mozilla.org/En/Using_web_workers
Related
My Three.js project uses and OrthographicCamera and OrthographicTrackBallControls for zoom/pan. I'm trying to add functionality to zoom to the cursor position with no luck. First things first, here's how I'm getting mouse position:
var mX = ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
var vector = new THREE.Vector3(mX, mY, 0.5);
vector.unproject(camera);
vector.sub(camera.position);
Through looking on StackOverflow, there seems to be a lot of information on how to do this with PerspectiveCamera, but these methods don't work with OrthographicCamera. I was able to find this example:
https://htmlpreview.github.io/?https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html
Which does precisely what I am trying to accomplish, but the code that achieves this is hidden, though I am able to discern that the camera position is being changed.
Another SO question which is similar suggests changing camera.left, camera.right, camera.top and camera.bottom, but I have had no luck with this approach. This approach seems like a possibility, but I dont understand the calculations necessary to get the correct left, right, top and bottom values.
So the way I see it I have two possibilities:
Change camera's left/right/top/bottom to get the correct view rectangle.
Change camera position.
But I don't know how to get the values I need to accomplish either, let alone which is the better approach.
UPDATE 11/16/2018:
I've updated my function to this ( based on https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html):
zoomDirection = new THREE.Vector3();
function mousewheel(event) {
event.preventDefault();
var amount = event.deltaY / 100;
var zoom = camera.zoom - amount;
var offset = el.offset();
;
var mX = amount > 0 ? 0 : ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = amount > 0 ? 0 : -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
zoomDirection.set(mX, mY, 0.001)
.unproject(camera)
.sub(camera.position)
.multiplyScalar(amount / zoom);
camera.position.subVectors(camera.position, zoomDirection);
orthographictrackBallControls.target.subVectors(orthographictrackBallControls.target, webGl.zoomDirection);
camera.zoom = zoom;
camera.updateProjectionMatrix();
}
This seems to work at first: the camera zooms into the mouse point, but then the camera starts to "jump" around after a bit of zooming, with the mesh no longer visible on screen.
Something that might help: I have an axis helper in the screen as well that "flips" when it stops working as expected. When the scene is loaded, the X-axis helper point due left, but when I get to the point where the camera jumps and I no longer see the mesh, the X-axis helper flips to point due right.
Also, if I zoom OUT first, I can zoom in further before the mesh disappears. I'm not sure what this all adds up to but I would appreciate any help.
First week back after New Year and it's taken too long to fix this. Six sides of A4 covered with linear algebra results in
if ( factor !== 1.0 && factor > 0.0 ) {
const mX = (event.offsetX / event.target.width ) * 2 - 1;
const mY = -(event.offsetY / event.target.height) * 2 + 1;
const vector1 = new THREE.Vector3(mX, mY, 0);
const vector2 = new THREE.Vector3(0, 0, 0);
vector1.unproject(this.camera);
vector2.unproject(this.camera);
vector1.subVectors(vector1, vector2);
this.camera.zoom /= factor;
vector1.multiplyScalar(factor - 1.0);
this.camera.position.subVectors(this.camera.position, vector1);
this.controls.target.subVectors(this.controls.target, vector1);
this.camera.updateProjectionMatrix();
this.camera.updateMatrix();
}
Note the different calculation of mX, mY so that it is valid for a viewport.
Implementing the D3-library with its zoom function may seem like a good idea for this case. But giving up the three-controls is in a lot of cases not a deal.
If you want a zoom-behavior like in Google Maps, the following code could be helpful:
const cameraPosition = camera.position.clone();
// my camera.zoom starts with 0.2
if (zoomOld !== 0.2) {
const xNew = this.curserVector.x + (((cameraPosition.x - this.curserVector.x) * camera.zoom) /zoomOld);
const yNew = this.curserVector.y + (((cameraPosition.y - this.curserVector.y) * camera.zoom) /zoomOld);
const diffX = cameraPosition.x - xNew;
const diffY = cameraPosition.y - yNew;
camera.position.x += diffX;
camera.position.y += diffY;
controls.target.x += diffX;
controls.target.y += diffY;
}
zoomOld = camera.zoom;
Your other problem could be caused by the frustum. But I don't know, I'm still a newbie with Three xD
I have written a small inertial scrolling algorithm for mousewheels in javascript.
It works perfectly for my needs however there is one part missing and I cannot seem to get the desired behaviour.
As the user scrolls to the end of the container, be it the top or the bottom. I would like to momentum to naturally decelerate to a stop. Currently it just halts instantly when it hits either edge, whatever speed it is currently going.
Rather than post a load of code here I have created a small jsfiddle to illustrate:
https://jsfiddle.net/o8xLw68L/8/
This is a simplified version of my current code. If you uncomment line 111 You can kind of see the behaviour I am looking for if you scroll down a little from the top of the div and then flick the mousewheel up reasonably quickly. You will see the momentum slow naturally ot the 0 position.
Inertial.prototype.smoothWheel = function(amt) {
this.targetY += amt;
//uncomment this line to see the decelleration almost work against the top edge of the container
//this.targetY = Math.max(0, this.targetY);
this.vy += (this.targetY - this.oldY) * this.stepAmt;
this.oldY = this.targetY;
}
The problem with this approach is that it only dampens the resulting this.vy property when the mousewheel pulses and therefore does not always work correctly since the user may be scrolling from lower down in the container, and at a faster speed, but without any continuing mousewheel pulses. (This is hard to articulate, the jsFiddle should make it clearer)
The solution will likely need to somehow dampen the this.vy property as we get sclose to the top or the bottom of the container, so that it decellerates at a faster pace than the natural this.friction property allows.
I am happy for the dampening area to be either hardcoded to be when you reach 300px of the top/bottom of the content. Or it alternatively, a percentage of the container height would work also.
Any help would be greatly appreciated.
It is possible to dampen the velocity to the value that is enough just to touch the top or bottom edge only by inertial motion (with friction) independently of mouse wheel speed.
Suppose that in current direction the distance to travel x is given. So, it is needed to find the velocity v0 which is enough to travel that distance with inertial motion with friction.
Current scrolling dynamics (vy *= friction) corresponds to laminar fluid flow. It can be written as differential equation:
dv = - (1 - friction) * v * dt; // velocity change dv in time interval dt
dv / v = - (1 - friction) * dt;
// integrating lhs and rhs with initial conditions (v = v0 at t = 0)
ln(v/v0) = - (1 - friction) * t;
v = v0 * exp(- (1 - friction) * t);
So, the velocity decays exponentially from v0 to zero with time.
Traveled distance:
dx = v * dt = v0 * exp(- (1 - friction) * t) * dt;
// integrating, initial conditions x = 0 at t = 0
x = v0 / (1 - friction) * (1 - exp(- (1 - friction) * t))
Thus it is possible to travel the following distance with starting velocity v0 in infinite time:
x = v0 / (1 - friction);
Basing on the distance that is left to the edge it is possible to bound the velocity:
Inertial.prototype.boundVelocity = function () {
var dist = 0;
if (this.dir == 1)
dist = this.scrollerPos;
else if (this.dir == -1)
dist = this.contentHeight - this.scrollerHeight - this.scrollerPos;
var maxv = dist * (1 - this.friction) + 1;
if (Math.abs(this.vy) > maxv) {
console.log('reduce velocity ' + this.vy + ' to ' + (-maxv * this.dir) + ', dist: ' + dist);
this.vy = -maxv * this.dir;
}
}
There is some small non zero minimal value for maxv (+1) to make sure the edges are always hit despite of discretization errors. That function is called when the velocity can be increased in smoothWheel() and also it is called in render() just to avoid numerical error accumulation.
The runnable example: https://jsfiddle.net/c675bkn9/
this is my first question after having relied on this site for years!
Anyway, I'd like to accomplish something similar to this effect:
http://www.flashmonkey.co.uk/html5/wave-physics/
But on a circular path, instead of a horizon. Essentially, a floating circle/blob in the center of the screen that would react to mouse interaction. What I'm not looking for is gravity, or for the circle to bounce around the screen - only surface ripples.
If at all possible I'd like to apply a static texture to the shape, is this a possibility? I'm completely new to Canvas!
I've already tried replacing some code from the above example with circular code from the following link, to very limited success:
http://www.html5canvastutorials.com/tutorials/html5-canvas-circles/
If only it were that easy :)
Any ideas?
Thanks in advance!
I tried to figure out how wave simulation works using View Source and JavaScript console. It's working fine but threw some JS errors. Also, it seems physics update is entangled with rendering in the render() method.
Here is what I found about the code:
The mouseMove() method creates disturbances on the wave based on mouse position, creating a peak around the mouse. The target variable is the index of the particle that needs to be updated, it's calculated from mouse pos.
if (particle && mouseY > particle.y) {
var speed = mouseY - storeY;
particles[target - 2].vy = speed / 6;
particles[target - 1].vy = speed / 5;
particles[target].vy = speed / 3;
particles[target + 1].vy = speed / 5;
particles[target + 2].vy = speed / 6;
storeY = mouseY;
}
Then, the particles around target are updated. The problem I found is that it does no bounds checking, i.e. it can potentially particles[-1] when target == 0. If that happens, an exception is thrown, the method call ends, but the code does not stop.
The render() method first updates the particle positions, then renders the wave.
Here is its physics code:
for (var u = particles.length - 1; u >= 0; --u) {
var fExtensionY = 0;
var fForceY = 0;
if (u > 0) {
fExtensionY = particles[u - 1].y - particles[u].y - springs[u - 1].iLengthY;
fForceY += -fK * fExtensionY;
}
if (u < particles.length - 1) {
fExtensionY = particles[u].y - particles[u + 1].y - springs[u].iLengthY;
fForceY += fK * fExtensionY;
}
fExtensionY = particles[u].y - particles[u].origY;
fForceY += fK / 15 * fExtensionY;
particles[u].ay = -fForceY / particles[u].mass;
particles[u].vy += particles[u].ay;
particles[u].ypos += particles[u].vy;
particles[u].vy /= 1.04;
}
Basically, it's Hooke's Law for a chain of particles linked by springs between them. For each particle u, it adds the attraction to the previous and next particles (the if statements check if they are available), to the variable fForceY. I don't fully understand the purpose of the springs array.
In the last four lines, it calculates the acceleration (force / mass), updates the velocity (add acceleration), then position (add velocity), and finally, reduce velocity by 1.04 (friction).
After the physics update, the code renders the wave:
context.clearRect(0, 0, stageWidth, stageHeight);
context.fillStyle = color;
context.beginPath();
for (u = 0; u < particles.length; u++) {
...
}
...
context.closePath();
context.fill();
I'm not explaining that, you need to read a canvas tutorial to understand it.
Here are some ideas to get started, note that I didn't test these code.
To modify the code to draw a circular wave, we need introduce a polar coordinate system, where the particle's x-position is the angle in the circle and y-position the distance from center. We should use theta and r here but it requires a large amount of refactoring. We will talk about transforming later.
mouseMove(): Compute particle index from mouse position on screen to polar coordinates, and make sure the disturbance wrap around:
Define the function (outside mouseMove(), we need this again later)
function wrapAround(i, a) { return (i + a.length) % a.length; }
Then change
particles[target - 2] --> particles[wrapAround(target - 2, particles)]
particles[target - 1] --> particles[wrapAround(target - 1, particles)]
...
The modulo operator does the job but I added particles.length so I don't modulo a negative number.
render(): Make sure the force calculation wrap around, so we need to wrapAround function again. We can strip away the two if statements:
fExtensionY = particles[wrapAround(u - 1, particles)].y - particles[u].y - springs[wrapAround(u - 1, springs)].iLengthY;
fForceY += -fK * fExtensionY;
fExtensionY = particles[u].y - particles[wrapAround(u + 1, particles)].y - springs[warpAround(u, springs)].iLengthY;
fForceY += fK * fExtensionY;
Here is the result so far in jsfiddle: Notice the wave propagate from the other side. http://jsfiddle.net/DM68M/
After that's done, the hardest part is rendering them on a circle. To do that, we need coordinate transform functions that treat particle's (x, y) as (angle in the circle, distance from center), and we also need inverse transforms for mouse interaction in mouseMove().
function particleCoordsToScreenCoords(particleX, particleY) {
return [ radiusFactor * particleY * Math.cos(particleX / angleFactor),
radiusFactor * particleY * Math.sin(particleX / angleFactor) ];
}
function screenCoordsToParticleCoords(screenX, screenY) {
// something involving Math.atan2 and Math.sqrt
}
Where the ...Factor variables needed to be determined separately. The angleFactor is two pi over the highest x-position found among particles array
Then, in the coordinates supplied to the context.lineTo, context.arc, use the particleCoordsToScreenCoords to transform the coordinates.
I'm creating a web-application that's going to display 3D objects in a canvas. Now I came across this problem:
I am slowly rotating the camera around the scene so the 3D object can be looked at from all sides. For this I use this code (JavaScript):
var step = 0.1*Math.PI/180;
scene.camera.position.x = Math.cos(step) * (scene.camera.position.x - 0) - Math.sin(step) * (scene.camera.position.z - 0) + 0;
scene.camera.position.z = Math.sin(step) * (scene.camera.position.x - 0) + Math.cos(step) * (scene.camera.position.z - 0) + 0;
Those zeroes are the center of the scene, I leave them there in case we decide to use another base-origin.
This code will make the camera rotate around point 0,0, but it slowly gets closer and closer to it. Here are some screenshots to show you what it does:
There are no other parameters that have impact on the camera's position. I don't understand why it's doing this and what the problem could be.
I found what was causing this issue: I change the camera's X position, then I change the camera's Z position with the new value of it's X position. Because this will be different the origin no longer is relatively at the same position for both calculations.
This was easy to fix, just by storing them into two new variables and then assigning them
var posx = Math.cos(step) * (scene.camera.position.x - 0) - Math.sin(step) * (scene.camera.position.z - 0) + 0;
var posz = Math.sin(step) * (scene.camera.position.x - 0) + Math.cos(step) * (scene.camera.position.z - 0) + 0;
scene.camera.position.x = posx;
scene.camera.position.z = posz;
I'm making a 2d side scroller and for the life of me I can't get jumping to work. This is how I'm doing moving left and right:
for(var i = 0; i < time; i++)
newVelocityX = (oldVelocityX + accelerationX) * frictionX;
then to update my player position I do
positionX = oldPositionX + newVelocityX;
This works great, and the variable "time" just has the amount of ms it's been since I last ran the function. Friction works great and I'm happy it's all good in the X direction. This is what I have in the Y direction:
for(var i = 0; i < time; i++) {
accelerationY += gravityAccelerationY;
newVelocityY = oldVelocityY + accelerationY;
}
The object falls down due to gravity just fine. If I set a negative accelerationY when the user hits the up arrow then I can even make the player jump, but on a fast computer they jump very high, and on an old computer they jump very low. I'm not sure how to fix this, I thought I already was accounting for this by putting it in the foor loop like I did.
You will need to do several things to change your code to work properly. There are numerous bugs/performance hits in the code you posted.
Here is some code to do the basics of the game.
Sample code for the jumping:
if (jump) {
velocityY = -jumpHeightSquared; // assuming positive Y is downward, and you are jumping upward
}
velocityY += gravityAccelerationY * time;
positionY += velocityY * time;
if (positionY > 0) {
positionY = 0; // assuming the ground is at height 0
velocityY = 0;
}
Sample code for moving sideways:
velocityX += accelerationX * time;
velocityX *= Math.pow(frictionX, time);
positionX += velocityX * time;
Some comments on the code:
The velocity and position variables need to keep their values in between frames (I'm assuming you've got that figured out).
gravityAccelerationY and frictionX are constant values, unless gravity or friction changes.
Where I replaced your for loops with * time, using a single multiplication will be faster than a loop. The only difference would be at low frame rates, or high rates of acceleration, where the acceleration would seem to be 'sped up' from what it should be. You shouldn't have problems with that though.