aframe - how enforce max/min camera tilt - javascript
I have six planes set up as cube with textures to display a 360-degree jpg set. I positioned the planes 1000 away and made them 2000 (plus a little because the photos have a tiny bit of overlap) in height and width.
The a-camera is positioned at origin, within this cube, with wasd controls set to false, so the camera is limited to rotating in place. (I am coding on a laptop, using mouse drag to move the camera.)
I also have a sphere (invisible), placed in between the camera and the planes, and have added an event listener to it. This seemed simpler than putting event listeners on each of the six planes.
My current problem is wanting to enforce minimum and maximum tilt limits. Following is the function "handleTilt" for this purpose. The minimum tilt allowed depends on the size of the fov.
function handleTilt() {
console.log("handleTilt called");
var sceneEl = document.querySelector("a-scene");
var elCamera = sceneEl.querySelector("#rotationCam");
var camRotation = elCamera.getAttribute('rotation');
var xTilt = camRotation['x'];
var fov = elCamera.getAttribute('fov');
var minTilt = -65 + fov/2;
camRotation['x'] = xTilt > minTilt ? xTilt : minTilt;
// enforce maximum (straight up)
if (camRotation['x'] > 90) {
camRotation['x'] = 90;
}
console.log(camRotation);
}
The event handler is set up in this line:
<a-entity geometry="primitive:sphere" id="clickSphere"
radius="50" position="0 0 0" mousemove="handleTilt()">
When I do this, a console.log call on #clickSphere shows the event handler exists. But it is never invoked when I run the program and move the mouse to drag the camera to different angles.
As an alternative, I made the #clickSphere listen for onClick as follows:
<a-entity geometry="primitive:sphere" id="clickSphere"
radius="50" position="0 0 0" onclick="handleTilt()">
The only change is "mousemove" to "onclick". Now, the "handleClick()" function executes with each click, and if the camera was rotated to a value less than the minimum, it is put back to the minumum.
One bizarre thing, though, after clicking and adjusting the rotation a few times, the program goes into a state where I can't rotate the camera down below the minimum any more. It is as if the mousemove listener had become engaged, even though the only listener coded is the onclick. I can't for the life of me figure out why this kicks in.
Would it be possible to get some advice as to what I might be doing wrong, or a plan for troubleshooting? I'm new to aframe and JavaScript.
An alternative plan for enforcing the min and max camera tilts in real time would also be an acceptable solution.
I just pushed out this piece on the docs for ya: https://aframe.io/docs/0.6.0/components/look-controls.html#customizing-look-controls
While A-Frame’s look-controls component is primarily meant for VR with sensible defaults to work across platforms, many developers want to use A-Frame for non-VR use cases (e.g., desktop, touchscreen). We might want to modify the mouse and touch behaviors.
The best way to configure the behavior is to copy and customize the current look-controls component code. This allows us to configure the controls how we want (e.g., limit the pitch on touch, reverse one axis). If we were to include every possible configuration into the core component, we would be left maintaining a wide array of flags.
The component lives within a Browserify/Webpack context so you’ll need to replace the require statements with A-Frame globals (e.g., AFRAME.registerComponent, window.THREE, AFRAME.constants.DEFAULT_CAMERA_HEIGHT), and get rid of the module.exports.
Can modify https://github.com/aframevr/aframe/blob/master/src/components/look-controls.js to hack in your min/max just for mouse/touch.
Related
how to detect minimum rotation in a JavaScript gesture library (eg alloyfinger.js)
I have a mobile web app written in JavaScript, using alloyfinger.js. I tried Hammer.js, but it didn't work with certain iPhone models (eg iPhone 7+). I suspect my question is the same for various gesture detection libraries. My app detects rotate events, but I can't seem to write the correct code to ignore rotate events below a minimum angle of rotation. The code below hides the rotated element, even when the amount of rotation is very small. handleRotatEvent(evt) { const angle = evt.angle; const absAngle = Math.abs(angle); const minAngle = (90 * Math.PI / 180); // convert 90 degrees to radians if (absAngle < minAngle) return; hideItem(evt); // use the event to find the target DOM element and hide it } My intent is to ignore rotations smaller than 90 degrees. Otherwise, hide the rotated element. What am I misunderstanding or doing wrong here? Thanks! Adam Leffert https://www.leffert.com
#dntzhang, the author of AlloyFinger.js answered this question on Twitter. He said that the angle resets as the user rotates the element. My more fundamental intent was to detect small rotations as a way to distinguish rotate events from swipe or pan events. He said that the correct way to do that is to check the number of fingers touching the element. One for swipe or pan. Two for rotate. Much simpler.
animating svg pan when manually setting pan.x and pan.y
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.
OrbitControls : updating position and World/View scale of a 'helper'
I'm trying to manage a 3Dobject that visualize the controls target in order to make rotation and zoom more immediatly understandable ... I've made a sample here with an AxisHelper which is supposed to indicate the target and to keep the same size. In OrbitControls.js there are comments on each line I've added. As you can see if you pan and zoom (right click and scroll) it manages cursors too, but the 'helper' has two problems : the position and scale of the helper is set after the renders, that why it seems to be somewhat elastic ... And if I place the position/scale updates into the scope.update() function that's the same thing. the function bellow scales the helper to a constant size, it computes a World/View scale at a defined point (the control's target) from a unit vector. But it seems it's not the good solution because when you scroll to the max zoom the helper is growing. var point = new THREE.Vector3( 1, 0, 0 ); point = point.applyMatrix4( scope.object.matrixWorld ); var scale = point.distanceTo( scope.target ); helper.scale.set(scale, scale, scale); So if you have an idea to achieve this you are welcome ...
i'm currently developing something similar with threejs and i think that: I can't see the demo, my proxy not allow it. I think that is correct...if the object helper is a mesh or a sprite added to the scene when orbit controls zoom the camera became more near to the objects in the scene, the objects dimension remain the same.
Making Javascript TileEngine Scrollable in Canvas
The basis for the code is from John E. Graham's blog http://johnegraham2.com/blog/2010/09/25/project-javascript-2d-tile-engine-with-html5-canvas-part-4-using-zones-for-further-optimization/ It works perfectly for drawing a screen's worth of tiles, but I cannot for the life of me figure out how to adjust it 1 row/column at a time based on pressing up, down, left, or right keys. Here is an example with the transparency to help visualize the zones http://simplehotkey.com/Javascript/canvas.html (loading positions of 1,188 tiles but only draws a couple hundred to fill the screen) I had it loading an array with 70,000 entries and it was still quick because it's only drawing whats on the screen, but cannot figure out how to slide everything based on input... I've come up with a couple ideas and am not sure what's the best way. One screen worth of tiles is shown here: tilesArray = [ 0,0,0,0,0,0,0,1,2,9,6,0,0,7,0,0,1,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,9,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,9,0, 0,9,9,9,9,9,9,0,7,2,0,0,0,0,0,1,2,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,9,0,7,2,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; Where 0 is a wall tile (around perimeter), 9 a floor tile, 7 a door and a couple other random tiles. That is exactly what is loaded to the screen, but I cannot figure out how to shift everything 1 tile in either direction based on input, up, down, left, right. The one idea I'm leaning towards now, is just to use that array above as the basis for rendering, and somehow feeding the new values into it based on keyboard input, from another array. Maybe slicing from another, much larger array (holding all the tiles for the entire level) and using that slice to populate the array that's actually rendered??? That's replacing every tile every frame though... for getting player input I was using: //Key listener document.onkeydown = function(e){ e = e?e:window.event; console.log(e.keyCode + "Down"); switch (e.keyCode){ case 38: //UP KEY Game.inputReaction('up'); //Game.moveDir('up'); break; case 40: //DOWN KEY //Game.inputReaction(40); //Game.moveDir('down'); break; case 37: //Left Key //Game.inputReaction(37); break; } } The other alternative is to try to adjust the tiles already on the screen and add new tiles but this engine isn't using global variables so I'm not sure how to affect the tile engine programatically based on input....like I can add another method (inputReaction(num)) and trigger some actions from my keyboard input (console.log()) but I can't access the other methods actually drawing the tiles. Or maybe I have to make a copy of the object, change it and return it? but it's pretty complex. I think it might be easier to adjust the array values that are being fed into the "engine" (array above) rather than changing around how the engine is calculating what's being drawn. Can you confirm this?
Add a camera abstraction that you can move around on the map, then shift the drawing positions according to the camera position. When the camera moves south 10px, all tiles move north 10px, same with east and west. Since you only draw the tiles that are visible, there won't be much of a performance loss. The renderer looks at the camera to figure out what needs to be drawn and you can expose the camera object to the outside to manipulate it. That way you only need to change the camera position to change what is shown on the screen. I did this in a proof of concept tiling engine a year ago and I was able to smoothly scroll and scale huge tilemaps. If you start changing the array itself, your performance will suffer and you won't be able to scroll smoothly since you can only go in steps of one tile and not one pixel.
Calculate current (nearest) side of rotated cube
I would like to find the current side and update the $currentFace property in my Cube class, every time the cube is rotated. Here is my JSFiddle: http://jsfiddle.net/joecritch/tZBDW/ As you can see, I am updating the $currentFace successfully when I directly go to a face, using the menu. However, I am using a different approach for keyboard controls (using a 3D Matrix so that the cube doesn't go onto an incorrect axis). What's the best way to calculate this? I have an event handler for afterRotation, so the calculation code can be placed in there. __afterRotation: function(e) { // ... // Insert calculation code? // ... }