Raphael JS : mouseover/mouseout - problem with text-labels - label

I use Raphael JS to create an SVG-map with area's and textlabels. I want the area to highlight when you move the mouse over it.
I have this working now, but when I move the mouse over the label (in the center of the area) the mouseout-event for that area is triggered, so the area is unhighlighted again.
Is there any way to prevent this from happening, or a workaround ?

Create a rect with opacity set to 0 over the text and attach the event handlers on that rect. You can calculate the dimensions of the rect using getBBox() of the text.

Creating a set via Paper#set was the approach that worked for me. e.g.:
var st = paper.set();
st.push.apply(st, vectors); // `vectors`: my array of "hoverable" vectors
st.push(text); // `text`: my text vector for `vectors`
st.hover(function () {
text.show();
}, function () {
text.hide();
});

Related

d3.brush does not work in Firefox or Edge when embedded

I'm using the Swiper API. When there's any d3 Chart embedded, the brush receives senseless mouse coordinates, in any case not relative to the container where the click occurs. (That's in fact at least the surrounded svg.)
I'm trying to find a solution but I don't know how I can force d3.brushX to use mouse coordinates which are really relative.
I don't know whether this is a bug or not, it has probably not really to do with the brush itself, rather how the browser pass mouse clicks top-down the DIV's until the SVG will be reached.
Here's the Fiddle.
(just for the annoying code rule:)
// Add brushing
var brush = d3.brushX()
The second slide contains an embedded d3 line chart example, taken from here.
The fiddle works only in Chrome 75+.
Not in Firefox 68+ nor in Edge 44+.
Running the chart example standalone, it works in all available browsers. So I designate this post for Swiper and D3 hopefully to get a hint for an solution.
According to the problem here I found out, that I can change the behavior in the point.js routines an an workaround.
If a D3 chart makes use of an brush **and ** the SVG of the chart element is embedded by a surrounding DIV with an explicite width, mouse clicks will not be interpreted correctly in Firefox or Edge. In Chrome it works perfectly.
I changed the code like this to achieve that it works in FF and Edge, but lose functionality with Chrome:
function reverseTraversal(node,targetTagName) {
var p = node;
while(p.tagName != targetTagName) p = p.parentNode;
return p;
}
function point(node, event) {
var svg = node.ownerSVGElement || node;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
var p = reverseTraversal(node,"DIV");
var rect = p.getBoundingClientRect();
point.x = event.clientX + rect.width, point.y = event.clientY;
point = point.matrixTransform(node.getScreenCTM().inverse());
return [point.x, point.y];
}
var rect = node.getBoundingClientRect();
return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
}
As you can see, I have to traverse backwards until the closest DIV will be reached, get the bounding and add its width to the clientX coordinate.
Without adding the fixed width, the brush is unusable in the particular case.
To get working with all the browsers, maybe a switch is necessary.
It's not a perfect solution, just a workaround for d3.brushX behavior.

How can I click on coordinates on globe?

I am using chrome experiment's globe http://globe.chromeexperiments.com and I would like to click on any color coordinates to show popup but event listener is not being called. can somebody assist me how to do it?
You could try casting a ray from the camera into the scenery and then getting the intercepts. See the docs for rayCaster.
What you would do is create a new rayCaster object, then get the mouse coordinates of the user's screen. You would need to create some kind of "onclick" event on the parent element of the canvas.
Once the event occurs, the rayCaster object has a property which handles our situation, "setFromCamera". However, you need to provide it a list of all of the objects that you are interested in intersecting, that is all of the rectangles.
So you code would look something like this, note that I used jQuery for this:
var rayCaster = new THREE.Raycaster();
var onClickEventHandler = function(e) {
//Save the mouse data as a vector
//width and height pertain to the size of the canvas.
var mousePosition = new THREE.Vector3((e.offsetX / width) * 2 - 1,
-(e.offsetY / height) * 2 + 1,
0.5);
rayCaster.setFromCamera(mousePosition, camera);
//"objects" is the array of meshes that you care about the user intersecting
var intersects = rayCaster.intersectObjects(objects);
}
What is contained inside the "intersects" array is all of the items from the "objects" array which ere intersected by the ray in the order of intersection. You are likely interested in the first element of this array.
I don't know if that globe utility provides a better way of doing it, what I showed is the generic way of selecting an object in the scenery.

Hit testing against text shapes

I want to know whether a given point is inside or outside of a text shape. As you will notice in the sample I provided below, hitTest will return true as soon as the point is inside of the TextItem's bounds, and not only if the point is inside of the character itself. (You can experience this behavior best when you place your mouse pointer in the middle of the #)
Sample: Hit-testing against TextItem
I also tried drawing the character based on paths (as Raphaƫl is doing in their font samples) to use the paths itself for hit-testing but stumbled upon some quite strange behavior where (some) characters are not drawn correctly. (If you copy the path definition into a vector image software like Inkscape the text shapes are drawn correctly)
Sample: Drawing text as path
What is the most promising way to find out whether a given point is inside or outside of a text shape?
You can hit-test a text shape (or any other mathematically irregular shape) by texting whether the pixel under the mouse is transparent or not.
You can get the pixel-array for the entire canvas using:
var data=context.getImageData(0,0,canvas.width,canvas.height).data;
Then you can fetch the opacity (alpha) value for the pixel under the mouse like this:
var pixelIsTransparent = data[(mouseY*canvas.width+mouseX)*4+3]==0
If the pixel is not transparent then you're over the text shape.
If you have other non-text drawings on the canvas then those non-text drawings might give false-positives for your hit-tests. A workaround for that is to use a second in-memory canvas containing only your text-shape and then do hit testing against the pixels on that second canvas.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
ctx.strokeStyle='gray';
ctx.font='300px verdana';
var wasHit=false;
var isHit=false;
draw();
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
$("#canvas").mousemove(function(e){handleMouseMove(e);});
function draw(){
ctx.fillStyle=(isHit)?'green':'lightgray';
ctx.fillText("M",25,250);
ctx.strokeText("M",25,250);
}
function handleMouseMove(e){
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
isHit=(data[(mouseY*cw+mouseX)*4+3]>10)
if(!isHit==wasHit){
draw();
wasHit=isHit;
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p>Hit test: Move mouse over letter</p>
<canvas id="canvas" width=300 height=300></canvas>
After spending quite some time debugging paper.js code I finally found the solution for this problem.
Instead of using Path you are supposed to use CompoundPath:
A compound path contains two or more paths, holes are drawn where the paths overlap. All the paths in a compound path take on the style of the backmost path and can be accessed through its item.children list.
I also updated the example from above:
http://jsfiddle.net/64v7s6L9/1/

How to know if user is zooming and in which direction using d3 behavior zoom?

I'm developing an application which use d3 for charting. x axis is time scale. User can zoom in/out to expand or shrink x axis. Now I want to add some logic in the event handler based on the direction user is zooming. But I don't find an easy way to know if user is doing zoom and in which direction.
Can someone share some experience here?
You want to add an event handler to the zoom.
To run a function whenever the zoom changes, you can add an event handler for zoom as follows:
zoom.on("zoom", function () {
var scale = d3.event.scale;
var translate = d3.event.translate;
// Do your processing
});
If you want to tell the direction of the zoom, you can save the translate each time, and compare the current one to the previous.
Direction of zoom : Zooming in or zooming out
In version 4 of d3, ZoomEvent contains WheelEvent as sourceEvent property. So to have the direction i'am doing :
zoom.on("zoom", () => {
-1 * Math.sign(d3.event.sourceEvent.deltaY)
});
d3.event.sourceEvent.deltaY gives nigative values when you zooming in, for me i use negative values when i zoom out that's why i'am multipling by -1.
The value of d3.event.deltaY works only in wheelDelta method of the zoom (see code below) but it gives mes values in v3 when i called it in the handler of zoom.
d3
.zoom()
.wheelDelta((ev, i, g) => {
return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1) / 500;
})
.on('zoom', () => {
// ...
});

FabricJS Canvas, Scrolling parent container moves child hit area

I am using FabricJS to create an application. I am finding that scrolling a parent div/container offsets the selectable area of an object to the right in direct relation to amount scrolled.
So, if I have a canvas that is 1200x600 and a container div that is 600x600 and I add a rect to that canvas at 400, 120; when I scroll 200px, I can't click on the rect to select it. Rather, I have to move my mouse to 600, 120 (empty space) to get the cross bar and select the rect.
Not sure if this is known, or has a work around - but I would appreciate any help possible.
You'll have to modify FabricJs code to make it work.
The problem is in the getPointer function, if you search for it in all.js you'll notice the comment "this method needs fixing" from kangax.
A workaround can be substituting this function with
function getPointer(event) {
// TODO (kangax): this method needs fixing
return { x: pointerX(event) + document.getElementById("container").scrollLeft, y: pointerY(event) + document.getElementById("container").scrollTop };
}
where "container" is the wrapper div of you canvas. It's not nice, since you have to put the exact id, but it works.
Hope this helps.

Resources