Changing cursor on drag in openlayers 3 - openlayers-3

What are the proper way of changing the cursor when user is dragging the map. Below example is not that good as it only triggers when pointerdrag starts the drag and then change it back after no events for 125ms. Is there any other way?
var timer = null;
this.map().on("pointerdrag",() => {
this.map().getViewport().style.cursor = "-webkit-grabbing";
clearTimeout(timer);
timer = setTimeout(() => this.map().getViewport().style.cursor = "-webkit-grab", 125);
});

What about listening to pointerup to reset the cursor?
map.getViewport().style.cursor = "-webkit-grab";
map.on('pointerdrag', function(evt) {
map.getViewport().style.cursor = "-webkit-grabbing";
});
map.on('pointerup', function(evt) {
map.getViewport().style.cursor = "-webkit-grab";
});
http://jsfiddle.net/9vwgdcyr/

Related

$(window).scroll(function () {}) is not working

I'm trying to perform some action on window scroll event but it is not working.
Here is my code
$(window).scroll(function () {
// var limit = 7; //The number of records to display per request
var lastID = $newsfeed_start;
if (($(window).scrollTop() == $(document).height() - $(window).height()&& (lastID != 0)) {
// increment strat value
$newsfeed_start = $newsfeed_start + $newsfeed_limit;
get_timeline_post('');
}
});
even $(window).scroll(function () { } ) function is not working
Your CSS is actually setting the rest of the document to not show overflow therefore the document itself isn't scrolling. The easiest fix for this is bind the event to the thing that is scrolling, which in your case is div#page.
So its easy as changing:
$(document).scroll(function() { // OR $(window).scroll(function() {
didScroll = true;
});
to
$('div#page').scroll(function() {
didScroll = true;
});

iOS - Google Maps Places onclick conflicts with map onclick

I have a project created with ionic. In here I have a Google Map with a places search box.
I have included the Google Map library like so:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=SOMEKEY&libraries=places" defer></script>
Now in my TypeScript code I have the following
// init the google map
initMap() {
var centerOfMap;
var draggableMarker = false;
this.mapHasBeenInitialized = true;
this.isStaticLocation = false;
centerOfMap = new google.maps.LatLng(this.locationService.gpsLat, this.locationService.gpsLong);
draggableMarker = true;
var options = {
center: centerOfMap,
zoom: 11,
fullscreenControl: false,
disableDefaultUI: true, // dont allow default zoom/sattelite/street view
gestureHandline: 'cooperative' // disable moving map with one finger
};
this.map = new google.maps.Map(this.googleMap.nativeElement, options);
this.marker = new google.maps.Marker({
position: centerOfMap,
map: this.map,
draggable: draggableMarker
});
if(!this.isStaticLocation) {
var searchBox = new google.maps.places.SearchBox(this.googleInput.nativeElement);
// add the searchbar to the google map
this.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.googleInput.nativeElement);
// Bias the SearchBox results towards current map's viewport
this.map.addListener('bounds-changed', () => {
searchBox.setBounds(this.map.getBounds());
});
searchBox.addListener('places_changed', () => {
var places = searchBox.getPlaces();
if(places.length == 0) {
return;
}
var bounds = new google.maps.LatLngBounds();
places.forEach(place => {
if(!place.geometry) {
console.log("returned place contains no geometry");
return;
}
this.setMarkerLocation(place);
if(place.geometry.viewport) {
bounds.union(place.geometry.viewport);
} else {
bounds.extends(place.geometry.location);
}
});
this.map.fitBounds(bounds);
});
}
if(draggableMarker) {
google.maps.event.addListener(this.marker, 'dragend', (event)=>{
this.getMarkerLocation();
});
}
google.maps.event.addListener(this.map, 'click', (event: any)=> {
var clickedLocation = event.latLng;
this.marker.setPosition(clickedLocation);
this.getMarkerLocation();
});
// neccessary for reload. Made async to trick loading process
setTimeout(()=> {
google.maps.event.trigger(this.googleMap.nativeElement, 'resize');
this.map.setCenter(centerOfMap);
}, 100);
}
// function to set the location marker on a different spot
setMarkerLocation(place: any) {
this.marker.setMap(null);
this.marker = new google.maps.Marker({
position: place.geometry.location,
map: this.map,
draggable: true
});
this.getMarkerLocation();
}
getMarkerLocation() {
var currLoc = this.marker.getPosition();
this.locationService.setGoogleMapsLocation(currLoc.lat(), currLoc.lng());
this.locationChanged = true;
}
And this code works like a charm in the browser and on Android. Basically what the code does is whenever someone taps on the map, the marker position changes to their tap location.
When a person searches for a Place, the places dropdown will show over the map. On android, when you tap a place in this dropdown, the marker will go to the selected place (f.e. australia).
On iOS however, the marker will position itself on the location where the person tapped and will totally ignore the tap on the place dropdown.
So when I'm in Europe and I type in 'Australia' and select 'Australia' from my dropdown, on Android I'll go to australia but on iOS I'll stay somewhere in Europe wherever the dropdown was positioned.

How can I detect a zoom end?

Is there a good way to detect when the map's zoom animation has ended? OpenLayers used to raise the 'zoomend' event after the zoom had completed, but OpenLayers 3 doesn't have a corresponding event. I'm currently using the following approach, but it seems kludgy and brittle at best.
function main() {
var map = ...;
map.getView().on('change:resolution', handleResolutionChange);
}
function handleResolutionChange() {
var map = ...;
map.once('moveend', handleMoveEnd);
}
function handleMoveEnd() {
setTimeout(handleZoomEnd, 0);
}
function handleZoomEnd() {
//Handle the 'Zoom end' event
}
did you try the moveend event on its own???? I have not try it but it should rise on zoomend as well. Also the 'change:resolution' event is not documented. Does it really work??
try the following
var ghostZoom = map.getView().getZoom();
map.on('moveend', (function() {
if (ghostZoom != map.getView().getZoom()) {
ghostZoom = map.getView().getZoom();
console.log('zoomend');
}
}));
I know this question has been a while. I just want to share my idea.
let isMapResolutionChanged;
map.getView().on('change:resolution', () => {
isMapResolutionChanged = true;
});
map.on('moveend', () => {
if (isMapResolutionChanged) {
console.log('fire moveend + zoomend')
}
});
Just wanted to share my solution because I stumbled over the same problem:
let zoomend = function(evt) {
alert('zoomend on resolution: ' + evt.map.getView().getResolution());
evt.map.once('moveend', function(evt) {
zoomend(evt);
});
};
map.getView().once('change:resolution', function(evt) {
map.once('moveend', function(evt) {
zoomend(evt);
});
});
Here the change:resolution event is only fired once at the beginning of a zoom action and is activated again when its finished.
You can have a look at a working fiddle.

Latency issue with Primefaces overlayPanel - loads to lazy

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.
I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:
<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>
This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible.
For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.
I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives.
This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.
Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.
Is this a good appraoch or does anyone know any other way to solve this nasty problem?
Thanks Pete
As my first answer is already very long and contains valid information, I decided to open a new answer presenting my final approach.
Im now using Primefaces inheritance pattern making the code alot cleaner. Also I noticed that replacing/overwriting the whole bindEvents function isnt necessary, as we can remove the old event handlers. Finally this code fixs the latest issue experienced: A hide event before ajax arrival.
PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
.extend({
bindEvents : function() {
this._super();
var showEvent = this.cfg.showEvent + '.ui-overlay', hideEvent = this.cfg.hideEvent
+ '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(
showEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.timer = setTimeout(function() {
_self.hidden = false;
_self.show();
}, 300);
}).on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hidden = true;
_self.hide();
});
},
_show : function() {
if (!this.cfg.dynamic || !this.hidden) {
this._super();
}
}
});
Im sorry for the poor formatting: Eclipses fault ;)
Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
if(_self.isVisible()) {
_self.hide();
}
});
As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:
The widget isnt visible at all
The animation is still going on
In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:
PrimeFaces.widget.OverlayPanel.prototype.bindEvents = function() {
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent == this.cfg.hideEvent) {
var event = this.cfg.showEvent;
$(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
e.data.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlay',
hideEvent = this.cfg.hideEvent + '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
if(!_self.isVisible()) {
_self.show();
}
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.hide();
});
}
//enter key support for mousedown event
this.bindKeyEvents();
var _self = this;
//hide overlay when mousedown is at outside of overlay
$(document.body).bind('mousedown.ui-overlay', function (e) {
if(_self.jq.hasClass('ui-overlay-hidden')) {
return;
}
//do nothing on target mousedown
var target = $(e.target);
if(_self.target.is(target)||_self.target.has(target).length > 0) {
return;
}
//hide overlay if mousedown is on outside
var offset = _self.jq.offset();
if(e.pageX < offset.left ||
e.pageX > offset.left + _self.jq.outerWidth() ||
e.pageY < offset.top ||
e.pageY > offset.top + _self.jq.outerHeight()) {
_self.hide();
}
});
//Hide overlay on resize
var resizeNS = 'resize.' + this.id;
$(window).unbind(resizeNS).bind(resizeNS, function() {
if(_self.jq.hasClass('ui-overlay-visible')) {
_self.hide();
}
});
};
Execute this code on load and the issue should be gone.
As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.timer = setTimeout( function(){
if(!_self.isVisible()) {
_self.show();
}
}, 300);
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hide();
});
Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.
At the same time I thank you for this brilliant solution I take the opportunity to update it for Primefaces 5.2. In our application the code broke after that upgrade.
Follows the updated code for Primefaces 5.2:
PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents = function() {
var $this = this;
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent === this.cfg.hideEvent) {
var event = this.cfg.showEvent;
this.target.on(event, function(e) {
$this.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
this.target
.off(showEvent + ' ' + hideEvent)
.on(showEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
$('.ui-overlaypanel').hide();
$this.hidden = false;
$this.show();
}, 500);
})
.on(hideEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
// don't hide if hovering overlay
if(! $this.jq.is(":hover")) {
$this.hide();
}
}, 100);
});
}
$this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
e.preventDefault();
}
})
.on('keyup.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
$this.toggle();
e.preventDefault();
}
});
};
I also added an extra feature which allows the user to move the mouse over the overlay without hiding it. It should hide when you move the mouse out of it then which I accomplished through:
<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>
function onShowOverlayPanel(ovr) {
ovr.jq.on("mouseleave", function(e) {
ovr.jq.hide();
});
}
Hope you enjoy!
It's been a long time, but in case anyone bumps into this problem, a showDelay attribute was added to the overlayPanel to solve this problem starting from Primefaces 6.2. However, it is not in the official documentation for some reason.

using http://code.google.com/p/jquery-ui-map/

using jquery-ui-map
here is my code
$(document).ready(function() {$('#map_canvas').gmap({ 'center': new google.maps.LatLng(3.162456,21.09375), 'zoom': 2, 'streetViewControl': false, 'callback':
function() {
$('#map_canvas').gmap('loadHTML', 'microformat', '.importers', function(markerOpts, node, index) {
var clone = $(node);
// We have to add a callback in the addmarker method so we can access the marker just added
var name = $(node).find('.name');
var icon = $(node).find('.icon');
$('#map_canvas').gmap('addMarker', jQuery.extend({ 'title': name.html(), 'icon':new google.maps.MarkerImage(icon.html())}, markerOpts), function(map, marker) {
$(name).click( function() {
$(marker).triggerEvent('click');
return false;
});
}).click(function() {
$('.reveal').removeClass('reveal');
$(this).get(0).map.panTo($(this).get(0).position);
$(clone).toggleClass('reveal');
//need to wait till pan has complete before doing zoom!
});
});
}
});
});
at the moment when you click on a market it pans to its position
what i want to do is also zoomin, i've tried just adding
$(this).get(0).map.setZoom(5, true);
but this means the pan does not work it just jumps straight to the zoom level, how do i get it to fire the $(this).get(0).map.setZoom(5, true); after the panning is done?
thanks in advance
$('#map_canvas').gmap('addMarker').click(function() {
$(this).get(0).map.panTo($(this).get(0).position);
var self = $(this).get(0);
setTimeout(function() { self.map.setCenter(self.position); self.map.setZoom(15); }, 2000);
});
This would be the easy way of setting the zoom after the pan.

Resources