What are we going to tackle?
When creating D3 graphs, the more graphs you put in your page, the less readable it tends to be. A friend of mine advise me to set my graphs in fullscreen. I immediately loved the idea. That would definitely help users to focus.What do we need to solve?
- Our graphs need to be responsive: when they will be toggled on fullscreen, they need to expand and take as much space as possible.
- When setting up fullscreen, the viewport is black despite all your already setup styles.
- Tooltips on your graphs needs to be included in the fullscreen's viewport.
- As your viewport is changing so are the references on where your tooltips needs to be displayed.
Piece by piece solution
First, let's setup our markup in Jade. figure is our SVG container, the div, .svg-content, is where D3 will place our SVG content and the div, .tip, is our tooltip for this graph:
figure .svg-content .tip span Point .arrowNow, we need to setup our style so that our SVG content takes the available width of its container or the width of the viewport when it will be set on fullscreen. Note that we also need to set the background color of the container so that we don't end up with a black viewport. Here is the relevant extract of the Sylus file:
// Container for responsive SVG // Mixin for creating SVG container with specific aspect ratio svgContainer() display inline-block position relative width 100% vertical-align middle overflow hidden figure display inline-block absolute top left height 100% margin 0 // This container targets 1:1 SVG svgContainer() .svg-content // Here are the properties for the fullscreen content &:fullscreen size 100% background whiteFor the tooltip, we use a fixed position that will be modified using our logic. The style is pretty straightforward in Stylus:
.tip fixed top leftNow, for creating a responsive D3 graph, we need to use the preserveAspectRatio and the viewBox attributes on the graph instead of the regular width and height on the SVG tag. This is what is done by the following logic in Coffee on a 100x100 graph:
svgWidth = svgHeight = 100 svg = d3.select '.svg-content' .append 'svg:svg' .attr 'preserveAspectRatio', 'xMinYMin meet' .attr 'viewBox', "0 0 #{svgWidth} #{svgHeight}"Setting the graph in fullscreen is eased thanks to screenfull.js. This library provide a cross browser API of the vanilla JavaScript Fullscreen API:
Template.home.rendered = -> @fullscreen = -> return screenfull.exit() if screenfull.isFullscreen target = (@$ '.svg-content')[0] screenfull.request target Template.home.events 'click button': (e, t) -> $button = t.$ e.target role = $button.attr 'data-role' t.fullscreen() if role is 'fullscreen'Now, we need to adjust the tooltips so that they are positioned properly. In our example, we are using a Voronoï diagram. Hovering on the path elements of the Voronoï is triggering the tooltip of its points modeled by circle elements. Getting the position is achieved thank to the DOM API getBoundingClientRect:
tip = @$ '.tip' @positionSetTip = (circle) -> rect = circle[0].getBoundingClientRect() tip.css 'transform', "translate3d(\ #{rect.left + .5 * (rect.width - tip.width())}px,\ #{rect.top - tip.height()}px, 0)" @showHideTip = -> tip.toggleClass 'show' @showTip = -> Meteor.setTimeout (-> tip.addClass 'show'), 300 # A debouncing function is used for transitioning over path @lazyShowHideTip = _.debounce @showHideTip, 300 path = svg.append 'g' .selectAll 'path' svg.append 'g' .selectAll 'circle' .data vertice .enter() .append 'circle' .attr 'transform', (d) -> "translate(#{d.toString()})" .attr 'r', 1 data = path.data (voronoi vertice) data .enter() .append 'path' .attr 'd', polygon .order() .on 'mouseover', (d, i) => circle = $ "circle:nth-child(#{i + 1})" @positionSetTip circle, i @showTip() .on 'mouseleave', => @lazyShowHideTip() data.exit().remove()
Some links
- Demo of a Voronoï diagram set in fullscreen.
- Source of the demo is available on Github.
- The library used for easing the cross browser setup of the Fullscreen API: screenfull.js.
- Creative & Pure is the used CSS framework for this little demo.
Bonus
On the demo, I've also added some features like:
- An animated tooltip: when entering or leaving a small animation is done.
- The Voronoï graph is animated using a simple random function.
- Key events are handle to let you set the graph in fullscreen or to animate it.
Happy coding!