I am trying to add area elements to an image map dynamically. The image is set to display transparently superimposed over a canvas. My goal is to write text on the canvas, use the same coordinates to create the area element on the map, and draw a rectangle on the canvas surrounding the text when the user hovers over the text. (Ultimately I want it to trigger a tooltip, too.) I have done this already with the same map and canvas setup using area elements hardcoded in HTML.
My problem is that I can create the area, appendChild it to the map element and add attributes. However, mousing over the text never triggers the function call to draw the rectangle.
The function used to add the areas to the map (shown as cMap) is "addArea", and the function to draw the rectangle on the canvas (context is ctx) is "labelHover". I have tried every different syntax I have seen demonstrated for adding the .onmouseover attribute to the area, but the alert in the labelHover function never triggers.
function addArea(pX, lY, idX, tipText) {
var labelArea = document.createElement('area');
cMap.appendChild(labelArea);
labelArea.className = "labelArea";
var tlTipID = "tlTip" + idX;
labelArea.id = tlTipID;
labelArea.shape = "rect";
areaCoords = pX + "," + (lY + 42) + "," + (pX + 100) + "," + (lY + 54);
labelArea.coords = areaCoords;
// alert(labelArea.coords);
labelArea.onmouseover = function(){labelHover(pX, lY+42)};
labelArea.onmouseleave = function(){labelLeave(pX, lY+42)};
}
and
function labelHover(ulx,uly) {
ctx.lineWidth = "1";
ctx.strokeStyle = "#ff0000";
ctx.strokeRect(ulx,uly,100,12);
alert(ulx);
}
Thanks for any help.
try this:
labelArea.setAttribute('onmouseover', "labelHover('" + pX + "," + (lY+42) + "')");
labelArea.setAttribute('onmouseout', "labelLeave('" + pX + "," + (lY+42) + "')");
Related
function base_axes() {
var s = Snap("#base_axes");
var dim = 0.1*window.innerWidth;
var x_line = s.line(0, dim, 0, dim);
x_line.attr("stroke", "#5e0734");
x_line.attr("stroke-width", "5px");
x_line.animate({
x2: window.innerWidth
}, 1000, mina.easein);
var y_line = s.line(dim, 0, dim, 0);
y_line.attr("stroke", "#5e0734");
y_line.attr("stroke-width", "5px");
y_line.animate({
y2: window.innerHeight
}, 1000, mina.easein);
Snap.load('https://upload.wikimedia.org/wikipedia/commons/3/35/Tux.svg', function(data) {
var logo = s.append(data);
var bbox = logo.getBBox();
var scale_factor = dim/bbox.height;
var transform_string = "s" + scale_factor + "," + scale_factor;
logo.transform(transform_string);
});
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/snap.svg.js"></script>
<body onload="base_axes()">
<svg id="base_axes" height="100%" width="100%"></svg>
I'm using this to load an svg into another svg and then transform it:
<svg id="base" height="100%" width="100%"></svg>
JS:
var s = Snap("#base");
var dim = 1;
Snap.load('img.svg', function(data) {
var logo = s.append(data);
var bbox = logo.getBBox();
console.log(bbox);
var scale_factor = dim/bbox.height;
var transform_string = "'s" + scale_factor + "," + scale_factor + "'";
logo.transform(transform_string);
});
But nothing happens. In an effort to troubleshoot, I replaced the bottom line with logo.transform('s0.1,0.1'), and that failed too. Is there something wrong with the creation of logo?
To clarify - the first svg, #id, is selected correctly, the new svg (logo) is correctly appended to it, and bbox is calculated correctly, but the final transform does nothing. The transform string looks correct, evaluating to s0.03,0.03, but the final line logo.transform(transform_string) does nothing.
I think your problem is this line, but I can't be sure without seeing it in a test example...
var logo = s.append(data);
If you look at the docs here, it says it returns the parent element. So you are saying 'logo = the svg element'.
The svg element doesn't allow transforms (in some browsers, see Roberts comment below).
So you either want to select for example a 'g' element in the svg logo by selecting it (eg s.select('#logo') or Snap('#logo'), but we haven't seen that to know if it exists), or append the svg logo to a 'g' element thats inside your svg element eg
<svg id="base" height="100%" width="100%"><g id="logo"></g></svg>
then you can apply the transform to that, rather than the svg, eg
var logo = Snap('#logo').append(data)
You also need to remove the quotes in your transform string. I.e
var transform_string = "s" + scale_factor + "," + scale_factor;
I’m a bit at loss since a couple of weeks, trying to get a mousemove event to work on a set of layered canvases.
Following advice in an earlier post, I confirmed that the event triggers properly only when the z-index of the targeted layer is on top (as demonstrated by this simple fiddle):
However, in the extended code I’m working with (d3 parcoords), despite having the same HTML structure as above, I can’t get the event to fire for canvases in the parallel coordinates chart on top.
This bl.ocks shows the extended version, and how the event won't work even when the targeted layered canvas has the greatest z-index (although the event does work well in a simple canvas below the chart).
I tried making a minimal example out of the parcoords file, but can’t manage to get a useful and working version given the number of interconnected functions.
I’m hoping someone who knows the original parcoords code might be able to clarify how exactly the chart's canvases are organized, and if something particular there might be causing the mousemove event not to work. Alternatively, maybe some experienced eyes might catch something I’m missing in the examples I posted.
Any tips much appreciated!
Extract of code from d3.parcoords.js which generates the canvases:
var pc = function(selection) {
selection = pc.selection = d3.select(selection);
__.width = selection[0][0].clientWidth;
__.height = selection[0][0].clientHeight;
// canvas data layers
["marks", "foreground", "brushed", "highlight", "clickable_colors"].forEach(function(layer, i) {
canvas[layer] = selection
.append("canvas")
.attr({
id: layer, //added an id for easier selecting for mouse event
class: layer,
style: "position:absolute;z-index: " + i
})[0][0];
ctx[layer] = canvas[layer].getContext("2d");
});
// svg tick and brush layers
pc.svg = selection
.append("svg")
.attr("width", __.width)
.attr("height", __.height)
.style("font", "14px sans-serif")
.style("position", "absolute")
.append("svg:g")
.attr("transform", "translate(" + __.margin.left + "," + __.margin.top + ")");
return pc;
};
Function used to draw the squares and set the mousemove event:
//This custom function returns polyline ID on click, based on its HEX color in the hidden canvas "clickable_colors"
//Loosely based on http://jsfiddle.net/DV9Bw/1/ and https://stackoverflow.com/questions/6735470/get-pixel-color-from-canvas-on-mouseover
function getPolylineID() {
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
function rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
// set up some squares
var my_clickable_canvas = document.getElementById('clickable_colors');
var context = my_clickable_canvas.getContext('2d');
context.fillStyle = "rgb(255,0,0)";
context.fillRect(0, 0, 50, 50);
context.fillStyle = "rgb(0,0,255)";
context.fillRect(55, 0, 50, 50);
$("#clickable_colors").mousemove(function(e) {
//$(document).mousemove(function(e) {
//debugger;
var pos = findPos(this);
var x = e.pageX - pos.x;
//console.log(x)
var y = e.pageY - pos.y;
var coord = "x=" + x + ", y=" + y;
var c = this.getContext('2d');
var p = c.getImageData(x, y, 1, 1).data;
var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
$('#status').html(coord + "<br>" + hex);
console.log("Polyline's hex:" + hex)
});
}
The svg is covering your canvases.
The canvases and svg are all on the same level, but you have not set a z-index on the svg, and as it is rendered after all the canvases.
Simply putting z-index: 0; on the svg in the page you linked fixed it for me in Chrome.
It seems to simply be a problem with z-indexes.
You should set both css position and z-index on all the canvases and the sgv on the same level.
EDIT
Sorry, I was wrong about it being just the z-index.
I could get it to work by removing the following css.
.parcoords > canvas {
pointer-events: none;
}
But that seems to be in the library you are using, so just override it.
.parcoords > canvas {
pointer-events: auto;
}
I am using JavaFX WebView included in jdk-8u45 to open a web page which shows a map using OpenLayers 2.13.1. I'm trying to zoom in on the map using a ZoomBox with a BoxHandler. The zooming works like it should, but the problem is how the the rectangle is drawn.
The wanted result is that once I click on the map and start dragging, the rectangle should start drawing as I move the mouse. This works fine in all browsers, except inside my WebView. What happens is that only after I have moved my mouse a few cm in both x- and y-direction (e.g. diagonally), the rectangle starts drawing from this position (not the one where I started dragging). I have looked at the coordinates from the different mouse events, and they all seem to be correct, which is confirmed by the fact that it zooms in on the area I actually dragged over (e.g. not the area that is drawn).
JavaScript console.log stmts output coordinates from the moment I click on the map, but nothing is drawn in the beginning of the drag.
Has anyone had similar problems with WebView? As I said, the code works like a charm in all other browsers I have tried (Safari, Firefox, Chrome, IE). I have looked around on the internet, but I haven't been able to find an answer to my problem.
Code taken from BoxHandler.js:
startBox: function (xy) {
;;;console.log(xy);
;;;console.log("startbox xy=" + xy.x + "," + xy.y);
if(this.zoomBox){
this.removeBox();
}
this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
this.dragHandler.start);
this.zoomBox.className = this.boxDivClassName;
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.viewPortDiv.appendChild(this.zoomBox);
OpenLayers.Element.addClass(
this.map.viewPortDiv, "olDrawBox"
);
},
/**
* Method: moveBox
*/
moveBox: function (xy) {
var startX = this.dragHandler.start.x;
var startY = this.dragHandler.start.y;
;;;console.log("dragHandler.start.x=" + this.dragHandler.start.x);
;;;console.log("dragHandler.start.y=" + this.dragHandler.start.y);
var deltaX = Math.abs(startX - xy.x);
var deltaY = Math.abs(startY - xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
console.log("zoombox width=" + this.zoomBox.style.width);
console.log("zoombox height=" + this.zoomBox.style.height);
console.log("zoombox left=" + this.zoomBox.style.left);
console.log("zoombox top=" + this.zoomBox.style.top);
// depending on the box model, modify width and height to take borders
// of the box into account
var box = this.getBoxCharacteristics();
;;;console.log("movebox xOffset=" + box.xOffset);
;;;console.log("movebox yOffset=" + box.yOffset);
if (box.newBoxModel) {
if (xy.x > startX) {
this.zoomBox.style.width =
Math.max(1, deltaX - box.xOffset) + "px";
}
if (xy.y > startY) {
this.zoomBox.style.height =
Math.max(1, deltaY - box.yOffset) + "px";
}
}
},
/**
* Method: endBox
*/
endBox: function(end) {
var result;
console.log(this.map.viewPortDiv.lastElementChild.style);
if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
Math.abs(this.dragHandler.start.y - end.y) > 5) {
var start = this.dragHandler.start;
var top = Math.min(start.y, end.y);
var bottom = Math.max(start.y, end.y);
var left = Math.min(start.x, end.x);
var right = Math.max(start.x, end.x);
result = new OpenLayers.Bounds(left, bottom, right, top);
} else {
result = this.dragHandler.start.clone(); // i.e. OL.Pixel
}
this.removeBox();
this.callback("done", [result]);
}
Also, I don't know if this is relevant, but when I inspect the HTML div element that holds the map (using Firebug Lite) it looks like the top border of the div is further down than it should be. The map (in correct position on the webpage) is extending beyond the top border. This is different behavior than in the other browsers I mentioned.
Any help would be appreciated.
I have an image in a canvas. The image contains squares of different colors. I'd to click on a square and get the dimensions of the square. (pixels)
For example click on a red square with yellow border and return 24 X 100
I have seen code like this . which I think is part of the solution.... but can't seem to get it work.
Any ideas?
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
// Let's draw a green square
ctx.fillStyle = "rgb(0,127,0)";
ctx.fillRect(10, 10, 20, 20);
// We will now get two pixels, the first one filling inside the above square and the other outside the square
var Pixel = ctx.getImageData(29, 10, 2, 1);
// Let's print out the colour values of the first pixel. Since we have set the colour of the
// square as rgb(0,127,0), that is what the alert should print out. Since we have not set
// any alpha value, Pixel.data[3] should be the defualt 255.
alert("Pixel 1: " + Pixel.data[0] + ", " + Pixel.data[1] + ", " + Pixel.data[2] + ", " + Pixel.data[3]);
// Print out the second pixel, which is outside the square.
// since we have drawn nothing there yet, it will show the default values 0,0,0,0 (Transparent Black)
alert("Pixel 2: " + Pixel.data[4] + ", " + Pixel.data[5] + ", " + Pixel.data[6] + ", " + Pixel.data[7]);
// Let's get the width and height data from Pixel
alert("Pixels Width: " + Pixel.width);
alert("Pixels Height: " + Pixel.height);
}
}
You could use .getImageData, but there's a much easier way...
Save the information about each square you're drawing in an object:
var green={x:10,y:10,width:20,height:20,color:"rgb(0,127,0)"};
Put all objects in an array:
var squares=[];
squares.push(green);
Then when the user clicks, enumerate all the saved squares and see if the mouse is over one:
for(var i=0;i<squares.length;i++){
var s=squares[i];
if(mouseX>=s.x && mouseX<=s.x+s.width && mouseY>=s.y && mouseY<=s.y+s.height){
alert("You clicked on a square with size: "+s.width+"x"+s.height);
}
}
[ Addition based on questioner's comment (that comment was deleted by questioner) ]
Questioner's deleted comment:
What if I don't have info on the squares. The squares are from a photo?
Assuming the canvas contains colored rectangles, here's how to calculate the width x height of the colored rectangle containing the specified x,y coordinate:
function calcColorBounds(x,y){
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
var n=i=(y*canvas.width+x)*4;
var r=data[i];
var g=data[i+1];
var b=data[i+2];
var minX=x;
while(minX>=0 && data[i]==r && data[i+1]==g && data[i+2]==b){
minX--;
i=(y*canvas.width+minX)*4;
}
i=n;
var maxX=x;
while(maxX<=canvas.width && data[i]==r && data[i+1]==g && data[i+2]==b){
maxX++;
i=(y*canvas.width+maxX)*4;
}
i=n;
var minY=y;
while(minY>=0 && data[i]==r && data[i+1]==g && data[i+2]==b){
minY--;
i=(minY*canvas.width+x)*4;
}
i=n;
var maxY=y;
while(maxY<=canvas.height && data[i]==r && data[i+1]==g && data[i+2]==b){
maxY++;
i=(maxY*canvas.width+x)*4;
}
alert("size",maxX-minX-1,"x",maxY-minY-1);
}
I was wandering if any one could help me with this svg problem. How do I get the mouse coordinate version of an svg object. Usually when a user clicks on the page, the click event gets trigger and the object has a mouse position in terms of x and y. In my case, I don't want to do it with an event. Is getting the mouse position possible by simply examining the svg object's properties like the x and y coordinate? I put together an example page, hope it makes it clearer. http://jsfiddle.net/kenny12/XBCHF/ is the link. Excerpt is:
var svg = document.getElementsByTagName('svg')[0];
var pt = svg.createSVGPoint();
var el1 = document.getElementsByTagName('rect')[0];
var log_svgcursorPoint,
log_mouseclick,
log_mousecoord;
function svgcursorPoint(evt){
pt.x = evt.clientX; pt.y = evt.clientY;
var a = svg.getScreenCTM();
log_svgcursorPoint = "offset based on svg"+ " x:" + a.e +" y:" + a.f;
var b = a.inverse();
return pt.matrixTransform(b);
};
(function(elem){
elem.addEventListener('mousedown',function(e){
log_mouseclick = "mouse clicked at"+ " x:" + e.clientX +" y:" + e.clientY ;
var svgmouse = svgcursorPoint(e);
log_mousecoord = "svg mouse at"+ " x:" + svgmouse.x +" y:" +svgmouse.y;
document.getElementById('op').innerHTML = log_svgcursorPoint + "<br>" + log_mouseclick + "<br>" + log_mousecoord;
},false);
})(el1);
(function calc_manually(){
var rec = document.getElementsByTagName("rect")[0];
var root = document.getElementsByTagName("svg")[0];
var x = rec.getAttribute("x");
var y = rec.getAttribute("y");
var CTM = root.getScreenCTM();
// How to get the mouse position these information without using events is the problem.
})();
Why don't you want an event? That's what they're for. If you're dealing with mouse coordinates, just stick standard DOM event listeners on your objects and then when they trigger, use the event.target.getBoundingClientRect() function for the element's position on-screen, and the event.offsetX/screenX and event.offsetY/screenY properties for the mouse coordinates.
simple demonstrator: http://jsfiddle.net/HBmYV/1/
you can use event layer as well which works better if the svg element is positioned some where besides 0,0 on the page p.x =event.layerX || event.clientX;