zoukankan      html  css  js  c++  java
  • svg保存为图片下载到本地

    今天给大家说一个将svg下载到本地图片的方法,这里我不得不吐槽一下,为啥博客园不可以直接上传本地文件给大家用来直接下载分享呢,好,吐槽到此为止!

    这里需要用到一个js文件,名字自己起,内容如下:

    (function() {
    const out$ = typeof exports != 'undefined' && exports || typeof define != 'undefined' && {} || this || window;
    if (typeof define !== 'undefined') define('save-svg-as-png', [], () => out$);

    const xmlns = 'http://www.w3.org/2000/xmlns/';
    const 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" [<!ENTITY nbsp " ">]>';
    const urlRegex = /url(["']?(.+?)["']?)/;
    const fontFormats = {
    woff2: 'font/woff2',
    woff: 'font/woff',
    otf: 'application/x-font-opentype',
    ttf: 'application/x-font-ttf',
    eot: 'application/vnd.ms-fontobject',
    sfnt: 'application/font-sfnt',
    svg: 'image/svg+xml'
    };

    const isElement = obj => obj instanceof HTMLElement || obj instanceof SVGElement;
    const requireDomNode = el => {
    if (!isElement(el)) throw new Error(`an HTMLElement or SVGElement is required; got ${el}`);
    };
    const isExternal = url => url && url.lastIndexOf('http',0) === 0 && url.lastIndexOf(window.location.host) === -1;

    const getFontMimeTypeFromUrl = fontUrl => {
    const formats = Object.keys(fontFormats)
    .filter(extension => fontUrl.indexOf(`.${extension}`) > 0)
    .map(extension => fontFormats[extension]);
    if (formats) return formats[0];
    console.error(`Unknown font format for ${fontUrl}. Fonts may not be working correctly.`);
    return 'application/octet-stream';
    };

    const arrayBufferToBase64 = buffer => {
    let binary = '';
    const bytes = new Uint8Array(buffer);
    for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
    return window.btoa(binary);
    }

    const getDimension = (el, clone, dim) => {
    const v =
    (el.viewBox && el.viewBox.baseVal && el.viewBox.baseVal[dim]) ||
    (clone.getAttribute(dim) !== null && !clone.getAttribute(dim).match(/%$/) && parseInt(clone.getAttribute(dim))) ||
    el.getBoundingClientRect()[dim] ||
    parseInt(clone.style[dim]) ||
    parseInt(window.getComputedStyle(el).getPropertyValue(dim));
    return typeof v === 'undefined' || v === null || isNaN(parseFloat(v)) ? 0 : v;
    };

    const getDimensions = (el, clone, width, height) => {
    if (el.tagName === 'svg') return {
    width || getDimension(el, clone, 'width'),
    height: height || getDimension(el, clone, 'height')
    };
    else if (el.getBBox) {
    const {x, y, width, height} = el.getBBox();
    return {
    x + width,
    height: y + height
    };
    }
    };

    const reEncode = data =>
    decodeURIComponent(
    encodeURIComponent(data)
    .replace(/%([0-9A-F]{2})/g, (match, p1) => {
    const c = String.fromCharCode(`0x${p1}`);
    return c === '%' ? '%25' : c;
    })
    );

    const uriToBlob = uri => {
    const byteString = window.atob(uri.split(',')[1]);
    const mimeString = uri.split(',')[0].split(':')[1].split(';')[0]
    const buffer = new ArrayBuffer(byteString.length);
    const intArray = new Uint8Array(buffer);
    for (let i = 0; i < byteString.length; i++) {
    intArray[i] = byteString.charCodeAt(i);
    }
    return new Blob([buffer], {type: mimeString});
    };

    const query = (el, selector) => {
    if (!selector) return;
    try {
    return el.querySelector(selector) || el.parentNode && el.parentNode.querySelector(selector);
    } catch(err) {
    console.warn(`Invalid CSS selector "${selector}"`, err);
    }
    };

    const detectCssFont = (rule, href) => {
    // Match CSS font-face rules to external links.
    // @font-face {
    // src: local('Abel'), url(https://fonts.gstatic.com/s/abel/v6/UzN-iejR1VoXU2Oc-7LsbvesZW2xOQ-xsNqO47m55DA.woff2);
    // }
    const match = rule.cssText.match(urlRegex);
    const url = (match && match[1]) || '';
    if (!url || url.match(/^data:/) || url === 'about:blank') return;
    const fullUrl =
    url.startsWith('../') ? `${href}/../${url}`
    : url.startsWith('./') ? `${href}/.${url}`
    : url;
    return {
    text: rule.cssText,
    format: getFontMimeTypeFromUrl(fullUrl),
    url: fullUrl
    };
    };

    const inlineImages = el => Promise.all(
    Array.from(el.querySelectorAll('image')).map(image => {
    let href = image.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || image.getAttribute('href');
    if (!href) return Promise.resolve(null);
    if (isExternal(href)) {
    href += (href.indexOf('?') === -1 ? '?' : '&') + 't=' + new Date().valueOf();
    }
    return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas');
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.src = href;
    img.onerror = () => reject(new Error(`Could not load ${href}`));
    img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    canvas.getContext('2d').drawImage(img, 0, 0);
    image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', canvas.toDataURL('image/png'));
    resolve(true);
    };
    });
    })
    );

    const cachedFonts = {};
    const inlineFonts = fonts => Promise.all(
    fonts.map(font =>
    new Promise((resolve, reject) => {
    if (cachedFonts[font.url]) return resolve(cachedFonts[font.url]);

    const req = new XMLHttpRequest();
    req.addEventListener('load', () => {
    // TODO: it may also be worth it to wait until fonts are fully loaded before
    // attempting to rasterize them. (e.g. use https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet)
    const fontInBase64 = arrayBufferToBase64(req.response);
    const fontUri = font.text.replace(urlRegex, `url("data:${font.format};base64,${fontInBase64}")`)+' ';
    cachedFonts[font.url] = fontUri;
    resolve(fontUri);
    });
    req.addEventListener('error', e => {
    console.warn(`Failed to load font from: ${font.url}`, e);
    cachedFonts[font.url] = null;
    resolve(null);
    });
    req.addEventListener('abort', e => {
    console.warn(`Aborted loading font from: ${font.url}`, e);
    resolve(null);
    });
    req.open('GET', font.url);
    req.responseType = 'arraybuffer';
    req.send();
    })
    )
    ).then(fontCss => fontCss.filter(x => x).join(''));

    let cachedRules = null;
    const styleSheetRules = () => {
    if (cachedRules) return cachedRules;
    return cachedRules = Array.from(document.styleSheets).map(sheet => {
    try {
    return {rules: sheet.cssRules, href: sheet.href};
    } catch (e) {
    console.warn(`Stylesheet could not be loaded: ${sheet.href}`, e);
    return {};
    }
    });
    };

    const inlineCss = (el, options) => {
    const {
    selectorRemap,
    modifyStyle,
    modifyCss,
    fonts
    } = options || {};
    const generateCss = modifyCss || ((selector, properties) => {
    const sel = selectorRemap ? selectorRemap(selector) : selector;
    const props = modifyStyle ? modifyStyle(properties) : properties;
    return `${sel}{${props}} `;
    });
    const css = [];
    const detectFonts = typeof fonts === 'undefined';
    const fontList = fonts || [];
    styleSheetRules().forEach(({rules, href}) => {
    if (!rules) return;
    Array.from(rules).forEach(rule => {
    if (typeof rule.style != 'undefined') {
    if (query(el, rule.selectorText)) css.push(generateCss(rule.selectorText, rule.style.cssText));
    else if (detectFonts && rule.cssText.match(/^@font-face/)) {
    const font = detectCssFont(rule, href);
    if (font) fontList.push(font);
    } else css.push(rule.cssText);
    }
    });
    });

    return inlineFonts(fontList).then(fontCss => css.join(' ') + fontCss);
    };

    out$.prepareSvg = (el, options, done) => {
    requireDomNode(el);
    const {
    left = 0,
    top = 0,
    w,
    height: h,
    scale = 1,
    responsive = false,
    } = options || {};

    return inlineImages(el).then(() => {
    let clone = el.cloneNode(true);
    const {backgroundColor = 'transparent'} = options || {};
    clone.style.backgroundColor = backgroundColor;
    const {width, height} = getDimensions(el, clone, w, h);

    if (el.tagName !== 'svg') {
    if (el.getBBox) {
    clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate(.*?)/, ''));
    const svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
    svg.appendChild(clone);
    clone = svg;
    } else {
    console.error('Attempted to render non-SVG element', el);
    return;
    }
    }

    clone.setAttribute('version', '1.1');
    clone.setAttribute('viewBox', [left, top, width, height].join(' '));
    if (!clone.getAttribute('xmlns')) clone.setAttributeNS(xmlns, 'xmlns', 'http://www.w3.org/2000/svg');
    if (!clone.getAttribute('xmlns:xlink')) clone.setAttributeNS(xmlns, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');

    if (responsive) {
    clone.removeAttribute('width');
    clone.removeAttribute('height');
    clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
    } else {
    clone.setAttribute('width', width * scale);
    clone.setAttribute('height', height * scale);
    }

    Array.from(clone.querySelectorAll('foreignObject > *')).forEach(foreignObject => {
    if (!foreignObject.getAttribute('xmlns'))
    foreignObject.setAttributeNS(xmlns, 'xmlns', 'http://www.w3.org/1999/xhtml');
    });

    return inlineCss(el, options).then(css => {
    const style = document.createElement('style');
    style.setAttribute('type', 'text/css');
    style.innerHTML = `<![CDATA[ ${css} ]]>`;

    const defs = document.createElement('defs');
    defs.appendChild(style);
    clone.insertBefore(defs, clone.firstChild);

    const outer = document.createElement('div');
    outer.appendChild(clone);
    const src = outer.innerHTML.replace(/NSd+:href/gi, 'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');

    if (typeof done === 'function') done(src, width, height);
    else return {src, width, height};
    });
    });
    };

    out$.svgAsDataUri = (el, options, done) => {
    requireDomNode(el);
    const result = out$.prepareSvg(el, options)
    .then(({src, width, height}) => {
    const svgXml = `data:image/svg+xml;base64,${window.btoa(reEncode(doctype+src))}`;
    if (typeof done === 'function') {
    done(svgXml, width, height);
    }
    return svgXml;
    });
    return result;
    };

    out$.svgAsPngUri = (el, options, done) => {
    requireDomNode(el);
    const {
    encoderType = 'image/png',
    encoderOptions = 0.8,
    canvg
    } = options || {};

    const convertToPng = ({src, width, height}) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    const pixelRatio = window.devicePixelRatio || 1;

    canvas.width = width * pixelRatio;
    canvas.height = height * pixelRatio;
    canvas.style.width = `${canvas.width}px`;
    canvas.style.height = `${canvas.height}px`;
    context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);

    if (canvg) canvg(canvas, src);
    else context.drawImage(src, 0, 0);

    let png;
    try {
    png = canvas.toDataURL(encoderType, encoderOptions);
    } catch (e) {
    if ((typeof SecurityError !== 'undefined' && e instanceof SecurityError) || e.name === 'SecurityError') {
    console.error('Rendered SVG images cannot be downloaded in this browser.');
    return;
    } else throw e;
    }
    if (typeof done === 'function') done(png, canvas.width, canvas.height);
    return Promise.resolve(png);
    }

    if (canvg) return out$.prepareSvg(el, options).then(convertToPng);
    else return out$.svgAsDataUri(el, options).then(uri => {
    return new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(convertToPng({
    src: image,
    image.width,
    height: image.height
    }));
    image.onerror = () => {
    reject(`There was an error loading the data URI as an image on the following SVG ${window.atob(uri.slice(26))}Open the following link to see browser's diagnosis ${uri}`);
    }
    image.src = uri;
    })
    });
    };

    out$.download = (name, uri) => {
    if (navigator.msSaveOrOpenBlob) navigator.msSaveOrOpenBlob(uriToBlob(uri), name);
    else {
    const saveLink = document.createElement('a');
    if ('download' in saveLink) {
    saveLink.download = name;
    saveLink.style.display = 'none';
    document.body.appendChild(saveLink);
    try {
    const blob = uriToBlob(uri);
    const url = URL.createObjectURL(blob);
    saveLink.href = url;
    saveLink.onclick = () => requestAnimationFrame(() => URL.revokeObjectURL(url));
    } catch (e) {
    console.warn('This browser does not support object URLs. Falling back to string URL.');
    saveLink.href = uri;
    }
    saveLink.click();
    document.body.removeChild(saveLink);
    }
    else {
    window.open(uri, '_temp', 'menubar=no,toolbar=no,status=no');
    }
    }
    };

    out$.saveSvg = (el, name, options) => {
    requireDomNode(el);
    out$.svgAsDataUri(el, options || {}, uri => out$.download(name, uri));
    };

    out$.saveSvgAsPng = (el, name, options) => {
    requireDomNode(el);
    out$.svgAsPngUri(el, options || {}, uri => out$.download(name, uri));
    };
    })();

    看到这里还以为我写了一万多字的博客,太占内存和视觉距离了

    然后在自己的svg页面中获取到该svg,我的代码是这个样子的

    var canvas = $("#today_chartcontainer svg")[0];
    //调用方法转换即可,转换结果就是uri,
    svgAsPngUri(canvas, null, function(uri) {

    //这里的uri就是要下载到本地的图片地址,是不是很简单啊
    $("#tabpanel_items").append('<a class="downBtn"; href="'+uri+'" download="图片下载">下载图片</a>');
    });

  • 相关阅读:
    Haproxy 【转载】
    Nginx介绍
    Day 13 进程和线程
    运维第一课
    面试bb
    Day 12 字符串和正则表达式
    Day 11 文件和异常
    Day10 图形用户界面和游戏开发
    Day9 面向对象进阶
    day8 面向对象编程基础
  • 原文地址:https://www.cnblogs.com/shenwh/p/10528538.html
Copyright © 2011-2022 走看看