Konwertuj SVG na obraz (JPEG, PNG itp.) W przeglądarce


299

Chcę przekonwertować SVG na obrazy bitmapowe (takie jak JPEG, PNG itp.) Za pomocą JavaScript.


Jakie to zadanie naprawdę chcesz wykonać? Mimo że odpowiedzi przepływów echa mówią nam, że jest możliwe (w niektórych przeglądarkach), że istnieją lepsze i łatwiejsze metody konwersji dla prawie wszystkich praktycznych przypadków.
aaaaaaaaaaaa

2
Oto przykład z użyciem d3: stackoverflow.com/a/23667012/439699
as

svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Działa idealnie! [Na stronie linku sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh

Odpowiedzi:


244

Oto, jak możesz to zrobić za pomocą JavaScript:

  1. Użyj biblioteki JavaScript canvg, aby wyrenderować obraz SVG za pomocą Canvas: https://github.com/gabelerner/canvg
  2. Przechwyć URI danych zakodowany jako JPG (lub PNG) z Canvas, zgodnie z tymi instrukcjami: Capture HTML Canvas jako gif / jpg / png / pdf?

28
To nie jest wyłącznie Javascript, ale także HTML5. To nie będzie działać na IE8 ani żadnej innej przeglądarce, która nie obsługuje HTML5 Canvas.
James

16
Jeśli przeglądarka obsługuje SVG i płótno, to byłoby znacznie prostszy sposób załadowania SVG do pamięci, a następnie pomalowania go na płótno, bez potrzeby Canvg, która jest dość dużą biblioteką, ponieważ obsługuje wszystkie parsowania SVG, które przeglądarka obsługująca SVG zapewnia już za darmo. Nie jestem pewien, czy to odpowiada oryginalnemu przypadkowi użycia, ale jeśli tak, zobacz ten zasób, aby uzyskać szczegółowe informacje .
Premasagar

120
Dziękujemy za brak obsługi IE8. Ludzie powinni zrozumieć, że nadszedł czas, aby przejść dalej.
Sanket Sahu

9
Możesz teraz użyć biblioteki Pablo JavaScript SVG, aby to osiągnąć (zrobiłem to). Zobacz toImage()i także download()dla automatycznie pobranego obrazu.
Premasagar

2
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - Działa idealnie! [Na stronie linku sourceSVG = $ ("# your_svg_elem_name"). Get (0)]
Vijay Singh

44

Rozwiązanie jbeard4 działało pięknie.

Korzystam z programu Raphael SketchPad, aby utworzyć plik SVG. Link do plików w kroku 1.

W przypadku przycisku Zapisz (identyfikator svg to „edytor”, identyfikator płótna to „canvas”):

$("#editor_save").click(function() {

// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());

// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});

1
canvg potrzebuje drugiego parametru, <svg>...</svgale funkcja hquml () jquery nie dodaje tagu svg, więc ten kod działa dla mnie, ale musiałem edytować live canvg docanvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
Luckyn

1
@Luckyn, jeśli zadzwonisz $(selector).html()do rodzica elementu svg , zadziała
jonathanGB

@Luckyn i @jonathanGB, nie powinieneś używać html()na opakowaniach ani ręcznie konstruować svgtagu nadrzędnego - który może nawet mieć atrybuty, które pomijasz podczas tego włamania. Wystarczy użyć $(svg_elem)[0].outerHTMLdaje pełne źródło svg i jego zawartość. Tylko mówię ...
nemesisfixx,

18

Wydaje się, że działa to w większości przeglądarek:

function copyStylesInline(destinationNode, sourceNode) {
   var containerElements = ["svg","g"];
   for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
       var child = destinationNode.childNodes[cd];
       if (containerElements.indexOf(child.tagName) != -1) {
            copyStylesInline(child, sourceNode.childNodes[cd]);
            continue;
       }
       var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
       if (style == "undefined" || style == null) continue;
       for (var st = 0; st < style.length; st++){
            child.style.setProperty(style[st], style.getPropertyValue(style[st]));
       }
   }
}

function triggerDownload (imgURI, fileName) {
  var evt = new MouseEvent("click", {
    view: window,
    bubbles: false,
    cancelable: true
  });
  var a = document.createElement("a");
  a.setAttribute("download", fileName);
  a.setAttribute("href", imgURI);
  a.setAttribute("target", '_blank');
  a.dispatchEvent(evt);
}

function downloadSvg(svg, fileName) {
  var copy = svg.cloneNode(true);
  copyStylesInline(copy, svg);
  var canvas = document.createElement("canvas");
  var bbox = svg.getBBox();
  canvas.width = bbox.width;
  canvas.height = bbox.height;
  var ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, bbox.width, bbox.height);
  var data = (new XMLSerializer()).serializeToString(copy);
  var DOMURL = window.URL || window.webkitURL || window;
  var img = new Image();
  var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
  var url = DOMURL.createObjectURL(svgBlob);
  img.onload = function () {
    ctx.drawImage(img, 0, 0);
    DOMURL.revokeObjectURL(url);
    if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
    {
        var blob = canvas.msToBlob();         
        navigator.msSaveOrOpenBlob(blob, fileName);
    } 
    else {
        var imgURI = canvas
            .toDataURL("image/png")
            .replace("image/png", "image/octet-stream");
        triggerDownload(imgURI, fileName);
    }
    document.removeChild(canvas);
  };
  img.src = url;
}

3
To nie działa w IE11, z powodu problemu bezpieczeństwa z.msToBlob()
Florian Leitgeb

Dzięki!! Uwielbiam to, jak to działa zarówno w przypadku „lokalnego” węzła HTML SVG, jak i zdalnego adresu URL SVG. Ponadto nie wymaga pełnej biblioteki zewnętrznej
Fabricio PH

7

Rozwiązanie do konwersji SVG na URL obiektu blob i URL obiektu blob do obrazu png

const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
   <rect width="100%" height="100%" fill="red" />
   <circle cx="150" cy="100" r="80" fill="green" />
   <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>{
    const pngImage = document.createElement('img');
    document.body.appendChild(pngImage);
    pngImage.src=imgData;
});
 function svgToPng(svg, callback) {
    const url = getSvgUrl(svg);
    svgUrlToPng(url, (imgData) => {
        callback(imgData);
        URL.revokeObjectURL(url);
    });
}
function getSvgUrl(svg) {
    return  URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
    const svgImage = document.createElement('img');
    // imgPreview.style.position = 'absolute';
    // imgPreview.style.top = '-9999px';
    document.body.appendChild(svgImage);
    svgImage.onload = function () {
        const canvas = document.createElement('canvas');
        canvas.width = svgImage.clientWidth;
        canvas.height = svgImage.clientHeight;
        const canvasCtx = canvas.getContext('2d');
        canvasCtx.drawImage(svgImage, 0, 0);
        const imgData = canvas.toDataURL('image/png');
        callback(imgData);
        // document.body.removeChild(imgPreview);
    };
    svgImage.src = svgUrl;
 }


3

Napisałem tę klasę ES6, która wykonuje zadanie.

class SvgToPngConverter {
  constructor() {
    this._init = this._init.bind(this);
    this._cleanUp = this._cleanUp.bind(this);
    this.convertFromInput = this.convertFromInput.bind(this);
  }

  _init() {
    this.canvas = document.createElement("canvas");
    this.imgPreview = document.createElement("img");
    this.imgPreview.style = "position: absolute; top: -9999px";

    document.body.appendChild(this.imgPreview);
    this.canvasCtx = this.canvas.getContext("2d");
  }

  _cleanUp() {
    document.body.removeChild(this.imgPreview);
  }

  convertFromInput(input, callback) {
    this._init();
    let _this = this;
    this.imgPreview.onload = function() {
      const img = new Image();
      _this.canvas.width = _this.imgPreview.clientWidth;
      _this.canvas.height = _this.imgPreview.clientHeight;
      img.crossOrigin = "anonymous";
      img.src = _this.imgPreview.src;
      img.onload = function() {
        _this.canvasCtx.drawImage(img, 0, 0);
        let imgData = _this.canvas.toDataURL("image/png");
        if(typeof callback == "function"){
            callback(imgData)
        }
        _this._cleanUp();
      };
    };

    this.imgPreview.src = input;
  }
}

Oto jak z niego korzystasz

let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
    // You now have your png data in base64 (imgData). 
    // Do what ever you wish with it here.
});

Jeśli chcesz waniliową wersję JavaScript, możesz udać się na stronę Babel i tam przetransportować kod.


2

Oto rozwiązanie po stronie serwera oparte na PhantomJS. Możesz użyć JSONP do wykonania połączenia między domenami do usługi obrazu:

https://github.com/vidalab/banquo-server

Na przykład:

http: // [host] /api/https%3A%2F%2Fvida.io%2Fdocuments%2FWgBMc4zDWF7YpqXGR/viewport_width=980&viewport_height=900&delay=5000&selector=%23canvas

Następnie możesz wyświetlić obraz z tagiem img:

<img src="data:image/png;base64, [base64 data]"/>

Działa w różnych przeglądarkach.


Usługa wydaje się być martwa.

3
Nasz gospodarz został trafiony fałszywymi prośbami. Więc postanowiliśmy to zdjąć. Musisz teraz uruchomić własny serwer. Zobacz repozytorium github, aby uzyskać więcej informacji.
Phuoc Do

1

zmień, svgaby dopasować swój element

function svg2img(){
    var svg = document.querySelector('svg');
    var xml = new XMLSerializer().serializeToString(svg);
    var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
    var b64start = 'data:image/svg+xml;base64,';
    var image64 = b64start + svg64;
    return image64;
};svg2img()

1
to nie działa dla mnie, otrzymuję ten błąd:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
Xsmael

1
@Xsmael próbuje przełączyć interfejs DOMParser developer.mozilla.org/en-US/docs/Web/API/DOMParser
Mahdi Khalili

1

Svgdo pngkonwersji w zależności od warunków:

  1. Jeśli svgjest w formacie ścieżki SVG (ciąg) :
    • stworzyć płótno
    • utwórz new Path2D()i ustaw svgjako parametr
    • narysuj ścieżkę na płótnie
    • utwórz obraz i użyj canvas.toDataURL()jako src.

przykład:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;

Zauważ, że Path2Dnie obsługiwane iei częściowo obsługiwane w Edge. Polyfill rozwiązuje to: https://github.com/nilzona/path2d-polyfill

  1. Utwórz svgkroplę i narysuj na płótnie, używając .drawImage():
    • zrobić element na płótnie
    • utwórz obiekt svgBlob z pliku XML svg
    • utwórz obiekt url z domUrl.createObjectURL (svgBlob);
    • utwórz obiekt Image i przypisz adres URL do obrazu src
    • narysuj obraz na płótnie
    • pobierz ciąg danych png z canvas: canvas.toDataURL ();

Ładny opis: http://ramblings.mcpher.com/Home/excelquirks/gassnips/svgtopng

Zauważ, że np. Otrzymasz wyjątek na etapie canvas.toDataURL (); Jest tak, ponieważ IE ma zbyt wysokie ograniczenia bezpieczeństwa i traktuje płótno jako tylko do odczytu po narysowaniu tam obrazu. Wszystkie inne przeglądarki ograniczają tylko wtedy, gdy obraz ma pochodzenie krzyżowe.

  1. Użyj canvgbiblioteki JavaScript. Jest to osobna biblioteka, ale ma przydatne funkcje.

Lubić:

ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();

Trzeci link jest zepsuty
Serdar Sayın

w rzeczy samej. Nie wiem, jak się tam teraz dostać. Ale powyższy opis może wystarczyć do pewnego zrozumienia. Dobry pomysł na przyszłe kopiowanie kontekstu po referencji
Alex Vovchuk

0

Niedawno odkryłem kilka bibliotek śledzenia obrazów dla JavaScript, które rzeczywiście są w stanie stworzyć akceptowalne przybliżenie mapy bitowej, zarówno pod względem wielkości, jak i jakości. Tworzę tę bibliotekę JavaScript i interfejs CLI:

https://www.npmjs.com/package/svg-png-converter

Który zapewnia ujednolicony interfejs API dla wszystkich, obsługujący przeglądarkę i węzeł, w zależności od modelu DOM, oraz narzędzie wiersza poleceń.

Do konwersji logo / kreskówek / podobnych obrazów wykonuje doskonałą robotę. W przypadku zdjęć / realizmu potrzebne są pewne poprawki, ponieważ rozmiar wyjściowy może znacznie wzrosnąć.

Ma plac zabaw, chociaż teraz pracuję nad lepszym, łatwiejszym w użyciu, ponieważ dodano więcej funkcji:

https://cancerberosgx.github.io/demos/svg-png-converter/playground/#

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.