I would like to create a map annotation tool with custom controls (not using map providers built-in annotation features).
Something like this:
My idea was to overlay a canvas above the map.
The problem is: I can't zoom on the map by scrolling anymore because of that overlay.
And if I set pointer-events: none to the canvas, then I can't draw on the canvas anymore.
Demo: https://codepen.io/vandrieu/pen/qBqdpzX?editors=1010
I would like to be able to draw on the canvas while still being able to zoom the map by scrolling the mouse.
How would you do this?
Using Rojo's comment I started looking into event dispatching and wrote this method that dispatches an event to another
function redirectEvent(eventType, fromElementSelector, toElementSelector) {
const fromElement=document.querySelector(fromElementSelector)
const toElement=document.querySelector(toElementSelector)
fromElement.addEventListener(eventType, function (event) {
toElement.dispatchEvent(new event.constructor(event.type, event));
event.preventDefault();
event.stopPropagation();
//event.stopImmediatePropagation();
});
}
Then I struggled,
First because scroll event didn't work. Until I figured out the right event for this is not scroll, it's wheel.
Then because dispatching the event this way:
redirectEvent("wheel", '#canvas2', '#map2')
Didn't work.
I figured maybe I needed to send the event to the canvas inside the map (the map is rendered using a canvas).
So I changed to:
redirectEvent("wheel", '#canvas2', '#map2 canvas')
But it didn't work either.
The reason is when page loads, the canvas inside the map is not rendered yet.
But if I wait a little:
setTimeout(() => {
redirectEvent("wheel", '#canvas2', '#map2 canvas')
},2000)
It works!
See the updated demo (map number 2): https://codepen.io/vandrieu/pen/qBqdpzX?editors=1010
Related
I am using p5.js library to implement some visuals, along with reactJS.
What I have implemented: When I click and drag my mouse on screen, the background(say canvas) changes color. This is implemented using p5.js
What I want: I have created a draggable HTML element. However, When i click and drag my new element, the canvas also responds to the click and changes color. I want my click to only affect the new layered element, not the canvas.
I have tried setting the new element to be at higher z-Index and canvas container to be at a lower one. However, this doesn't work. Any help would be appreciated!
Without having a look at your code, my best guess is that you have to stop the propagation of the event. So in your dragStart event, you could do this:
onDragStart={ e => {
e.stopPropagation();
// your code
}
From the mdn docs:
The stopPropagation() method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases
I'm creating a web application that uses Paper.js to manipulate vectors that are imported through SVG. Up until recently, everything worked. Then, all of a sudden, the onMouseDown event stopped getting triggered, even though onKeyDown and onMouseMove trigger just fine.
I create an instance of a Tool object like this:
paper.setup("canvas");
var tool = new paper.Tool();
tool.activate();
Then I bind events later in the code like this:
tool.onMouseDown = function (event) {
console.log("Mouse down triggered!");
}
tool.onMouseMove = function (event) {
console.log("Mouse move triggered!");
};
For some reason the latter does trigger, but the former does not. Other events like onKeyDown and onKeyUp trigger as well, but onMouseDown and onMouseDrag don't do anything. Does anyone have an idea what's causing this? I'm also using jQuery and jQueryUI, maybe that causes some kind of interference?
I had the same effect. It was caused by an overlaying html element eating up the click events. Add
style="pointer-events:none;"
to the elements html definition and then both events should trigger you paper.js object.
The full line reads:
<div id="colorizerOptions" style="pointer-events:none;"></div>
OpenLayers3 API has a map.on("moveend") , however I cannot find a movestart. Any one know how I can achieve this? Is there a equivalent event?
OpenLayers 2 had a movestart event on map. I am looking an exact parallel in OpenLayers3
Here's a basic jsFiddle. If someone want's to play around. I did add a movestart event there, to show what I want, but it doesn't actually exist I think.
Use Case! one might ask: I have stops on maps that have nearly fullscreen infowindows. Users can switch to next marker from infowindow. I make the windows translucent to show the map panning underneath, so users get a context of where next location is. This work's great in OpenLayers2 with movestart and moveend events. But in the new OL3 version of the map, I can't get the movestart event.
Update: I did answer the question my self, but I am still offering bounty if anyone would like to propose a better solution.
UPDATE v4.2.0 now supports native movestart and moveend events
map.on('movestart', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
map.on('moveend', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
For OpenLayers 3 versions before release of v4.2.0
Okay so in the mean while without the movestart event, and with the moveend only triggering if there is a actual movement in map, here's how I was able to achive movestart and moveend behavior.
jsFiddle:
var pan = ol.animation.pan({
duration: 700,
source: this.map.getView().getCenter()
});
map.beforeRender(function(map, frameState) {
var stillPanning = pan(map, frameState); // returns false panning is done
if (stillPanning) {
// do movestart stuff here
if (!everDone) {
doSomething();
everDone = true;
}
} else {
// do move end stuff here
undoSomething();
everDone = false;
}
return stillPanning;
});
map.getView().setCenter(geom);
So why this works?
ol.animation.pan returns a ol.PreRenderFunction, which returns false if animation is not complete
Writing custom function and providing it to map.renderBefore can be now used to write a wrapper around pan animation as shown above
The whole business with everDone is because, stillPanning section will get called multiple times. This is okay if what you want to do there can take repeated calls, but if you want to toggle something then you want to do it only once.
behavior of 'moveend'
moveend callback is only triggered if map actually moves. This is fine, but it prevents us from doing pre-animation activities, by just doing them before animation done. If you had a scenario where map doesn't actually move, then what ever you did before animation will never undo because that behavior is in moveend which never gets called!
Hope this helps someone. I had to spend good two hours to get it to work for me, because a movestart callback is absent :(
UPDATE
Upon more discussion on this thread there is another solution as suggested by #ahocevar. That is to use the propertychange event on the view like so:
function onpropertychange() {
map.dispatchEvent('movestart');
var view = map.getView();
view.un('propertychange', onpropertychange);
map.on('moveend', function() {
view.on('propertychange', onpropertychange);
});
};
map.getView().on('propertychange', onpropertychange);
Here's a working example of this approach: jsFiddle
you could use the pointerdrag or pointermove event, but you will want to debounce them. I did so here with a variable called dragStarted
http://jsfiddle.net/sean9999/j2cP4/115/
http://openlayers.org/en/v3.8.2/apidoc/ol.MapBrowserEvent.html
I have created a Kineticjs scene with a scrollable background. On top of this background is a rect with an event listener attached. My question is, what could cause this event listener to not fire after I move the background and draw the scene. I don't know what it could be and I am even calling .moveToTop() on everything to see if I could move the shape back to the top even though I am pretty sure it still is. So what could cause an event listener to stop firing after a draw?
Does anybody know a fix for this? I figure I could might be able to attach another event listener to the shape, but this seems unnecessary if I am doing something fundamentally wrong.
Thanks in advance!
Edit: Here is some code.
// Here is the event listener attached to scroll bar
hscroll4.on('dragmove', updateBackgroundPos4);
// Here is code to move background
var updateBackgroundPos4 = function() {
var hscrollPos = hscroll4.getX();
console.log("mb4: ", mainBody4Dynamic.getX());
mainDynamicWrapper.setX(-hscrollPos+120); //the BG I am moving on scroll
// my attempt at getting it to work
dynamicLayer4.moveToTop();
mainBody4.moveToTop();
mainDynamicWrapper.moveToTop();
newFolder.moveToTop();
dynamicLayer4.draw();
//stage4.drawHit();
};
// Here is the group which contains the rectangle
folderGroup.on('mouseover', function() { //---- this isn't firing after draw
document.body.style.cursor = 'pointer';
console.log("change text");
updateTestText();
});
folderGroup.on('mouseout', function() {
document.body.style.cursor = 'default';
});
Edit 2: even my suggestion does not work. Adding the event listener back does not make it clickable again :(
Alright I got it! I found a thread which said to create an dragend event on the scrollbar then call layer.moveToTop() and layer.draw(). So putting it in dragmove was not enough for the program to register click events. The reasoning behind this was:
"When you use a drag event, KineticJS create a temporary layer on top as a result of which your events where not getting registered after the dragmove."
Here is the thread if anybody is interested:
LINK TO THREAD
The Google maps API v3 has a callback on map zoom_changed but that gets triggered before the zoom starts (when I click the zoom in/out button). The state of the map inside the callback function is the one before zooming, I want the one after the zoom.
Is there such a callback?
Thanks
Edit: The link was deleted.
It seems like a bug in the API.
What most people try to do is basically the following:
google.maps.event.addListener(map,'zoom_changed',function (event) {
// some handling code here
});
But that won't work as the event fires before the bounds change. What is suggested to do in this case is the following:
zoomChangeListener = google.maps.event.addListener(map,'zoom_changed',function (event) {
zoomChangeBoundsListener = google.maps.event.addListener(map,'bounds_changed',function (event) {
console.log(map.get_bounds());
google.maps.event.removeListener(zoomChangeBoundsListener);
});
});
So now, after the zoom_changed event fires, we actually set another listener, this time for the bounds_changed event, so at the time of this event firing, we are sure that the bounds have changed.