block by shimizu fda228d7229df38673cc6d5c12776cf4

SVG download module

Full Screen

解説

https://shimz.me/blog/d3v4/5126

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<style>
html, body {
    width: 100%;
    height: 100%;
    padding: 0px;
    margin:  0px;
}
#chart {
    width: 960px;
    height: 400px;    
}
.bg {
    fill:white;
}
.bar {
    fill:skyblue;
}
.axisLayer .axis .domain {
    stroke: #333333;
}
.axisLayer .tick line {
    stroke: #333333;
    stroke-width: 1px;
}
.axisLayer .tick text {
    fill: #333333;
    font-size: 14px;
    letter-spacing: .05em;
}
.axisLayer .label {
    font-size: 12px;
    font-weight: normal;
    letter-spacing: .05em;
}

.backgroundLayer .grid line {
    stroke: #cccccc;
    stroke-dasharray: 3,3;
}
.backgroundLayer .grid .domain {
    stroke: none;
}
</style>
</head>

<body>
    <button id="update">updarte</button>  
    <div id="chart"></div>
    <button id="downloadSVG">SVG Download</button>  
    <button id="downloadPNG">PNG Download</button>  

<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>     
<script src="//bl.ocks.org/shimizu/raw/0b526eab82263c8443108c33e454d221/nChart.js"></script>
<script src="createDownloader.js"></script>
<script>
!(function(){
    "use strict"

    var data = generateData()
    
    var BarChart = nChart.createVStackBarChart()
        .baseMargin({top:20, left:0, bottom:20, right:0})
        .plotMargin({top:20, left:100, bottom:20, right:100})
        .x(function(d){ return d["年"] })
        .s(function(d){ return d["国名"] })
        .y(function(d){ return d["値"] })


    var Axis = nChart.createAxis()
        .yAxisGridVisible(true)    

    var downloader = createDownloader()        

    var selector = d3.selectAll("#chart")
        .datum(data)
        .call(BarChart)
        .call(Axis)
        .call(downloader)

    d3.select("#update").on("click", function(){
        selector.update(generateData())
    })          

    d3.select("#downloadSVG").on("click", selector.downloadSVG )    
    d3.select("#downloadPNG").on("click", selector.downloadPNG )        

    
    function generateData(){
        return Array.prototype.concat.apply([], ["日本", "アメリカ", "フランス"].map(function(country){
            var array  = [2001,2002,2003,2004].map(function(year){
                var value = ~~(Math.random() * 100)
                return {"国名":country, "年":year, "値":value}                
            })
            return array
        }))   
    }    

    
}());
    

</script>    
</body>
</html>

createDownloader.js

function createDownloader(){
    
    var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
    var prefix = {
        xmlns: "http://www.w3.org/2000/xmlns/",
        xlink: "http://www.w3.org/1999/xlink",
        svg: "http://www.w3.org/2000/svg"
    }
    
    
    
    function exports(_selection) {
        var svg = _selection.node()
        
        var w = svg.clientWidth, h = svg.clientHeight
        
        
        var _emptySvg,_emptySvgDeclarationComputed
        var _copyChart
        
        
        
        function createEmptySVG() {
            _emptySvg = window.document.createElementNS(prefix.svg, 'svg');
            window.document.body.appendChild(_emptySvg);
            _emptySvgDeclarationComputed = getComputedStyle(_emptySvg);
            
        }
        
        function createCopySVG() {
            _copyChart = d3.select("body")
                .append("div")
                .html(svg.innerHTML)
                .node()            
            
        }

        function traverse(obj){
            var tree = [];
            tree.push(obj);
            visit(obj);
            function visit(node) {
                if (node && node.hasChildNodes()) {
                    var child = node.firstChild;
                    while (child) {
                        if (child.nodeType === 1 && child.nodeName != 'SCRIPT'){
                            tree.push(child);
                            visit(child);
                        }
                        child = child.nextSibling;
                    }
                }
            }
            return tree;
        }
        
        function explicitlySetStyle(element) {
            var cSSStyleDeclarationComputed = getComputedStyle(element)
            var attributes = Object.keys(element.attributes).map(function(i){ return element.attributes[i].name } )          
            var i, len
            var computedStyleStr = ""
            for (i=0, len=cSSStyleDeclarationComputed.length; i<len; i++) {
                var key=cSSStyleDeclarationComputed[i]             
                var value=cSSStyleDeclarationComputed.getPropertyValue(key)
                if(!attributes.some(function(k){ return k === key}) && value!==_emptySvgDeclarationComputed.getPropertyValue(key)) {
                    computedStyleStr+=key+":"+value+";"
                }            
            }
            element.setAttribute('style', computedStyleStr);
        }        

        function downloadSVG(source) {
            var filename = "chart.svg";
            var svg = d3.select(source).select("svg")
                .attr("xmlns", prefix.svg)
                .attr("version", "1.1")            
                .node()
            
            var blobObject = new Blob([doctype +  (new XMLSerializer()).serializeToString(svg)], { "type" : "text\/xml" })   
            
            if (navigator.appVersion.toString().indexOf('.NET') > 0){ //IE hack
                window.navigator.msSaveBlob(blobObject, filename)

            }else {
                var url = window.URL.createObjectURL(blobObject)                
                var a = d3.select("body").append("a")
                
                a.attr("class", "downloadLink")
                    .attr("download", "chart.svg")
                    .attr("href", url)
                    .text("test")
                    .style("display", "none")
                    
                    a.node().click()
    
                setTimeout(function() {
                  window.URL.revokeObjectURL(url)
                  a.remove()
                }, 10);            
            }
        }    


        function downloadPNG(source) {
            var filename = "chart.png";
            
            var svg = d3.select(source).select("svg")
                .attr("xmlns", prefix.svg)
                .attr("version", "1.1")
                .node()
                                    
            var data_uri =  "data:image/svg+xml;utf8," +   encodeURIComponent( (new XMLSerializer()).serializeToString(svg) )
                        
            var canvas = d3.select("body").append("canvas")
                .attr("id", "drawingArea")
                .attr("width", w)
                .attr("height", h)
                .style("display", "none")
            
            var context = canvas.node().getContext("2d")


            var download = function() {
            
                if (navigator.appVersion.toString().indexOf('.NET') > 0){
                
                    canvg(document.getElementById('drawingArea'), (new XMLSerializer()).serializeToString(svg))
    
                    var dataURI2Blob = function(dataURI, dataTYPE) {
                           var binary = atob(dataURI.split(',')[1]), array = [];
                           for(var i = 0; i < binary.length; i++) array.push(binary.charCodeAt(i));
                           return new Blob([new Uint8Array(array)], {type: dataTYPE});
                       }                   
                    
                    var data_uri = canvas.node().toDataURL("image/png")
                    var blobObject = dataURI2Blob(data_uri, "image/png")
                        
                    window.navigator.msSaveBlob(blobObject, filename)
    
                }else {
    
                    context.drawImage(img, 0, 0) 
                    var url = canvas.node().toDataURL("image/png")
                    var a = d3.select("body").append("a").attr("id", "downloadLink")
                    
                    a.attr("class", "downloadLink")
                        .attr("download", filename)
                        .attr("href", url)
                        .text("test")
                        .style("display", "none")
                        
                        a.node().click()
        
                    setTimeout(function() {
                      window.URL.revokeObjectURL(url)
                      canvas.remove()
                      a.remove()
                    }, 10);            
                }
                    
                
            }                
                
            var img = new Image();
            img.src = data_uri
            if (navigator.appVersion.toString().indexOf('.NET') > 0){ //IE hack
                d3.select(img).attr("onload", download)
            }else{
                img.addEventListener('load', download, false)                
            }
            
            
        }
        
        
        
        /**
         * @callback downloadSVG
         * @desc svgをダウンロードする
         */        
        _selection.downloadSVG = function(){
            
            createEmptySVG()
            createCopySVG()
            
            var allElements = traverse(_copyChart)            
            var i = allElements.length;            
            while (i--){
                explicitlySetStyle(allElements[i]);
            }
            
            downloadSVG(_copyChart)
            
            d3.select(_copyChart).remove()
            d3.select(_emptySvg).remove()
            
        }
        
        /**
         * @callback downloadPNG
         * @desc pngをダウンロードする
         */        
        _selection.downloadPNG = function(){
            
            createEmptySVG()
            createCopySVG()
            
            var allElements = traverse(_copyChart)            
            var i = allElements.length;            
            while (i--){
                explicitlySetStyle(allElements[i]);
            }
            
            downloadPNG(_copyChart)
            
            d3.select(_copyChart).remove()
            d3.select(_emptySvg).remove()
            
        }

        
    }
    
    

    return exports
    
}