I am using OpenLayers to display a map, and AdminLte for the interface.
My problem : When collapsing the main left sidebar on a given page, all the boxes and what it contains changes size (I don't really know how) so the maps gets bigger as well. The problem is that when it happens, all the features displayed on the maps apparently change position and are not where they are supposed to be anymore.
What I would like: To redraw the map after the sidebar collapses.
Any suggestion?
I tried:
$('.navbar-collapse').on('shown.bs.collapse', function() {
map.updateSize();
});
and:
$('.sidebar').on('shown.bs.collapse', function() {
map.updateSize();
});
but to no avail...
EDIT : My question is similar to this one: OpenLayers: How to re-align mouse coordinates and vector layers after fluid css rendering of map div but his solution doesn't work for me :)
EDIT 2 : Just to clarify: I think the solution to my problem would be to call the map.updateSize() method when the sidebar has finished collapsing. The problem is that I don't know how to catch the moment when the sidebar has finished collapsing/expanding!
A temporary solution I found was to start a timeout when the button triggering the sidebar collapse and then call the map.updateSize() method:
$('.sidebar-toggle').click(function(){
setTimeout(function(){ map.updateSize(); }, 500);
});
It works...but it's kind of meh :/
If you're trying to redraw the map after the sidebar collapses, change your event handler to the following:
$('.sidebar').on('hidden.bs.collapse', function() {
map.updateSize();
});
According to the list of event handlers here, the hidden.bs.collapse event is fired when a collapse element has been hidden from the user.
I have the same problem, using React and a class component my (awful) solution is this:
shouldComponentUpdate = async () =>
await new Promise(res =>
setTimeout(() => {
this.map.updateSize()
res(false)
}, 500)
)
It's awful because the resize causes the map to jump. If there is a way of achieving the map resize without a jump that would be pretty cool.
(500 is the animation time for my drawer to close)
Related
Introduction
I'm using Semantic-UI's sidebar functionality, which gives you a button that triggers a sidebar that pushes the content from the left (in this case).
I want to unfold that same sidebar by hovering with the mouse on the left side. I realize there are several ways to do it (as these often do. Maybe just checking the X position of the mouse would work but that's beside the point); I chose to create a transparent div on the left side and make its :hover pseudo-class to trigger the sidebar:
// create sidebar and attach to menu open
$('.ui.sidebar').sidebar('attach events', '.toc.item');
// hover transparent div to trigger the sidebar too:
$('.sidebar-trigger').hover(function() {
$('.ui.sidebar').sidebar('show')
});
// hide() and show() the sidebar accordingly to use the sidebar:
$('.ui.sidebar').sidebar('setting', {
onShow: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
Problem
Now, it all works except for one occasion: when you don't stop moving the mouse as the sidebar opens. I've looked at $(document).on('transitionend', function(event) { ... } and that mouse effectively prevents the transition to finish.
Resources
I've put a blue background on my .sidebar-trigger and made a small video/gif so as to be clearer.
I moved the mouse like a crazy creature but with natural gestures the problem occurs as well.
I'm using Semantic-UI's guide on this thing: http://semantic-ui.com/modules/sidebar.html#/settings (I've also tried onVisible and onHide with no luck)
This is a OSX Yosemite 10.10.3 running Chrome 45.0.2454.101 (64-bit)
jsfiddle with the problem at hand
PS: It seems it might be an OSX Chrome bug?
I would try using one and mouseover:
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
Then, when it has finished animating, reattach the event:
$(document).on('transitionend', function(event) {
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
});
I think what is happening is that the hover event is getting called multiple times - every time the element is hovered, then goes over a child element, and then goes back over the hover element, and things are getting mixed up at some point. So you need to only call show if it's not already shown.
Here is a working example: Fiddle
I believe when the element was hovered, it was adding a classes 'uncover' and 'visible', and another called 'animating' which wouldn't fire until the mouse stopped moving. I changed the jQuery slightly to only add classes 'uncover' and 'visible', and it still animated okay. However, the body was pushing right too far by 175px, so I had to edit the class that was causing that (noted below) from 260px to 85px. This DOES get the menu acting properly though from my understanding.
$('.sidebar-trigger').mouseenter(function() {
$('.ui.sidebar').addClass('uncover, visible');
$('body').addClass('mleft175');
});
$('body').click(function() {
$('.ui.sidebar').removeClass('uncover, visible');
$('body').removeClass('mleft175');
});
and then add overriding class
.ui.visible.left.sidebar ~ .pusher
{
-webkit-transform: translate3d(85px, 0, 0);
transform: translate3d(85px, 0, 0);
}
Right now it is set to hide the menu when the body is clicked. Alternatively you can hide it when the mouse leaves the sidebar menu:
$('.ui.sidebar').mouseleave(function(){
$(this).removeClass('uncover, visible')
});
Ok, my first answer was (of course) way too much work for what it really needed. The onVisible seems to work perfectly. Was that not working for you? Demo HERE
Simply change 'onShow' to 'onVisible' in your sidebar setting:
$('.ui.sidebar').sidebar('setting', {
onVisible: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
As shown on the Semantic UI site, the onVisible fires when the animating starts. The onShow fires when the animating finishes. So what you were doing was hiding that blue / transparent bar when the animation was finally done (the .animating class noted in my previous answer), as opposed to when it starts. If you need further explanation please let me know.
I made a jsfiddle so you can reproduce the bug:
FIDDLE
I implemented a carousel to display 3 images. There's a current image (the image being displayed) and the other two remain hidden until I click one of the lateral arrows, causing the next image to slide from the side overlaying the (now previous) current image.
I've been 2 hours trying to figure out why there are certain specific 'transitions' in which the animation doesn't seem to work. For example, when clicking the left arrow to pass from the first image to the second and from the second to the third the animation works fine, but when clicking it again, the transition from 3 to 1 doesn't perform the slide animation. When moving in the opposite direction (using the right arrow) only one transition is animated. I think the problem has to do with that if in the click event handler function, but couldn't spot what's causing it.
Any help would be greatly appreciated.
TIA
The underlying issue here is related to the z-order of the three images. Your slide animations are only showing up where the image being slid in is above the displayed image; the "broken" transitions are actually occurring, they're just obscured by the "higher" visible image.
You can fix this by explicitly setting the z-index of the new and current image. For example, on the right transition:
prevLandscape.zIndex(1);
currLandscape.zIndex(0);
If you do this, you'll also need to increase the z-index of the arrows so they're above the images.
Fiddle
jsfiddle
The issue is with the hide method you just simply hide it add the slide transition for the hide method.
change this line currLandscape.hide(); to currLandscape.hide("slide");
there seemed to be a problem with the order of the images also. please try this code out. The code is reuse of the previous image arrow code. Just try it out.
$('.arrowRight').on('click',function(e) {
var currLandscape = $(this).siblings(".currImg");
var nextLandscape = currLandscape.nextAll(".hiddenImg").first();
var currDesc= $(".currDesc");
var nextDesc= currDesc.nextAll(".hiddenDesc").first();
if (nextLandscape.length == 0) {
nextLandscape = currLandscape.siblings('.hiddenImg').first();
}
if (nextDesc.length == 0) {
nextDesc= currDesc.siblings('.hiddenDesc').first();
}
nextLandscape.show("slide", { direction: "right" }, 400, function() {
currLandscape.hide("slide");
});
currDesc.fadeOut().removeClass('currDesc').addClass('hiddenDesc');
nextDesc.fadeIn().removeClass('hiddenDesc').addClass('currDesc');
currLandscape.removeClass('currImg').addClass('hiddenImg');
nextLandscape.removeClass('hiddenImg').addClass('currImg');
});
I have a <div> containing a leaflet map. Upon certain events the height of the <div> will be altered. I'd like for the map to resize to the new dimensions of its surrounding <div> so that the old center is centered in the resized smaller or larger map. I tried using the invalidateSize() function, but it doesn't seem to work at all. How can I resize and center the map after that map-container-resize event?
$mapContainer.on('map-container-resize', function () {
map.invalidateSize(); // doesn't seem to do anything
});
Edit to give more context:
The map container is styled initially as
#map-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
transition: height 0.5s ease-in-out;
}
After a user clicks a certain button, another panel shows at the bottom of the page and the map-container's height will be reduced to something less than 100% (say 80%).
Upon click on this button, the map-container-resize event is triggered so that I can make the map resize and center on its old (i.e. before the resizing happened) center. The map itself should then also be resized to 80% of its initial height.
The APi doc for invalidateSize seemed to be what I wanted:
"Checks if the map container size changed and updates the map if so
[...]"
But having a look with the output of the getSize function before and after the call to invalidateSize, nothing is different, the map remains at its old size.
The problem is that the resizing of the #map-container div is done via a css transition. The transition hasn't started yet, let alone ended, when the call to invalidateSize happens so the leaflet map cannot recognize any change of dimensions of its surrounding div.
Triggering the map-container-resize event with a delay solved the problem. This way :
setTimeout(function(){ map.invalidateSize()}, 400);
L.Map.invalidateSize() only informs leaflet map object that its container size has been changed, and therefore is should draw less or more map tiles. It does not actually change any dimensions, e.g. of its containing <div>, and does not move the map. You should do it yourself.
I came across this question today and wanted to provide an updated answer based on 2020 Browser API. This example uses the Browser's ResizeObserver to monitor the size of the div that Leaflet is mounted too. Assuming the following HTML Snippet:
<div id="map" />
With the following JavaScript:
const mapDiv = document.getElementById("map");
const map = L.map(mapDiv).setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
const resizeObserver = new ResizeObserver(() => {
map.invalidateSize();
});
resizeObserver.observe(mapDiv);
This should monitor the map div, and call the invalidateSize() method on the Leaflet map when the map div size changes. This approach allows you to handle the resizing "closer" to the map code, rather than trying to rely on window resize events or listening for changes triggered elsewhere in the application.
Obviously the CSS for the map div itself will need to ensure that it resizes in whatever way you want it to. This code snippet will ensure the Leaflet is appropriately updated when that happens.
You can use below code after resize that
map.invalidateSize()
https://github.com/Leaflet/Leaflet/issues/690
the accepted answer is a bit hacky in that it relies on the sleep being longer than the transition.
I have found this to work well:
$("body").on($.support.transition.end, '#main-navbar .nav-collapse', function(event){
console.log("end of the animation");
});
Just call resize window event rather than timing the map to load.
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
window.dispatchEvent(new Event('resize'));
// Triggers a window resize
// Thus your map automatically triggers invalidateSize().
Ran into this problem running VueJS, Leaflet 1.2.0. The resizing didn't appear complete as others mentioned above. My solution within VueJS was to call the nextTick function:
var vm = this
var container = vm.$refs.container
vm.mapStyle.width = `${vm.getElementContentWidth(container)}px`
vm.mapStyle.height = `${vm.getElementContentHeight(container)}px`
vm.$nextTick(() => {
if (vm.map) vm.map.invalidateSize()
if (vm.layerBase) vm.layerBase.redraw()
})
I believe pure javascript would be
I'm using Google Chart Tools to display a simple line graph for unknown reason the labels overlap no matter how I set the "legend" parameters. In the screenshot below you can see the result for legend: {position: 'in', alignment:'center'}. How to work around this?
"..when people generally complain about labels overlapping, that's due to attempting to draw in an invisible container. We currently do not support this, so you need to make sure that your container is not display:none when you draw the chart." - Sergey
Link: https://groups.google.com/forum/#!topic/google-visualization-api/c-KpZk--8p0
I had a chart loading near the bottom of a pretty complex page and this issue started. I decided to execute the creation of the chart after the page had loaded to give the parent div time to render.
$(document).ready(function(){
makeChart(data);
})
And the css for the parent div had a fixed height & width.
Hope this helps!
I was rendering graphs on a popup, labels were overlapping. I tried executing the rendering in $(document).ready() as well as $(window).load() - nothing worked out.
On a click event, the popup will appear.
$(document).ready(function()
{
$('.view_graphs').click(function(){
setTimeout(function(){
renderGraph();
},500)
})
})
function renderGraph() {
google.charts.setOnLoadCallback(column_chart);
function column_chart() {
var data = new google.visualization.arrayToDataTable(<?php echo $visitlang_json_data ?>);
var chart = new google.visualization.ColumnChart(document.getElementById('visit_lang_chart'));
var options = {'title':'Report based on language',
'width':600,
'height':500
};
chart.draw(data,options);
}
}
Rendering it on a setTimeout function on click event worked for me
What would be the best way to have a single image which when you hovered your mouse over it, it would cycle fade in/out into a series of other images (like 3 or 4) before returning back to the original? Also, it would stop fading/changing images and go back to the original image if you moved your mouse off of the image.
Any help is appreciated.
Thanks!
I would see about having the mouseover event (or hover if you are using an external library) of the object that you are wishing to switch tied to a window.setTimeout function. Make sure to store the timeout id so that you can cancel it if the user's mouse exits the window. The window.setTimeout would then scroll through the pictures.
Maybe something like this:
$(function(){
$("#test").hover(function(e) {
$.data("timeout", window.setTimeout(function() {
//Transition here
}));
}), function(e) {
window.clearTimeout($.data("timeout"));
//Put image back to normal
});
});
Hope this helps,
JMax