Skip to content

Instantly share code, notes, and snippets.

@kwharrigan
Forked from bollwyvl/README.md
Created May 1, 2014 17:08
Show Gist options
  • Save kwharrigan/0561b1e91cf2be89d98e to your computer and use it in GitHub Desktop.
Save kwharrigan/0561b1e91cf2be89d98e to your computer and use it in GitHub Desktop.

SVG fragment builds for reveal.js

Basic use case

  • make an SVG (maybe in inkscape)
    • save it someplace reveal.js can find it (maybe next to your presentation)
    • figure out how to identify them (maybe use named layers)
  • in reveal.js/index.html
    • add reveal-svg-fragment.js as a dependency
    • in a <section> of reveal.js markup
      • add data-svg-fragment="<url of the someplace>" to something, e.g. a div
      • add some things with class="fragment" inside that thing
        • add title="<a selector>" to those things
          • [*|label=<a label>] is good
          • for more about selectors, check out the W3C page

Example

Let's assume I made an SVG in Inkscape, and saved it next to my index.html. It has three layers: base, fragment1 and fragment2.

<html>
  ...
  <section>
    <div data-svg-fragment="figure.svg#[*|label=base]">
      <a class="fragment" href="[*|label=fragment1]"></a>
      <a class="fragment" href="[*|label=fragment2]"></a>
    </div>
  </section>
  ...
  <script>
    ...
    Reveal.initialize({
      dependencies: [
        ...
        {
          // maybe you put this in `plugins`
          src: 'reveal-svg-fragment.js',
          condition: function(){
            return !!document.querySelector( '[data-svg-fragment]' );
          }
          // Additional options
          // defaults to using already-loaded version, or CDN
          //d3: "./d3.min.js",
          // use a different attribute for your fragment selector
          //selector: "title",
        }
        ...
      ]
    ...
    }
    ...
  </script>
  ...
</html>

LIMITATIONS

  • won't work in Chrome against file://
    • workarounds:
      • dropbox
      • sharepoint
      • confluence
      • gist
      • python -m SimpleHTTPServer
  • probably won't work in IE
    • wontfix
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - The HTML Presentation Framework</title>
<meta name="description" content="A framework for easily creating beautiful presentations using HTML">
<meta name="author" content="Hakim El Hattab">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="//cdn.jsdelivr.net/reveal.js/latest/css/reveal.min.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/reveal.js/latest/css/theme/default.css" id="theme">
<!-- For syntax highlighting -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/reveal.js/latest/lib/css/zenburn.css">
<!-- If the query includes 'print-pdf', include the PDF print sheet -->
<script>
if( window.location.search.match( /print-pdf/gi ) ) {
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = '//cdn.jsdelivr.net/reveal.js/latest/css/print/pdf.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
}
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Reveal.js</h1>
<h3>HTML Presentations Made Easy</h3>
<p>
<small>Created by <a href="http://hakim.se">Hakim El Hattab</a> / <a href="http://twitter.com/hakimel">@hakimel</a></small>
</p>
</section>
<section>
<h2>Ugly Test SVG</h2>
<div data-svg-fragment="test.svg#[*|label=base]">
<a class="fragment" title="[*|label=fragment1]"></a>
<a class="fragment" title="[*|label=fragment2]"></a>
</div>
</section>
</div>
</div>
<script src="//cdn.jsdelivr.net/reveal.js/latest/lib/js/head.min.js"></script>
<script src="//cdn.jsdelivr.net/reveal.js/latest/js/reveal.min.js"></script>
<script>
var cdn = "//cdn.jsdelivr.net/reveal.js/latest/";
// Full list of configuration options available here:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
transition: Reveal.getQueryHash().transition || 'default', // default/cube/page/concave/zoom/linear/fade/none
// Parallax scrolling
// parallaxBackgroundImage: 'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg',
// parallaxBackgroundSize: '2100px 900px',
// Optional libraries used to extend on reveal.js
dependencies: [
{ src: cdn + 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: cdn + 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: cdn + 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: cdn + 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: cdn + 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
{ src: cdn + 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } },
{ src: 'reveal-svg-fragment.js', condition: function() { return !!document.querySelector( '[data-svg-fragment]' ); } }
]
});
</script>
</body>
</html>
/*
Copyright (C) 2014 Nicholas Bollweg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
;Reveal.SvgFragment = (function(Reveal){
"use strict";
var window = this,
document = window.document,
proto = window.location.protocol,
local = proto === "file:",
defaults = {
d3: (local ? "http:" : proto) + "//cdn.jsdelivr.net/d3js/latest/d3.min.js",
selector: "title"
};
// the main function, to be called when d3 is available
function api(){
var d3 = window.d3,
container = d3.selectAll("[data-svg-fragment]");
container.data(function(){
return container[0].map(function(d){
var $ = d3.select(d);
return {
container: $,
url: $.attr("data-svg-fragment")
};
});
});
container.append("iframe")
.attr({
src: "about:blank",
// TODO: make this an option?
scrolling: "no"
})
.on("load", api.iframed);
Reveal.addEventListener("fragmentshown", api.listen(container, true));
Reveal.addEventListener("fragmenthidden", api.listen(container));
return api;
};
// generate listeners for reveal events
api.listen = function(container, show){
return function(event){
var fragment = d3.select(event.fragment);
container.filter(function(){
return this === event.fragment.parentNode;
}).each(function(item){
api.toggle(fragment, item, show);
});
return api;
};
};
// toggle a fragment
// TODO: add hide
api.toggle = function(fragment, item, show){
var selector = fragment.attr(api.cfg("selector"));
item.svg.selectAll(selector)
.transition()
.style({opacity: show ? 1 : 0});
return api;
};
// the iframe was created for this item
api.iframed = function(item){
item.iframe = d3.select(this);
item.idoc = d3.select(this.contentDocument);
d3.xml(item.url, "image/svg+xml", function(xml){
item.idoc.node().body.appendChild(xml.documentElement);
item.svg = item.idoc.select("svg");
api.svged(item);
});
return api;
};
// the svg was loaded for this item
api.svged = function(item){
item.iframe.attr({
width: item.svg.attr("width"),
height: item.svg.attr("height")
});
return api.clean(item);
};
// prepare
// TODO: smarter initialization?
api.clean = function(item){
var base;
item.container.selectAll(".fragment").each(function(){
item.svg.selectAll(d3.select(this).attr(api.cfg("selector")))
.style({opacity: 0});
});
if(base = item.url.match(/(?:#)(.*)$/)){
item.svg.selectAll(base[1])
.style({opacity: 1});
}
return api;
};
// preflight, call immediately it d3 is available, otherwise load the script
api.init = function(){
var options = Reveal.getConfig().svgFragment || {};
return window.api ? api() : api.load(api.cfg("d3"), api);
};
// get configuration values (or defaults)
api.cfg = function(opt){
var cfg = Reveal.getConfig().svgFragment || {};
return cfg.hasOwnProperty(opt) ? cfg[opt] :
defaults.hasOwnProperty(opt) ? defaults[opt] :
function(){ throw new Error("Unknown property: "+ opt); };
};
// load a script, jacked from search, i think
api.load = function(url, callback){
var head = document.querySelector('head'),
script = document.createElement('script');
// Wrapper for callback to make sure it only fires once
var finish = function(){
if(typeof callback === 'function') {
callback.call();
callback = null;
}
};
// IE
script.onreadystatechange = function() {
if (this.readyState === 'loaded') {
finish();
}
};
script.type = 'text/javascript';
script.src = url;
script.onload = finish;
// Normal browsers
head.appendChild(script);
return api;
};
return api.init();
}).call(this, Reveal);
Display the source blob
Display the rendered blob
Raw
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1052.3622"
height="744.09448"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="New document 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="605.81428"
inkscape:cy="339.73748"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:window-width="1920"
inkscape:window-height="1058"
inkscape:window-x="-8"
inkscape:window-y="832"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="base"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-308.2677)">
<rect
style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#999999;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2993"
width="664.68036"
height="515.1778"
x="106.34142"
y="408.89502" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="fragment1">
<text
xml:space="preserve"
style="font-size:236.08952332px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
x="217.79936"
y="439.42963"
id="text2996"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2998"
x="217.79936"
y="439.42963">1</tspan></text>
</g>
<g
inkscape:label="fragment2"
id="g3000"
inkscape:groupmode="layer">
<text
sodipodi:linespacing="125%"
id="text3002"
y="440.64005"
x="527.91626"
style="font-size:236.08952332px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
xml:space="preserve"><tspan
y="440.64005"
x="527.91626"
id="tspan3004"
sodipodi:role="line"
style="fill:#ffffff;stroke:#000000">2</tspan></text>
</g>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment