• 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>');
    });

  • 相关阅读:
    【HDOJ】2267 How Many People Can Survive
    【HDOJ】2268 How To Use The Car
    【HDOJ】2266 How Many Equations Can You Find
    【POJ】2278 DNA Sequence
    【ZOJ】3430 Detect the Virus
    【HDOJ】2896 病毒侵袭
    求奇数的乘积
    平方和与立方和
    求数列的和
    水仙花数
  • 原文地址:https://www.cnblogs.com/shenwh/p/10528538.html
Copyright © 2020-2023  润新知