Mapbox-gl-js - Mouse events are off after applying CSS transform to map - mapbox-gl-js

I am using mapbox-gl-js to display a map inside of a responsive container.
Regardless of the size of the responsive container, I want the map to maintain the same resolution such that resizing the window does not affect the bounds or zoom level of the map, it simply scales the map as if it were a static image.
I achieve this by dynamically calculating the scale factor and applying it to the container via CSS transform:
#map {
width: 100%;
height: 100%;
}
#map-scaler {
width: 1280px;
height: 1280px;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
transform-origin: 0 0;
}
#map-container {
background-color: #fff;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
width: 100%;
height: 100%;
}
<div id="map-container">
<div id="map-scaler">
<div id="map"></div>
</div>
</div>
// Get size of map container for display
var mapContainer = $("#map-container");
var mcWidth = mapContainer.width();
var mcHeight = mapContainer.height();
// Get size of scaler container
var scaleContainer = $("#map-scaler");
var sWidth = scaleContainer.width();
var sHeight = scaleContainer.height();
var scaleWidth = mcWidth / sWidth;
var scaleHeight = mcHeight / sHeight;
$("#map-scaler").css("transform", "scale(" + scaleWidth + ", " + scaleHeight + ")");
This achieves the desired results visually. For example, I can have a 1280x1280 map displayed inside of a 500x500 container.
The problem is that all the mouse events are now "off". For example, if you attempt a map click or a scroll zoom, the map applies your mouse event to the wrong area of the map.
How can I compensate for my CSS transform so that mouse events accurately reflect where the user clicked?

Can you try the resize method? As mentionned in the doc, the method looks at your div width&height and resize your map. Event should be updated too.
https://www.mapbox.com/mapbox-gl-js/api/#Map#resize

Related

LeafletJS: How to make layer not movable?

I'd like to make a simple canvas layer (not tiled canvases, but one big canvas), but I can not find how can I put layer outside mapPane to make it non-draggable in a documented way.
Should I use 'non-documented' methods or should I use 'reverse-tranform' hack ?
If I understand correctly, you would like to overlay your own canvas onto a Leaflet map, but so that it does not pan (is being dragged) with the rest of the map like the Tile Layers or Markers.
Therefore it would be like a Control (like the Zoom, Layers switching and attribution Controls) that remains at the same position relative to the map container, except that it would cover the entire map view port.
As you seem to have figured out, as soon as you insert your element into the map pane, it will move with the rest of the map elements as user drags / pans around.
Therefore you could simply append it into the map container, as a sibling of the map pane:
// http://leafletjs.com/reference-1.3.0.html#map-getcontainer
map.getContainer().appendChild(myCanvasElement);
Then you need to adjust the CSS of your canvas element:
position it absolutely
above the other siblings (the map pane has a z-index of 400, but you probably want to remain below other controls, which have a z-index of 1000)
let mouse events go through (so that user can still use map objects like clicking on Markers, etc.)
#myCanvasElement {
position: absolute;
/* Let mouse events go through to reach the map underneath */
pointer-events: none;
/* Make sure to be above the map pane (.leaflet-pane) */
z-index: 450;
}
A working code snippet example:
var map = L.map('map').setView([48.86, 2.35], 11);
var myCanvasElement = document.getElementById('myCanvasElement');
// Adjust the canvas size, assuming we want to cover the entire map.
var mapSize = map.getSize(); // http://leafletjs.com/reference-1.3.0.html#map-getsize
myCanvasElement.width = mapSize.x;
myCanvasElement.height = mapSize.y;
// Move the canvas inside the map container.
var mapContainer = map.getContainer(); // http://leafletjs.com/reference-1.3.0.html#map-getcontainer
mapContainer.appendChild(myCanvasElement);
// Draw on the canvas...
var context = myCanvasElement.getContext('2d');
context.strokeStyle = 'rgb(0, 0, 200)';
var w = 200;
var h = 100;
var x = (mapSize.x - w) / 2;
var y = (mapSize.y - h) / 2;
context.strokeRect(x, y, w, h);
L.marker([48.86, 2.35]).bindPopup('Paris').addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
#myCanvasElement {
position: absolute;
/* Let mouse events go through to reach the map underneath */
pointer-events: none;
/* Make sure to be above the map pane (.leaflet-pane) */
z-index: 450;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>
<div id="map" style="height: 180px"></div>
<canvas id="myCanvasElement"></canvas>

ag-grid auto height for entire grid

I can't find a good way to size the grid to fit all rows perfectly.
documentation only points to sizing by % or px.
Since I want it to size based on rows, I came up with the following function. Seem like im re-inventing the wheel, so maybe there is a better way?
getHeight(type:EntityType) {
var c = this.Apis[type] && this.Apis[type].api && this.Apis[type].api.rowModel // get api for current grid
? this.Apis[type].api.rowModel.rowsToDisplay.length
: -1;
return c > 0
? (40+(c*21))+'px' // not perfect but close formula for grid height
: '86%';
}
there has to be a less messy way..
I came across this solution:
https://github.com/ag-grid/ag-grid/issues/801
There are 2 answers to this problem.
1) If you are using anything below v10.1.0, then you can use the following CSS to achieve the problem:
.ag-scrolls {
height: auto !important;
}
.ag-body {
position: relative !important;
top: auto !important;
height: auto !important;
}
.ag-header {
position: relative !important;
}
.ag-floating-top {
position: relative !important;
top: auto !important;
}
.ag-floating-bottom {
position: relative !important;
top: auto !important;
}
.ag-bl-normal-east,
.ag-bl-normal,
.ag-bl-normal-center,
.ag-bl-normal-center-row,
.ag-bl-full-height,
.ag-bl-full-height-center,
.ag-pinned-left-cols-viewport,
.ag-pinned-right-cols-viewport,
.ag-body-viewport-wrapper,
.ag-body-viewport {
height: auto !important;
}
2) Anything above v10.1.0, there is now a property called 'domLayout'.
https://www.ag-grid.com/javascript-grid-width-and-height/#autoHeight
If the following is the markup
<ag-grid-angular
#agGrid
id="myGrid"
class="ag-theme-balham"
[columnDefs]="columnDefs"
[rowData]="rowData"
[domLayout]="domLayout"
[animateRows]="true"
(gridReady)="onGridReady($event)"
></ag-grid-angular>
Note that [domLayout]="domLayout"
set it as this.domLayout = "autoHeight"; and you're done..
Ref:
https://www.ag-grid.com/javascript-grid-width-and-height/
https://next.plnkr.co/edit/Jb1TD7gbA4w7yclU
Hope it helps.
You can now automate grid height by setting the domLayout='autoHeight' property, or by calling api.setDomLayout('autoHeight')
reference Grid Auto Height
Add the following code on onGridReady function for auto width and auto height
onGridReady = params => {
// Following line to make the currently visible columns fit the screen
params.api.sizeColumnsToFit();
// Following line dymanic set height to row on content
params.api.resetRowHeights();
};
reference
Auto Height
Auto width
determineHeight() {
setTimeout(() => {
console.log(this.gridApi.getDisplayedRowCount());
if (this.gridApi.getDisplayedRowCount() < 20) {
this.gridApi.setDomLayout("autoHeight");
}
else {
this.gridApi.setDomLayout("normal");
document.getElementById('myGrid').style.height = "600px";
}
}, 500);
}
You can use ag-grid feature AutoHeight = true in the column Configuration. or if you want to calculate the height dynamically you should use getRowHeight() callback and create DOM elements like div and span, and add your text in it, then the div will give the OffsetHeight for it then you wont see any cropping of data/rows.
Hope this helps.

Html2Canvas is not able to render multiple DIVs properly

Html2Canvas is not able to render multiple DIVs properly when
The number of DIVs increases significantly, OR
The size of each DIV increases significantly (then it fails with fewer number of DIVs)
I am using Html2Canvas Plugin to convert div (containing image) to canvas. I have implemented a recursive function to convert multiple images at a time (see code below).
JavaScript
//counter
var div_number = 0;
function image2canvas() {
var img2canvasObj = {
logging:true,
onrendered: function (canvas) {
canvas.id = "cvs" + div_number;
$("#canvasid").append(canvas);
//incrementing the counter
div_number = div_number + 1;
if (div_number <= 18) {
html2canvas($("#tempDivforpublishplan" + div_number), img2canvasObj);
}
if (div_number === 19) {
<Some Ajax Call>
}
}
};
html2canvas($("#Content_to_convert" + div_number), img2canvasObj);
}
The HTML for multiple images (which I am converting to canvas) is as follows. Note that I am using images as background of div elements. Each of these elements have a base64 string appended to the image URL which is used to convert the image to canvas.
HTML:
<div id="tempDivforpublishplan0" style='background: url("data:image/png;base64,iVBORw0KGgo…..") 0% 0% / 634px 2266.26px; width: 634px; height: 2266.26px; position: relative; cursor: auto;'> </div>
<div id="tempDivforpublishplan1" style='background: url("data:image/png;base64,iVBORw0KGgo…..") 0% 0% / 634px 2266.26px; width: 634px; height: 2266.26px; position: relative; cursor: auto;'> </div>
.
.
.
<div id="tempDivforpublishplan19" style='background: url("data:image/png;base64,iVBORw0KGgo…..") 0% 0% / 634px 2266.26px; width: 634px; height: 2266.26px; position: relative; cursor: auto;'> </div>
When image count is more, or the size and resolution of images is high, only few images are converted properly. Remaining images are either converted partially or not converted at all. Is there any workaround which can be used to fix this issue?
i have achieved this using callback and delay....
before calling your recursive function use delay and use callback as html2canvas is asynchronous call
I think you are having this trouble because you reach CanvasRenderer height limit during rendering. Assuming all your divs your page height is more than 40000px. Html2Canvas renders your page completely and then crops your div from it according to your div's position. You can see how big your canvas object is if you take a look throw your console having 'logging: true' in html2canvas options. Here is some more info about this subject: Maximum size of a <canvas> element

Mobile Page Slider content with dynamic height

I am trying to implement the app that have 4 pages in 4 table. It supposed to be support swipe gesture, and each page contains dynamic data and make the pages have different height.
I tried to use responsive-slider-div-span.source example and integrate the 4 table into the slides.
I would like to set the width and height for the slides dynamically while the document ready, However, The slide will not show if the width and height is not pre set in the the slide div.
May I know any suggestion?
Typically, the size of jssor slider is defined in html code, and you can alter the size grammatically.
Given a slider at size 600x300, you can use javascript to change size before initialization of jssor slider.
<script>
jQuery(document).ready(function ($) {
var options = {};
//retrieve height of the slider, assuming the height should be 200px;
$("#slider1_container").height("200px");
$("#slider1_slides").height("200px");
var jssor_slider1 = new $JssorSlider$("slider1_container", options);
//According to your comment, please add following code
//Handle $EVT_PARK event BEGIN
function OnSliderPark(slideIndex, fromIndex) {
if (slideIndex == 0 || slideIndex == jssor_slider1.$SlidesCount() - 1) {
//do something scroll page to top
}
}
jssor_slider1.$On($JssorSlider$.$EVT_PARK, OnSliderPark);
//Handle $EVT_PARK event END
});
</script>
<div id="slider1_container" style="... width: 600px; height: 300px; ...">
<div u="slides" id="slider1_slides" style="... width: 600px; height: 300px; ...">
...
</div>
</div>

Can you use jQuery .css() with .live()?

I have a div with class="centerMessage" . This div is inserted into the DOM at a point after the page is loaded. I would like to change the CSS on this div to center it. I tried the CSS function below, but it did not work. Does anybody know a way to do this?
function centerPopup() {
var winWidth = $(window).width();
var winHeight = $(window).height();
var positionLeft = (winWidth/2) - (($('.centerMessage').width())/2);
var positionTop = (winHeight/2) - (($('.centerMessage').height())/2);
$('.centerMessage').live( function(){
$(this).css("position","absolute");
$(this).css("top",positionTop + "px");
$(this).css("left",positionLeft + "px");
});
}
If my assumption of what you're trying to achieve is correct, you don't need any Javascript to do this. It can be achieved by some simple CSS.
.centerMessage {
position: absolute;
top: 50%;
left: 50%;
margin-top: -150px; /* half of the height */
margin-left: -300px; /* half of the width */
width: 600px;
height: 300px;
background: #ccc;
}
Demo: http://jsbin.com/awuja4
.live() does not accept JUST a function. If you want something to happen with live, it needs an event as well, like click. If you want something to happen always for every .centerMessage, you will need the plugin .livequery()
I believe that the following works in FF & Webkit.
div.centerMessage{
position: absolute;
width: /* width */;
height: /* height */;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
margin: auto;
}
I know this is already answered, but I thought I'd provide a working jsFiddle demo using JavaScript like the OP originally wanted, but instead of using live(), I use setInterval():
First, we need to declare a couple variables for use later:
var $centerMessage,
intervalId;
The OP's issue was that they didn't know when the div was going to be created, so with that in mind we create a function to do just that and call it via setTimeout() to simulate this div creation:
function createDiv() {
$('<div class="centerMessage">Center Message Div</div>').appendTo("body");
}
$(function() { setTimeout("createDiv()", 5000); });
Finally, we need to create a function that will check, using setInterval() at a rate of 100ms, to see if the div has been created and upon creation, goes about modifying the div via jQuery:
function checkForDiv() {
$centerMessage = $('.centerMessage');
if ($centerMessage.length) {
clearInterval(intervalId);
var $window = $(window),
winHeight = $window.height(),
winWidth = $window.width(),
positionTop = (winHeight / 2) - ($centerMessage.height() / 2),
positionLeft = (winWidth / 2) - ($centerMessage.width() / 2);
$centerMessage.css({
"display" : "block",
"position" : "absolute",
"top" : positionTop.toString() + "px",
"left" : positionLeft.toString() + "px"
});
}
}
$(function() { intervalId = setInterval(checkForDiv, 100); });
Try Use this
$('.centerMessage').live('click', function(){
Try this
$('#foo').on('click', function() {
alert($(this).text());
});
$('#foo').trigger('click');
OR
$('#foo').live('click', function() {
alert($(this).text());
});
$('#foo').trigger('click');