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

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';
});

Related

Alignment issue between BPMN.IO and Heatmap.js

I'm trying to overlay a bpmn flow using bpmn.io with a heatmap using heatmap.js. If the canvas is set to the first and only element in the dom it actually works, but once you add anything else like in my sample a header, the coordinate system between both gets off.
I've prepared a fiddle that shows exactly what I mean.
https://jsfiddle.net/rafaturtle/qt8Ly4ez/16/
const rect = canvas.getGraphics(element).getBoundingClientRect();
const x = (rect.x + rect.width / 2);
const y = (rect.y + rect.height / 2);
data.push({
x: x.toFixed(0),
y: y.toFixed(0),
value: stats[element.id]
});
I believe its on the calculation of x,y of each element, off setting it by a factor but after trying every combination I could think of I didn't manage.
thanks
I am not sure if I misunderstood what you aim for, but laying the heatmap over the elements can be easily achieved by using the dimensions of the current element, element.x, element.y, element.width, element.height.
The following code places the heatmap at the exact position of the elements:
var registry = bpmnJS.get('elementRegistry');
for (var i in registry.getAll()) {
var element = registry.getAll()[i];
if (stats[element.id] != null) {
const centerx = element.x + (element.width / 2 )
const centery = element.y + (element.height / 2 )
data.push({
x: centerx,
y: centery,
value: stats[element.id]
});
}
}
Working example: https://jsfiddle.net/cr8Lg53a/
const x = (rect.x + rect.width / 2) - canvas.getContainer().getBoundingClientRect().x;
const y = (rect.y + rect.height / 2) - canvas.getContainer().getBoundingClientRect().y;
try this

interactjs - save responsive position

I'm creating a form builder and I'm using interact.js to move elements and change their size. The problem is that when I drag them the design is not responsive anymore. This is the code of the function that is fired on dragmove event:
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
console.log($(target).parent().attr('id'))
// translate the element
target.style.webkitTransform = target.style.transform
= 'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
I tried to use percentages and em but it didn't work. I'm really stuck and I will be grateful for any help you can provide.

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.

Google Maps V3 tooltip popup with dynamic auto-positioning

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';
}
}
};

HTML5 Canvas Clip or Crop Everything but an S shape

How would I clip everything in the following drawing except for the S stroke? In other words get rid of all transparent space and only keep black S shape... thanks in advance!
=== PNG RENDERED IMAGE OF CANVAS ===
image located at --> http://buildasearch.com/ant/s.png
=== ACTUAL COORDINATES OF CANVAS DRAWING ===
var x = '68,67,66,65,64,63,62,61,60,59,57,56,55,54,53,52,51,51,51,51,51,51,51,52,55,56,52,58,60,59,61,62,64,65,66,68,70,71,72,74,75,76,77,78,78,79,79,79,79,79,79,79,79,79,79,79,79,78,76,74,71,67,59,56,54,52,49,47,46,45,43,42,41,40,39';
var y = '11,11,11,11,11,12,12,12,12,12,13,14,14,15,17,18,20,21,23,24,27,30,32,32,34,34,33,34,34,34,34,35,35,35,35,35,35,36,36,37,38,38,39,40,41,42,43,44,45,47,48,49,50,51,52,53,54,55,56,57,58,59,59,59,60,60,60,60,60,60,60,60,60,60,60';
It would be easier if your x and y were of the form [6, 3, 19] instead of '6,3,19'. For the purposes of this answer, I will assume that you have it done this way as it makes the code a bit easier.
It's quite possible to calculate afterwards, but it would get messy with getting the bit that you want and then resizing the canvas. It will end up easier and faster to calculate it before-hand if you can. Something like this should work:
// Data specification
var x = [68,67,66,65,64,63,62,61,60,59,57,56,55,54,53,52,51,51,51,51,51,51,51,52,55,56,52,58,60,59,61,62,64,65,66,68,70,71,72,74,75,76,77,78,78,79,79,79,79,79,79,79,79,79,79,79,79,78,76,74,71,67,59,56,54,52,49,47,46,45,43,42,41,40,39],
y = [11,11,11,11,11,12,12,12,12,12,13,14,14,15,17,18,20,21,23,24,27,30,32,32,34,34,33,34,34,34,34,35,35,35,35,35,35,36,36,37,38,38,39,40,41,42,43,44,45,47,48,49,50,51,52,53,54,55,56,57,58,59,59,59,60,60,60,60,60,60,60,60,60,60,60];
// Work out the extreme points in both dimensions
var xs = x.slice().sort(),
ys = y.slice().sort(),
minX = xs[0],
maxX = xs[xs.length-1],
minY = ys[0],
maxY = ys[ys.length-1];
// Resize the canvas
canvas.width = maxX - minX + 1;
canvas.height = maxY - minY + 1;
// Shift the points to fit on the shrunk canvas
x.forEach(function(v, i) {
x[i] = v - minX;
});
y.forEach(function(v, i) {
y[i] = v - minY;
});