I am trying to transition camera.position and camera.lookAt smoothly between "zoomed out" and "zoomed in" views of individual, randomly placed objects.
The positioning works great. Lerping the lookAt(), however, doesn't seem to be playing nicely with other solutions for traditional ThreeJS ( see #bovesan's answer here) nor addressed by the relevant example on the react-three-fiber docs (link).
Zooming in past the z axis flips the camera around, and at the corners it's wildly distored.
You can see my progress here : https://codesandbox.io/s/three-fiber-zoom-to-object-rlme0?file=/src/App.js
With the relevant bit of code being in App.js on line 63 :
useFrame((state) => {
const step = 0.05;
// `focus` is a state variable that sends a Vec3 of the objects position
zoom ? vec.set(focus.x, focus.y, focus.z + 0.2) : vec.set(0, 0, 5);
// HERE, looking for a way to lerp camera lookAt in a way that can toggle.
state.camera.lookAt(0, 0, 0);
state.camera.position.lerp(vec, step);
state.camera.updateProjectionMatrix();
});
I've spent hours looking for relevant examples/tutorials, but haven't come up with much. I'm afraid I don't have enough ThreeJs experience to be looking in the right direction, though, so any help in any direction would be most welcome.
To anyone who happens upon this later, the solution was figured out over at by #drcmda.
You can find a working example here :
https://codesandbox.io/s/three-fiber-zoom-to-object-camera-controls-solution-final-sbgx0?file=/src/App.js
This is just a slight change on #drcmda 's implementation of camera-controls with normal lerping to move the camera. It’s not perfect (for one, the transition time in camera controls doesn’t seem to be editable, so there’s a weird swing-around thing that happens, when you’re zooming back out) but it definitely solves the problem. (Many thanks to #looeee and #forerunrun for additional help.)
If you'd rather not use another library, #forerunrun's answer in the original thread also works well, but I wasn't able to debug it enough to have it be reliable. (See convo.)
Related
I'm trying to throw an equirectangular image into Panolens and simply 1st get the same panorama viewer as can be visible below:
https://threejs.org/examples/webgl_panorama_equirectangular.html
https://www.chiefarchitect.com/products/360-panorama-viewer/
Since Panolens.js is a wrapper of Three.js I expected the same result as when I throw my image into the 1st link above. (drag & drop it) - camera looks into the center of the image.
What I get instead is : camera looks into the left most area of the panorama image.
I solve this using
panorama.addEventListener( 'enter-fade-start', function() {
viewer.getControl().target.set(10, -2, 0);
viewer.getControl().update();
//viewer.tweenControlCenter( new THREE.Vector3(2, -1, 1), 2000 );
});
But when I move to another panorama and come back, this code does not seem to have any effect (althought event is properly triggered).
How can I make the camera look into the same direction each time I come back to my panorama? (I'm switching between multiple panoramas).
Basically I don't seem to understand vectors properly and where 0,0,0 is each time I come back to panorama, since It seems to be working differently based on where the camera is facing.
Any tips/links/explanations very much welcome.
I over complicated things it seems. The solution is to only use:
viewer.tweenControlCenter( new THREE.Vector3(10, -2, 0), 0 );
inside "enter-fade-start". And that's it.
Much of docs is a bit misleading with saying you have to call .update() on control and setting .target properly etc, but that does not seem to work as I expected.
Relevant example also here: https://codepen.io/pchen66/pen/LLgxME
I'm relatively new to javascript, and am learning about drag and drop using snap.svg. My problem is in the drop. I can't tell if the dragged element is over the drop target. In this code, I want to drag the circle over the square, and thought I could use mouseover. My (distilled) example may also be a simpler version of this post.
var paper = Snap(300, 300);
var square = paper.rect(100, 100, 40, 40).attr({fill:"blue"});
var circle = paper.circle(50, 50, 20).attr({fill:"red"});
circle.drag();
square.mouseover(
function() {
console.log("Over the square");
}
);
As written, the mouseover will fire when you move the pointer over the blue square, but not when you drag the red circle over the blue square. If you reverse the creation of the square and circle, the mouseover fires either way, but of course the circle is behind the square.
Evidently the event gets caught in the view hierarchy (or something) and doesn't propagate. There must be an easy way around this. Any help?
(And if the best answer is, "use jQuery," fine, but I'd love to learn how to make this work directly, since snap.svg makes dragging so easy.)
Addition: The direction I'm hoping for: the snap.svg documentation for Element.drag() says, in part, "When Element is dragged over another element, drag.over.<id> fires as well." A fine, event-based direction, which would let me (for example) highlight the drop target without a lot of fuss.
But I haven't figured out how to listen for that event! Any help or advice?
Only quick way without collision or element detection from points that I can think of, is to place an almost invisible clone in front of the object, later in the DOM that you can't really see, eg ...
paper.append( square.clone().attr({ opacity: 0.000001 }) )
jsfiddle
Depends how complex your svgs are going to be as to whether this would work I guess, you also have a slight issue if you drop the element over it, your redrag start won't get picked up, so you would need to code around that as well. I think some testing is probably going to be the most bug free solution (there are a few solutions on S.O for getElementFromPoint or hit detection type solutions).
jsfiddle workaround for point above
I have a dynamically generated svg image that I am using Ariutta's svg-pan-zoom plugin with. When I double click an svg image, I set pan.x = centerOfScreenX, and pan.y = centerOfScreenY to center the image in the middle of the screen. ie:
$('.svg').dblclick(function(){
zoom.pan({'x':centerOfScreenX, 'y':centerOfScreenY });
});
Currently this causes the image to just suddenly move to the center of the screen. Is there a way I can animate this change in pan position so that the image doubleclicked moves along a path to the center of the screen instead?
Bumbu suggested two solution paths (see answers below), and I have taken a stab at the first. My attempt did not work however, and I do not know why.
// centerOfScreenX and centerOfScreenY are the correct values that pan.x and
// pan.y should have to center the svg in the middle of the screen
// xInterval and yInterval break the distance between the current pan
// position and the desired pan position into 10 steps
var xInterval = (centerOfScreenX - pan.x)/10;
var yInterval = (centerOfScreenY - pan.y)/10;
while( pan.x !== centerOfScreenX && pan.y !== centerOfScreenY ){
if(pan.x !== centerOfScreenX){
pan({'x': pan.x + xInterval })
}
if(pan.y !== centerofScreenY){
pan({'y': pan.y + yInterval })
}
}
When I try to run this code, the window freezes and I can no longer interact with my app, unless i close the window and reload it. My guess is that I am somehow triggering an infinite loop.
Currently there is no solution to do animation in an easy way.
There is a similar question (about animating zoom). The answer from there (adjusted to this one) is:
Currently such functionality is not supported. You could do it in 2 ways:
Use a twin library (or write you own function) and just call pan in small iterations multiple times. This may be slow but it is what many libraries do when implementing animation (eg. jQuery).
Use SVG animateTransform element. It seems to be the right way. But it needs some work to get it done.
You can actually try to implement second solution by listening to zoom
events, canceling them and adding animateTransform manually to the SVG.
When your animation is done, call zoom again but this time don't
cancel it (necessary to update library inner state).
There is an ongoing discussion about next version of library that would be more extensible. This would allow to write plugins. Animation is one of the candidates. But it will take some time (few months) to do this.
If you'll be able to find a temporary solution - share it here or on github and we'll be happy to update the library or integrate it in next version.
Edit
I added a simple example how this kind of animation can be implemented.
You can find it in demo/simple-animation.html
I used a simple interval there. A more advanced version should take into account how much time passed since last interval call and send the right amount for pan. But even like this it works very well.
The library internally uses requestAnimationFrame so you can call panBy even every millisecond and it shouldn't block the browser.
deviantART muro has a set of brilliant tools of painting. And I'm very curious how to implement these brushes like Sketch and Paintbrush, arithmetically?
Using any normal programming language to explain is okay, though I prefer C++ or JavaScript. I think it's better than read their JS source code.
I'd say it works something like:
Track mouse movement
On captured mouse movement, draw your desired brush from saved "Old mouse position" to captured "New mouse position", iterating at a pixel's distance at a time
If you move the mouse too fast for the script to capture, it will just look like a computed long straight line (which is what it looks like Muro is doing). If you want to get real fancy you can calculate the trajectory from previous mouse positions and draw that instead for a "smoother" line.
Since you specified Javascript you'd probably want to draw it in a canvas object.
EDIT 1:
Sketch specifically seems to save mouse movements and then loop through, say the 20 latest mouse movements for each mouse movement and draw a bezier curve from that point to the current point.
So, something like (pseudo code)
Object mousemovements = [];
on.mousemove(event)
{
if (mousemovements.length > 20)
{
mousemovements.removeLast();
}
mousemovements.insertAtBeginning([ event.mouseX, event.mouseY ]);
for-each (movement in mousemovements)
{
drawBeziercurveFromTo(movement.mouseX, movement.mouseY,
event.mouseX, event.mouseY);
}
}
Jquery/Canvas DEMO based on the above pseudo code
EDIT 2:
I had a closer look at how "Sketch" worked and it seems that they update the mouse pointer positions, moving the older points closer to the newer points. Something like this:
This DEMO works pretty much like the sketch brush
Can anyone explain or point me to an example where the "z" in translate3d (webkit transform) is being used? I have successfully used translate3d(x,y,0) to get hardware accelerated 2D animations on mobile Safari, but now I’m trying to scale using the z parameter, but it does not seem to have any effect...
elem.style.WebkitTransform = 'translate3d(100px,0,0)'; // this works as expected
elem.style.WebkitTransform = 'translate3d(0,0,100)'; // nothing happens
elem.style.WebkitTransform = 'translate3d(0,0,100px)'; // nothing happens
elem.style.WebkitTransform = 'scale(1.2, 1.2)'; // works but slow on ios
Sidenote: I’m trying to build a small zoom script that works smoothly on ios.
I made this for you to show how webkit transform 3D works:
http://jsbin.com/iderag
I hope it help you. I'm guessing you don't have -webkit-perspective in your body or parent tag.
Remember to set the -webkit-perspective on the containing box. 800 is a good starting value. If the box disappears, reduce it, it's probably bigger than the viewport.
The Surfin' Safari blog has an article from when 3d transforms were first invented:
-webkit-perspective is used to give an illusion of depth; it
determines how things change size based on their z-offset from the z=0
plane. You can think of it as though you’re looking at the page from a
distance p away. Objects on the z=0 plane appear in their normal size.
Something at a z offset of p/2 (halfway between the viewer and the z=0
plane) will look twice as big, and something at a z offset of -p will
look half as big. Thus, large values give a little foreshortening
effect, and small values lots of foreshortening. Values between 500px
and 1000px give a reasonable-looking result for most content.
More here: http://www.webkit.org/blog/386/3d-transforms/