Using the Google Earth plugin, I want to be able to allow the user to select placemarks on the ground while the camera is moving, but am not sure how this is possible. It seems that when you call setAbstractView(), even with the flyToSpeed set to SPEED_TELEPORT, the Google Earth plugin ignores any mouse down events except for those to the GEGlobe.
Here's the code, altered slightly (from http://code.google.com/apis/ajax/playground/#draggable_placemark) to illustrate my issue:
var ge;
var placemark;
var dragInfo = null;
var la;
var lat = 37;
var lon = -122;
google.load("earth", "1");
function init() {
google.earth.createInstance('map3d', initCallback, failureCallback);
}
function tick() {
la.set(lat, lon,
0, // altitude
ge.ALTITUDE_RELATIVE_TO_GROUND,
0, // heading
0, // straight-down tilt
5000 // range (inverse of zoom)
);
ge.getView().setAbstractView(la);
lon = lon + 0.00000001;
}
function initCallback(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
// add a navigation control
ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
// add some layers
ge.getLayerRoot().enableLayerById(ge.LAYER_BORDERS, true);
ge.getLayerRoot().enableLayerById(ge.LAYER_ROADS, true);
// create the placemark
placemark = ge.createPlacemark('');
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(lon);
placemark.setGeometry(point);
// add the placemark to the earth DOM
ge.getFeatures().appendChild(placemark);
// look at the placemark we created
la = ge.createLookAt('');
placemark.setName('Drag Me!');
ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT);
tick();
// Comment this next line out and the drag works as expected.
google.earth.addEventListener(ge, "frameend", tick);
// listen for mousedown on the window (look specifically for point placemarks)
google.earth.addEventListener(ge.getWindow(), 'mousedown', function(event) {
console.log("target type = " + event.getTarget().getType());
if (event.getTarget().getType() == 'KmlPlacemark' &&
event.getTarget().getGeometry().getType() == 'KmlPoint') {
//event.preventDefault();
var placemark = event.getTarget();
dragInfo = {
placemark: event.getTarget(),
dragged: false
};
}
});
// listen for mousemove on the globe
google.earth.addEventListener(ge.getGlobe(), 'mousemove', function(event) {
if (dragInfo) {
event.preventDefault();
var point = dragInfo.placemark.getGeometry();
point.setLatitude(event.getLatitude());
point.setLongitude(event.getLongitude());
dragInfo.dragged = true;
}
});
// listen for mouseup on the window
google.earth.addEventListener(ge.getWindow(), 'mouseup', function(event) {
if (dragInfo) {
if (dragInfo.dragged) {
// if the placemark was dragged, prevent balloons from popping up
event.preventDefault();
}
dragInfo = null;
}
});
document.getElementById('installed-plugin-version').innerHTML =
ge.getPluginVersion().toString();
}
function failureCallback(errorCode) {
}
If you comment out line 56, where tick() is called at each frameend, everything works as in the unaltered code: you can successfully drag the place mark. But when line 56 is in, you can't. So the problem is really with setAbstractView keeping mousedown events from propagating to either the globe or whatever placemark or feature was being clicked.
Any ideas? Is there a workaround for this?
The issue isn't caused by the setAbstractView method, it is caused because of the repeated calls to the tick method via framend.
To explain, you have set up the tick method as an event handler for the frameend event.
Then the tick method updates the view immediately, triggering the frameend event, ad infinitum ...
This pattern causes an issue with the browser message loop, in effect it is blocking the other drag events. Think of it like a very tight loop that is causing a deadlock. To work it you can use setTimeout with a value of 0 to execute the code. This way the animation won't be processed until all other pending drag messages are processed.
The key part is a the modification to your tick() method.
function tick() {
// prevent deadlock
setTimeout(function () {
la.set(lat, lon, 0, ge.ALTITUDE_RELATIVE_TO_GROUND, 0, 0, 5000);
ge.getView().setAbstractView(la);
lon += 0.00001;
}, 0);
};
Here, I made a fully working example for you http://jsfiddle.net/fraser/JFLaT/
I tested it and it is working in Chrome, IE, Firefox on Windows 8 and Chrome, Safari, Firefox on OSX.
Related
I'm using a camera and barcode markers. In order for my cannon to shoot I need to spawn a sphere when a clicked. I tried to get the marker position by using .getAttribute('position') but I'm getting disappointing results e.g. null and [object, object]. Is there a real way to to access the coordinates of a marker in AFrame? So far it creates a sphere but right in the camera because its unable to find the location of the marker.
Javascript
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('#cannonMarker');
// trigger event when button is pressed.
document.addEventListener("click", function (e) {
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
console.log('Sphere coordinates:' + pos);
});
Provided the marker is being recognized and the references are correct
markerEl.getAttribute('position')
should return the current marker position.
If your script is in the <head> element, the marker may not yet exist when the code is executed.
It's a good idea to throw your code into an a-frame component:
HTML:
<a-scene ball-spawner></a-scene>
js:
AFRAME.registerComponent('ball-spawner', {
init: function() {
// your code here - the scene should be ready
}
})
I've made a slight modification to your code (working glitch here):
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('a-marker');
// trigger event when button is pressed.
sceneEl.addEventListener("click", function (e) {
if (markerEl.object3D.visible === false) {
return;
}
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('radius', 0.5)
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
});
}
When the marker gets out of vision, it's position is 'last remembered'. So it's the same place on the screen. That's why there's a return if the marker element is not visible.
It seems to be working as you want since the ball is falling out of a transparent box on the marker (the box is a nice way to be sure the marker is recognized):
I'm trying to develop a function with leaflet which make user be able to draw a circle by pressing ctrl & dragging mouse, as the following
let mouseDownPos = null
let mouseUpPos = null
L.Map.CircleSelector = L.Map.Drag.extend({
_onMouseDown: function(e) {
if (!e.ctrlKey)
return
let map = this._map
map.dragging.disable()
mouseDownPos = map.containerPointToLatLng(this._point)
},
_onMouseUp: function(e) {
if (!e.ctrlKey) {
this._map.dragging.enable()
return
}
let map = this._map
mouseUpPos = map.containerPointToLatLng(this._point)
let radius = map.distance(mouseDownPos, mouseUpPos)
L.circle(mouseDownPos, {radius: radius}).addTo(map)
map.dragging.enable()
}
})
L.Map.mergeOptions({circleSelector: true})
L.Map.addInitHook('addHandler', 'circleSelector', L.Map.CircleSelector)
When I press ctrl & drag mouse on the map, it still does not work.
I've tried to print text to console at the beginning in _onMouseDown(), it shows nothing.
It seems that the event doesn't trigger.
What should I need to modify? Thank you.
Finally I extend leaflet.draw to approach my goal.
Refer to the source code of L.Draw.Circle, I extend my selector from L.Draw.Circle. The mainly modified part is in _onMouseUp, as the following
L.Map.CircleSelector = L.Draw.SimpleShape.extend({
_onMouseUp: function (e) {
// TODO
// 1. Get the circle center & radius
// 2. Calculate distances between center & markers
// 3. If the distance in step 2 <= radius, it is in the circle
// 4. Anything you'd like to do......
}
})
The rest code of the event can be referred to L.Draw.Circle, e.g., addHooks, _onMouseMove......
I am trying to tilt an image based on HTML5 DeviceOrientation event. However, I am seeing that the event is getting continuously fired even when the device is stable i.e non rotating/non moving. In the following code snippet, the console log is printed continuously. What is the possible reason, how can I stop it.
I tried both capturing and bubbling,
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', function(eventData) {
var tiltLR = eventData.gamma;
console.log("tiltLR..........",tiltLR);
}, false);
}
I havent needed to use this type of event listener before so I am not familiar with the output.
However, I believe you would need to compare the old tilt with the new tilt. If the new tilt is substantially greater or less then... execute code.
if (window.DeviceOrientationEvent) {
var originalTilt = undefined,
tolerance = 5;
window.addEventListener('deviceorientation', function(eventData) {
if (eventData.gamma > originalTilt + tolerance ||
eventData.gamma < originalTilt - tolerance){
var tiltLR = eventData.gamma;
console.log("tiltLR..........",tiltLR);
originalTilt = tiltLR;
}
}, false);
}
The basic functionality I'm going for is...
Tap (mousedown) on the stage to create and add a child at that location.
*EDIT: I'm also trying to solve for multitouch, so multiple balls can be created at the same time.
As you hold down you can drag (pressmove) that child around and it grows (using regX/regY/scaleX/scaleY) until you release (pressup), at which point it falls (using a tick cycle).
I have everything sort of working, but I've hit a snag wherein I can't add a child and have it immediately register mousedown without releasing and pressing again.
Is there a way to manually trigger mousedown after adding, or some other workaround that makes more sense? dispatchEvent doesn't seem to be working.
Here's my stage event listener and touch bits:
stage.enableMouseOver(10);
createjs.Touch.enable(stage, false, false);
stage.preventSelection = false;
stage.addEventListener("stagemousedown", spawnsnowball);
And here are my functions. The spawnsnowball one includes displayObject event listeners verging on desperate, but the only way I've been able to get the pressmove and pressup working is to click on the same snowball again. releasesnowball right now just releases all instances of them (using a 'stagemouseup' listener), but if I can get it triggering off of pressup then I'll rewrite it to target just the event target.
function spawnsnowball(evt){
var ball = new createjs.Bitmap(loader.getResult("snowball"));
ball.crossOrigin="Anonymous";
ball.name="ball";
ball.scaleX = 0.5;
ball.scaleY = ball.scaleX;
ball.regX = ball.image.width/2;
ball.regY = ball.image.height/2;
ball.x = evt.stageX;
ball.y = evt.stageY;
ball.type = balltype;
ball.holding = 1;
ball.velX = 0;
ball.velY = 0;
ball.addEventListener("pressup",releasesnowball);
ball.addEventListener("pressmove",dragsnowball);
ball.onPress = function(mouseEvent) {};
stage.addChild(ball);
ball.dispatchEvent("mousedown");
ball.dispatchEvent("pressdown");
}
function dragsnowball(evt){
evt.target.x = evt.stageX;
evt.target.y = evt.stageY;
}
function releasesnowball(evt){
for(var i=0;i<stage.getNumChildren();i++){
var shape = stage.getChildAt(i);
if(shape.type == balltype){
if(shape.holding){
shape.holding = 0;
var dX = shape.x - shape.oldX;
var dY = shape.y - shape.oldY;
if(Math.abs(dY)>8)
dY = 8*dY/Math.abs(dY);
if(Math.abs(dX)>3)
dX = 3*dX/Math.abs(dX);
}
}
}
}
The pressmove event is special because it basically stores off the target of the last mousedown event, and then remembers it for pressmove and pressup events.
This means you can't really fake the event by forcing mouse events. Dispatching a mouse event from the target will not do the trick.
Instead, consider just handling the initial drag manually. You already know what you want to be the target of the pressmove, so you can listen for the stagemousemove event, and handle it yourself:
// Listen to the stagemousemove and manually call the event.
var initialDrag = stage.on("stagemousemove", function(event) {
event.target = ball; // Re-target the event so your other method works fine.
dragsnowball(event);
});
// When done, remove the move listener.
// The off() method supports a "once" parameter so you don't have to unsubscribe that listener.
stage.on("stagemouseup", function(event) {
stage.off("stagemousemove", initialDrag);
}, null, true); // Fires one time
Here is a quick sample using your code as the base: http://jsfiddle.net/3qhmur82/
I also added some comments in the demo which might be useful.
Hope that helps!
in a map I want when the user clicks inside the map to be able to draw with his mouse a scheme. For example it clicks and then circles an area just by moving his mouse. The problem is that when he circles the area, continues to draw at the current mouse position.
In other words I want the user to click for start drawing and click again to stop it.
I am using addEventListener and removeEventListener
I am trying to remove the events from the map but it does not work.
Any ideas?
Code:
map.addEventListener('click', goInter, false);
function goInter() {
map.addEventListener('mousemove', move, false);
map.addEventListener('click', close, false);
var pointList = [];
var first_element;
var trig = 0;
function move(e) {
map.dragging.disable();
pointList[pointList.length] = new L.LatLng(e.latlng.lat, e.latlng.lng);
first_element = pointList[0];
//ama to valw den doulebei tipota!!
//document.getElementById("elements").innerHTML = pointList[ii++];
if (pointList.length > 400){
pointList = [];
}
var line = new L.Polyline(pointList, {
color: 'red',
weight: 2,
opacity: 0.5,
});
map.addLayer(line);
if (trig == 1){
map.preventDefault();
alert("den einai 1");
return false;
}
};
function close(){
//map.removeEventListener ('mousemove', move, false);
var last_points = [first_element, pointList[pointList.length-1]];
var last_connection = new L.Polyline(last_points, {
color: 'blue',
weight: 1,
opacity: 0.9
});
map.addLayer(last_connection);
trig = 1;
map.preventDefault();
//map.removeLayer(line);
//map.fitBounds(line.getBounds());
};
};
I have a progress -> i changed the two click events to -> mouseup and mousedown respectively. That gives me a better interaction. when the user releases the button it stops drawing. However if you press it again it draws. ALSO uncomment first line of close() function