I was looking at the NYTimes interactive on state subsidies this morning and noticed that even when a state is obscured by a dot it is brought forward on hover.
For example the dot covering Massachusetts also partially covers New Hampshire, yet when you when you mouse into the covered part of New Hampshire, New Hampshire is brought forward.
How do you suppose they achieve this? The dots are in front of the state outlines based on their order in the DOM. I thought there might be a second set of state outlines on top of everything, listening for mouseovers that would trigger the underlying shape, but that doesn't seem to be the case.
I need to implement similar functionality in an application I'm working on and am curious about an elegant way with SVG elements.
Thanks.
As noted by Andy the circles in that NYT graphic have a CSS pointer-events property of none:
circle {
pointer-events: none;
}
the bring to front behavior can be achieved in a the mouseover function using this method:
stateShape.on("mouseover",function(){this.parentNode.appendChild(this);})
Now you can just use the raise() method:
Example in a function:
highlightSite(site) {
let selectedSite = d3.selectAll(`[siteName=${site}]`)
selectedSite.raise()
selectedSite
.attr('stroke', 'black')
.style('opacity', 1)
}
Related
I have a group of elements that are masked by a rect in SnapSVG and I want to translate the elements, bringing new ones into view (and hiding ones that are currently in view). The code is really simple - here's a codepen: http://codepen.io/austinclemens/pen/ZbpVmX
As you can see from the pen, box1, which starts outside the mask element (clip) should cross through it when animated, but it never appears. Moreover, box2, which should move out of the clipping area, remains visible.
This example seems to do a similar thing and has no problems: http://svg.dabbles.info/snaptut-masks2
Here's the code from codepen:
var t = Snap('#target')
var clip=t.rect(200,200,200,200).attr({fill:'#fff'})
var box1=t.rect(300,100,50,50).attr({fill:'#000'})
var box2=t.rect(300,300,50,50).attr({fill:'#000'})
var boxgroup=t.group(box1,box2)
boxgroup.attr({mask:clip})
boxgroup.animate({transform:'t100,300'},2000)
I notice that the svg.dabbles examples translates the clip region by 0,0 at one point, but adding something like that doesn't seem to get me anywhere.
Ok, I figured this out thanks in part to this really great article about SVG transforms: http://sarasoueidan.com/blog/svg-transformations/
The upshot is that when I translate the box group, it takes the mask with it. This is a little confusing to me still - I guess the mask attribute is causing this somehow? Anyways, the solution is to apply an opposite translation to the mask to keep it in place. Check the pen to see it in action but basically I just had to add:
clip.animate({transform:'t-100,-300'},2000)
The tricky part of this is that you now need to synchronize the movement of the mask and the movement of the box group.
edit - I now demonstrate how synchronization can be achieved using snap's set.animate method on the codepen.
This is not a: "Do all the work for me!" kind of question. I just wanna know which approach you think would be suitable for this challenge.
I have this map:
As you can see by the blue marker, I've roughly drawned some selections/areas of the map. Theese areas I want to serve as links.
But I don't quite know how to grasp this challenge, since all of the areas have quite odd shapes.
I have looked at cords, but it seems like a huge job with all of the twists and turns that I would need to do.
I would be awesome if I could just slice up the areas in Photoshop and save each of them as .png and just tell my page to ignore the transparent area! But that's just wishfull thinking I suppose.
I hope that one of you have a suggestion that I've overlooked.
Give a try to these -
http://polymaps.org/
http://www.amcharts.com/javascript-maps/
Raphael JS
You can try making an SVG version of your map and then implement it's clickiness with one of these libraries depending on which one you choose.
Here's one tutorial to do this with Raphael JS - http://parall.ax/blog/view/2985/tutorial-creating-an-interactive-svg-map
Make an image for each clickeable zone, like this:
Register to the click event of the img element from the page, this way:
var getAreaFromXY = function(x,y) {
// for each section colored map
// get pixel color on x,y (see http://stackoverflow.com/questions/8751020/how-to-get-a-pixels-x-y-coordinate-color-from-an-image)
// if the color is red, that is the zone
};
$(".post-text img").click(function(e) {
var area = getAreaFromXY(e.offsetX, e.offsetY);
});
Im using this great article to produce a venn diagram with D3.
http://www.benfrederickson.com/venn-diagrams-with-d3.js/
It looks great but on occasion I get bubbles overlapping the the labels become hidden. Is there a way to make sure the text element is always on top? (see the picture below.. label A needs to be on top of circle B.
I found this good article but im struggling in how to implement this in the venn.
How can I bring a circle to the front with d3?
You should grab the latest code from master: this commit should fix the issue you had there https://github.com/benfred/venn.js/commit/4cb3bbef65b5b3c3ce02aee7d913e8814e898baf
Instead of having the 'A' label be overtop of the 'B' circle - it willnow move the label so that its in the certain of the 'A' region that isn't overlapped with 'B'. Some details are in this issue here: https://github.com/benfred/venn.js/issues/18
You might find it easier to work in actual layers. You can use g elements to create them. For example:
var lowerLayer = svg.append('g');
var upperLayer = svg.append('g');
Now anything you append to upperLayer will appear above anything you append to lowerLayer because the two g elements have been added to the DOM and are in a specific order.
Also check out this answer I wrote up for a similar question.
I love the way this scatterchart highlights circles when you mouse over them: http://novus.github.com/nvd3/examples/scatterWithLegend.html
But there's a lot of code there (looks like the author has defined his/her own standard libraries) and I can't figure out exactly how the effect is achieved.
Is it something to do with the .hover class and the stroke-width property?
I'd like to achieve the same effect on my own scatter graph, although I'm using circles rather than paths, so it may not be possible.
In the example, the effect seems to be realised in scatter.js from line 136.
Just highlighting individual circles is much easier though and doesn't need all the other stuff the example does. All you need to do is add a mouseover handler to the circles and (for example) increase stroke-width. That would look something like
d3.selectAll("circle").data(...).enter()
.append(...)
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
I'm assuming that a CSS class highlight defines the style. Alternatively, you can just use (in this example) CSS class circle:hover without the need for the event handlers.
I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.
FWIW - I'm using Backbone.js views to modularize the shape logic.
Approach 1
My initial thought was to use a combination of foreignObject for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject, and therefore this approach had to be abandoned.
Approach 2
With the beside the canvas object, add divs that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div. There are a couple of problems with this approach:
Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?
Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support pointer-events, this is easily done:
div.shape-text-overlay {
position: absolute;
background: none;
pointer-events: none;
}
However, other browsers (like IE8 and below) need forwarding of the events:
var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
this.$elText.on(forwardedEvents, function(e) {
var original = e.originalEvent;
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent(e.type, true, true);
}
else {
event = document.createEventObject();
event.eventType = e.type;
}
// FYI - This is the most simplistic approach to event forwarding.
// My real implementation is much larger and uses MouseEvents and UIEvents separately.
event.eventName = e.type;
_.extend(event, original);
if (document.createEvent) {
that.el.node.dispatchEvent(event);
}
else {
that.el.node.fireEvent('on' + event.eventType, event);
}
});
Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:
This approach is what I'm currently using but it just feels like a huge hack.
Approach 3
Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.
Question
Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?
I built this project (no longer live) using Raphael. What I did is actually abandoned the idea of using HTML inside of SVG because it was just too messy. Instead, I absolutely positioned an HTML layer on top of the SVG layer and moved them around together. When I wanted the HTML to show, I merely faded it in and faded the corresponding SVG object out. If timed and lined up correctly, it's not really noticeable to the untrained eye.
While this may not necessarily be what you're looking for, maybe it will get your mind thinking of new ways to approach your problem! Feel free to look at the JS on that page, as it is unminified ;)
PS, the project is a lead-gathering application. If you just want to see how it works, select "Friend" in the first dropdown and you don't have to provide any info.
Unless another answer can be provided and trump my solution, I have continued with the extra div layer. Forwarding events was the most difficult part (if anyone requires the forwarding events code, I can post it here). Again, the largest downside to this solution is that overlapping shapes will cause their text to overlap above the actual drawn shape.