Google Maps V3 tooltip popup with dynamic auto-positioning - google-maps

I'm looking for a popup that will work with Google Maps V3 that supports auto-positioning the popup in relation to the marker such that the whole popup window is always visible within the map viewport. I'm trying to hack the InfoBox library to make this work but it's proving to be a big hassle. I've also looked at QTip2 which shows some promise but also has some shortcomings such as having positioning, but it must be set manually.
EDIT: The solution needs to to not pan the map to show the popup window.

I found SmartInfoWindow in a quick search at the V3 Demo Gallery. It seems like it does just what you want. Here's the google code project.

I was able to add on to InfoBox to get it to do what I wanted with a little help from SmartInfoWindow. This is a partially customized solution so you may need to tweak the positioning. You just grab the div position of each corner of the InfoBox, convert those corners back to lat/lng and then grab the maps bounds and see if the corners are within the bounds. If they aren't then you adjust the InfoBox position accordingly depending on which corners are off the map.
InfoBox.prototype.maybePanMap = function() {
// if we go beyond map, pan map
var map = this.getMap();
var projection = this.getProjection();
var bounds = map.getBounds();
if (!bounds) return;
// The dimension of the infowindow
var iwWidth = this.div_.offsetWidth;
var iwHeight = this.div_.offsetHeight;
// The offset position of the infowindow
var iwOffsetX = this.pixelOffset_.width;
var iwOffsetY = this.pixelOffset_.height;
var anchorPixel = projection.fromLatLngToDivPixel(this.getPosition());
var bl = new google.maps.Point(anchorPixel.x + iwOffsetX,
anchorPixel.y + iwOffsetY);
var br = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth,
anchorPixel.y + iwOffsetY);
var tl = new google.maps.Point(anchorPixel.x + iwOffsetX,
anchorPixel.y + iwOffsetY - iwHeight + 100);
var tr = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth,
anchorPixel.y + iwOffsetY - iwHeight + 100);
var sw = projection.fromDivPixelToLatLng(bl);
var se = projection.fromDivPixelToLatLng(br);
var nw = projection.fromDivPixelToLatLng(tl);
var ne = projection.fromDivPixelToLatLng(tr);
if (!map.getBounds().contains(nw) && !map.getBounds().contains(sw)) {
this.div_.style.left = (anchorPixel.x + 10) + 'px';
if (!map.getBounds().contains(ne)) {
this.div_.style.top = (anchorPixel.y - 100) + 'px';
}
} else if (!map.getBounds().contains(nw) && !map.getBounds().contains(ne)) {
this.div_.style.top = (anchorPixel.y - 100) + 'px';
if (!map.getBounds().contains(se)) {
this.div_.style.left = (anchorPixel.x - iwWidth - 10) + 'px';
}
} else if (!map.getBounds().contains(ne) && !map.getBounds().contains(se)) {
this.div_.style.left = (anchorPixel.x - iwWidth - 10) + 'px';
if (!map.getBounds().contains(sw)) {
this.div_.style.top = (anchorPixel.y - iwHeight - 100) + 'px';
}
}
};

Related

Accordion toggle for selected div

I'm using bootstrap 3 for my new project, I wanted to use bootstrap accordion menu.
Everything works fine here.
My problem here is the toggle icon.
I need to rotate the × only for the selected div.
This is what I tried:
var targetDiv = $('.tog');
var i = 0;
var degrees;
var rotation;
$('.tog').click(function() {
i++;
degrees = i * -45;
rotation = 'rotate(' + degrees + 'deg)';
targetDiv.css('-webkit-transform', rotation);
});
Here is the link to fiddle: https://jsfiddle.net/5pLwxus7/5/
As you can see when the .tog is clicked all the remaining divs also respond.
I'll appreciate any help.
You can use currentTarget to find out the div which is currently clicked. See the updated fiddle here :
https://jsfiddle.net/n6xd8953/
//var targetDiv = $('.tog');
var i = 0;
var degrees;
var rotation;
$('.tog').click(function(event) {
var currentDiv = event.currentTarget;
i++;
degrees = i * -45;
rotation = 'rotate(' + degrees + 'deg)';
$(currentDiv).css('-webkit-transform', rotation);
});

Is canvas required?

I've been working with HTML5 drag and drop, and canvas. I'm trying to put the two together: I drag an element to a drop-area; in the drop-area I want to be able to move the dropped items around to position them as needed.
I know how to drop elements into a div, for ex, but:
does the drop-area have to be a canvas for (re)positioning?
is there a specific term for the moving/repositioning of elements in the drop-area/canvas. I've done a lot of searching and can't find a term for this specifically. simply 'dragging'??
sample drop-area.
No the canvas element is not required anyhow.
You can achieve the exact same result as the linked example you gave without using any canvas.
Here is an example code, among many others possible, certainly far from being perfect, but which does the same as your example, using only <div> elements, css and javascript, but it could also be made with svg.
// we will increment it to get the dragged element atop of everything
var zIndex = 0;
// our constructor
var newElement = function() {
var that = {};
// first get its dimension and position randomly
that.rad = Math.random() * 20 + 10;
// x and y are the center of our element
that.x = Math.random() * (500 - that.rad * 2) + that.rad;
that.y = Math.random() * (300 - that.rad * 2) + that.rad;
// define the element that will be appended to the doc
that.el = document.createElement('div');
// a shortcut to the style property of the element
// since we'll play with this to update our object's position
var s = that.el.style;
// don't forget we're talking in css
s.width = that.rad * 2 + 'px';
s.height = that.rad * 2 + 'px';
s.left = that.x - that.rad + 'px';
s.top = that.y - that.rad + 'px';
s.backgroundColor = get_random_color();
// needed to make be sure we're not in a corner of the circle shaped elements
that.isCircle = Math.random() > .5;
if (that.isCircle) {
that.el.className = 'circle';
}
// happens on mousedown
that.startDrag = function(x, y) {
that.lastX = x;
that.lastY = y;
s.zIndex = ++zIndex;
}
// happens on mousemove if we're the one being dragged
that.move = function(x, y) {
that.x += x - that.lastX;
that.y += y - that.lastY;
that.lastX = x;
that.lastY = y;
s.left = that.x - that.rad + 'px';
s.top = that.y - that.rad + 'px';
};
container.appendChild(that.el);
return that;
};
var elements = [];
for (var i = 0; i < (~~(Math.random() * 50)) + 15; i++) {
elements.push(newElement());
}
var dragged;
var mousedown = function(e) {
var rect = container.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
dragged = null;
// sort our elements, higher zIndex firsts
elements.sort(function(a, b) {
return (+b.el.style.zIndex) - (+a.el.style.zIndex);
});
elements.forEach(function(el) {
// we already found the One, no need to go further
// (no "break;" in forEach method...)
if (dragged) return;
// is our mouse over the rectangular bounds of this element
if (x >= el.x - el.rad && x <= el.x + el.rad &&
y >= el.y - el.rad && y <= el.y + el.rad) {
if (el.isCircle) {
// a little bit of Pythagorian
var a = el.x - x;
var b = el.y - y;
var dist = Math.sqrt(a * a + b * b);
// too far from the center, we were in the corner
if (dist > el.rad) {
return;
}
}
// we got through here,
// tell the whole app we've got the One
dragged = el;
el.startDrag(x, y);
}
});
};
var mousemove = function(e) {
// nobody is being dragged, so don't do anything
if (!dragged) return;
// otherwise, tell the browser we handle the event
e.preventDefault();
e.stopPropagation();
// get the coordinates of our container
var rect = container.getBoundingClientRect();
// get the relative coordinates of our event
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
// move the dragged element accordingly
dragged.move(x, y);
};
var mouseup = function() {
// we dropped it..
dragged = null;
};
container.onmousedown = mousedown;
container.onmousemove = mousemove;
container.onmouseup = mouseup;
function get_random_color() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
};
body {
text-align: center;
}
#container {
width: 500px;
height: 300px;
background-color: black;
margin: 0 auto;
position: relative;
overflow: hidden;
}
#container>div {
position: absolute;
opacity: .7;
}
.circle {
border-radius: 50%;
}
<div id="container"></div>
As you can see, I myself used the word dragged to refer to the object that we do move while we move the mouse with the button down. Yes, dragging is how this action is called...
But, this has very little to do with the HTML drag and drop API where what is important is not the positioning of your elements, but their content.
You can drag and drop elements into others, but it was mainly developed to drag data (external files or text content) to the document.
For this particular example, that would make things a lot harder and while the name "dragging" still applies to the action of the end-user, this shall not be confused with the API and every events related.
A canvas element is required by your linked canvas demo, but you can alternatively use the drag/drop API built into html+JS. And you can "manually" move DOM objects using mouse events as described in Kaiido's answer.
The canvas is required for your linked demo to work.
That exact demo was coded using a canvas element's drawing capabilities and that exact demo will not work if you try to substitute a DIV.
Think of html5 canvas as a re-writable bitmap. You can't "move" or "drag" anything on the canvas. Instead, you completely erase the canvas surface and redraw your circles in their new positions.
In the case of dragging, you listen for mousemove events on the canvas and reposition the circle under the mouse by the distance the mouse has moved since the last mousemove event.

How to convert mouse position into css 3d transformed element position

As shown in JSFiddle, when you move the mouse over the 3d transformed div, I tried to convert the mouse position into the css 3d transformed element space.
Which means, the red spot should always be display at the exact position of the mouse.
However, the DIV html element doesn't have method getScreenCTM or getTransformToElement or matrixTransform which can be used to transform the point. Does anybody know how to achieve that?
http://jsfiddle.net/9cv2y0dp/
var tel = $('#transformed')[0];
var box = $('#box')[0];
tel.style.transform
= 'translate3d(100px,100px,0px) rotateY(45deg)';
var rect = tel.getBoundingClientRect();
$('#transformed').bind('mousedown', function(e){
var evt = e || event;
var x = evt.clientX;
var y = evt.clientY;
$('#cursor')[0].style.left = (x - rect.left) + 'px';
$('#cursor')[0].style.top = (y - rect.top) + 'px';
box.innerHTML = (x - rect.left) + 'px<br/>' + (y - rect.top) + 'px';
});

Html5 scroll effect on images

I need to reproduce the effect created on this site about Team : http://www.case-3d.com/#about
I try to look on the web but I can not find a tutorial or site that talks about this html5 effect .... I was wondering if someone could help me ?
thanking you in advance
If you inspect the element you can find out they use a canvas. Looking further you can see the script that uses the canvas (by searching in the inspector for the ID or some similar techinque) is called "1". I pulled some of the basic structure so you can follow it:
//This part sets up the canvas and gets the pictures
function setupCanvas() {
// Get canvas and context references
teamCanvas = document.getElementById("stage1");
teamContext = teamCanvas.getContext("2d");
// Get images references
img1 = document.getElementById("01");
img2 = document.getElementById("02");
...
// This part sets the initial position of the shapes
// You can see that it is based of the window size and in reference to each other
// Shape #1
x1_1 = teamCanvas.width / 2;
x1_2 = teamCanvas.width;
x1_3 = teamCanvas.width;
x1_4 = 0;
x1_5 = 0;
y1_1 = ssp1_1 = 929 + diff;
y1_2 = ssp1_2 = 2000 + diff;
y1_3 = ssp1_3 = 4000;
y1_4 = ssp1_4 = 4000;
y1_5 = ssp1_5 = 2000 + diff;
// Shape #2
x2_1 = 0;
x2_2 = teamCanvas.width / 2;
x2_3 = teamCanvas.width;
x2_4 = teamCanvas.width;
x2_5 = 0;
y2_1 = ssp2_1 = 3000;
y2_2 = ssp2_2 = 4000;
y2_3 = ssp2_3 = 3000;
y2_4 = ssp2_4 = 6000;
y2_5 = ssp2_5 = 6000;
...
// Some other stuff goes here, I didn't copy all of it
}
// Then it goes into this function to handle the scroll and redraw it on the canvas
function redrawAbout(scrollPosition) {
// Refresh canvas
teamContext.clearRect(0, 0, teamCanvas.width, teamCanvas.height);
var scrollAmt = scrollPosition / maskModifier;
// Redraw
// Mask #1
if (scrollPosition > -4000) {
teamContext.save();
teamContext.beginPath();
teamContext.moveTo(x1_1, ssp1_1 + scrollAmt);
teamContext.lineTo(x1_2, ssp1_2 + scrollAmt);
teamContext.lineTo(x1_3, ssp1_3 + scrollAmt);
teamContext.lineTo(x1_4, ssp1_4 + scrollAmt);
teamContext.lineTo(x1_5, ssp1_5 + scrollAmt);
teamContext.lineTo(x1_1, ssp1_1 + scrollAmt);
teamContext.clip();
teamContext.drawImage(img1, 0, -200);
teamContext.restore();
}
// Mask #2
if (scrollPosition <= -2100 && scrollPosition > -5900) {
teamContext.save();
teamContext.beginPath();
teamContext.moveTo(x2_1, ssp2_1 + scrollAmt);
teamContext.lineTo(x2_2, ssp2_2 + scrollAmt);
teamContext.lineTo(x2_3, ssp2_3 + scrollAmt);
teamContext.lineTo(x2_4, ssp2_4 + scrollAmt);
teamContext.lineTo(x2_5, ssp2_5 + scrollAmt);
teamContext.lineTo(x2_1, ssp2_1 + scrollAmt);
teamContext.clip();
teamContext.drawImage(img2, 0, -200);
teamContext.restore();
}
In essence, they create some geometric shapes based on x and y co-ordinates, cut the images to fit within their respective geometric area based on those variables, calculate how much is being scrolled (through another plug in I believe), and redraw everything based on how far a user has scrolled.
Inspect element is an incredibly useful tool, learn to use it
When asking questions on StackOverflow, stay away from generics like this. Try to solve it yourself and post what you've tried so far and what is giving you trouble. Give detail and be articulate. If you do that you won't be down voted and you'll get relevant, all around good answers

Multiple google markers on the same place

i'm trying to use google maps with markers. i do not have any problems with the placement of markers in the map, but how can i get the markers to split like google earth when i have to markers in the same place? like this : Example
Thanks !
I didn't understand what you're trying to accomplish, but ...
Did you already check a marker clustering algorithm like this one or the google semi-official ?
//Here is my attempt... a Archimedes spiraling out of the markers:
// calc a spiraling out position based on marker count at that location
// this function is very tweeky
function spiral_coords(lat_long, i) {
i = (i == 1)? 0: i+1;
var r = i * 0.002;
// .8 is a fudge number to adjust to real appearance on the map
return [lat_long[0] + (r * .8 * Math.sin(.5 * (i + 2))), lat_long[1] + (r * Math.cos(.5 * (i + 2)))];
}
// this is from a fusion table query... but your source could be anything
// I take the coords and check against a hash count of them and calc out the spiral position
function data_handler(d) {
var map = $("#map")[0];
map.markers = [];
var rows = d.rows;
var fields = d.columns;
var index = {};
for (var i in fields) {
index[fields[i]] = i;
}
var location_count = {};
for (var i in rows) {
var row = rows[i];
var location = row[index["Location"]];
var lat_long = location.split(" ");
lat_long[0] = parseFloat(lat_long[0]);
lat_long[1] = parseFloat(lat_long[1]);
// here are the active ingredients
if(!(location in location_count)) {
location_count[location] = 0;
}
location_count[location]++;
lat_long = spiral_coords(lat_long, location_count[location]);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(lat_long[0], lat_long[1]),
map: map.map
});
}
}