SVG: 將 svg 導出成圖片


svg 導出成圖片會有很多限制, 外鏈的圖片, 樣式均會丟失,推薦使用 saveSvgAsPng.js 進行導出

 

以下是借鑒庫寫的導出函數:

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


function export2Base64Img(svgDom, MIMEType, option) {
    var serializer = new XMLSerializer();
    var source = serializer.serializeToString(svgDom);
    // 方式一: unescape(encodeURIComponent(txt))
    // var path = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(source)));
    // 方式二: decodeURIComponent(encodeURIComponent(txt))
    var path = "data:image/svg+xml;base64," + window.btoa(reEncode(source));
    var canvas = document.createElement("canvas"),
        context = canvas.getContext("2d"),
        img = new Image(),
        pixelRatio = window.devicePixelRatio || 1,
        _exportPath, handler
    option = option || {};

    canvas.width = parseFloat(svgDom.getAttribute('width')); //  * pixelRatio
    canvas.height = parseFloat(svgDom.getAttribute('height')); //  * pixelRatio 
    img.src = path;
    img.onload = function () {
        // 增加底色
        if (option.background) {
            context.beginPath();
            context.rect(0, 0, canvas.width, canvas.height);
            context.fillStyle = option.background;
            context.fill();
            context.closePath();
        }
        //
        context.drawImage(img, 0, 0);

        var marker = option.watermark || "";

        if (marker) {
            context.font = "18px 微軟雅黑";
            context.fillStyle = "rgba(12, 0, 70, 0.5)";

            var textWidth = context.measureText(marker).width,
                textHegith = 50,
                pk = 1.2,
                rotate = (option.rotation || -45) * Math.PI / 180,
                sinReg = Math.sin(rotate),
                cosReg = Math.cos(rotate),
                width = Math.abs(canvas.width * cosReg) + Math.abs(canvas.height * sinReg),
                height = Math.abs(canvas.height * cosReg) + Math.abs(canvas.width * sinReg);

                var xf = Math.ceil(width / textWidth * pk);
                var yf = Math.ceil(height / textHegith);

                context.rotate(rotate);

                for (var i = 0; i < yf; i++) {
                    for (var k = 0; k < xf; k++) {
                        context.fillText(marker, textWidth * k * pk - canvas.height * cosReg, textHegith * i)
                    }
                }
            }


        document.body.appendChild(canvas);
        _exportPath = canvas.toDataURL(MIMEType || 'image/png', 1)
        typeof handler === 'function' && handler(_exportPath)
        document.body.removeChild(canvas)
    }

    return new Promise(function (resolve, reject) {
        handler = resolve
    })
}
View Code

 

一個小例子;

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            /* margin: 0; */
            padding: 0;
        }

        body {
            background: #f3f3f3;
        }

        svg .pinkCircle {
            fill: rgba(145, 92, 101, 0.671);
            stroke: #f60;
            stroke-width: 5;
        }

        svg .blackRect {
            fill: black;
            stroke: #ccc;
            stroke-width: 2;
        }
    </style>
</head>

<body>
    <p>Svg Start</p>
    <svg width="800" height="600" id="svg">
        <!-- 圓形: cx, cy, r -->
        <circle cx="80" cy="40" r="15" class="pinkCircle"></circle>
        <circle cx="180" cy="40" r="15" class="pinkCircle"></circle>
        <circle cx="100" cy="100" r="25" class="pinkCircle"></circle>
        <!-- fill="pink" stroke="#f60" stroke-width="5" 也可以直接設置屬性的方式 -->

        <!-- 矩形: x, y, rx, ry -->
        <rect x="80" y="50" class="blackRect" width="60" height="42" rx="8" ry="20"></rect>
        <!-- 通過 class 定義的樣式只能定義填充和邊框, 無法定義 svg內幾何圖形的大小或位置 -->
        <!-- rect 標簽的 坐標位置用 x, y來表示, 而且額外可以定義 rx, ry; 表示圓角在 水平/垂直方向上的曲率半徑 -->

        <!-- 橢圓 -->
        <ellipse cx="120" cy="120" rx="60" ry="20"></ellipse>
        <!-- cx: 橢圓中心點的水平位置
             cy: 橢圓中心點的垂直位置
             rx: 橢圓水平方向上的半徑
             ry: 橢圓垂直方向上的半徑
            ???? 橢圓什么時候會有中心點? 而且橢圓是有兩個中心點
            橢圓的繪制方法:
            https://jingyan.baidu.com/article/4ae03de30f5bfc3efe9e6b7f.html -->

        <!-- 多邊形 -->
        <polygon points="300,300 350,350 420,300"></polygon>
        <!-- points 一系列的 x 和 y 坐標點, 描述了多邊形各個角的位置
            多邊形是一個封閉的形狀, 如果制定了幾個點, 那么這幾個點自動會與第一個點相連 -->

        <!---->
        <line x1="0" y1="0" x2="700" y2="700" stroke="black" stoke-width="2"></line>
        <!-- x1: 起點的水平位置
            y1: 起點的垂直位置
            x2: 終點的水平位置
            y2: 終點的垂直位置 -->

        <!-- 路徑 -->
        <!-- d: 告知瀏覽器如何繪圖 -->
        <!-- M: moveto 的縮寫, 目標是一個假想的點 
            q 告訴光標要繪制一條貝塞爾曲線, 什么是貝塞爾曲線:? 可以從B站中去搜索查看, 一段幾十分鍾的講解, 很復雜, 到處都在用的貝塞爾曲線 -->
        <path d="M 100 500 L 300 500 L 200 300 z" fill="orange" stroke="black" stroke-width="3" />

        <!-- 文本 -->
        <text x="120" y="520" font-size="30" fill="blue" stroke="#ccc" stroke-width="2" font-family="Comic Sans MS"
            text-anchor="start" opacity="0.5">hello Svg

            <a xlink:href="http://www.baidu.com" target="_blank">這是一個鏈接</a>
        </text>
        <!-- svg 的基本文本屬性與 HTML 同名, font-size, font-family, font-weight, font-style
        除了顏色除外, 這些都是。 svg文本和圖形一樣, 顏色都叫"填充", 同樣, 也可以在文本周圍添加 "筆觸" -->
        <!-- x 和 y 屬性指定了文本錨點的位置, 默認情況下, 錨點標識了文本的開始並與文本基線垂直對齊
            設置 text-anchor 可以改變錨點的位置, 值有 start, center, end
            svg 沒有辦法簡單繪制一個文本塊並讓它們自動換行
        -->

        <!--  svg 繪圖中, 還可以使用 opacity 設置透明度等;  -->
    </svg>

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

        function export2Base64Img(svgDom, MIMEType, option) {
            var serializer = new XMLSerializer();
            var source = serializer.serializeToString(svgDom);
            // 方式一: unescape(encodeURIComponent(txt))
            // var path = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(source)));
            // 方式二: decodeURIComponent(encodeURIComponent(txt))
            var path = "data:image/svg+xml;base64," + window.btoa(reEncode(source));
            var canvas = document.createElement("canvas"),
                context = canvas.getContext("2d"),
                img = new Image(),
                pixelRatio = window.devicePixelRatio || 1,
                _exportPath, handler
            option = option || {};

            canvas.width = parseFloat(svgDom.getAttribute('width')); //  * pixelRatio
            canvas.height = parseFloat(svgDom.getAttribute('height')); //  * pixelRatio 
            img.src = path;
            img.onload = function () {
                // 增加底色
                if (option.background) {
                    context.beginPath();
                    context.rect(0, 0, canvas.width, canvas.height);
                    context.fillStyle = option.background;
                    context.fill();
                    context.closePath();
                }
                //
                context.drawImage(img, 0, 0);

                var marker = option.watermark || "";

                if (marker) {
                    context.font = "18px 微軟雅黑";
                    context.fillStyle = "rgba(12, 0, 70, 0.5)";

                    var textWidth = context.measureText(marker).width,
                        textHegith = 50,
                        pk = 1.2,
                        rotate = (option.rotation || -45) * Math.PI / 180,
                        sinReg = Math.sin(rotate),
                        cosReg = Math.cos(rotate),
                        width = Math.abs(canvas.width * cosReg) + Math.abs(canvas.height * sinReg),
                        height = Math.abs(canvas.height * cosReg) + Math.abs(canvas.width * sinReg);

                    var xf = Math.ceil(width / textWidth * pk);
                    var yf = Math.ceil(height / textHegith);

                    context.rotate(rotate);

                    for (var i = 0; i < yf; i++) {
                        for (var k = 0; k < xf; k++) {
                            context.fillText(marker, textWidth * k * pk - canvas.height * cosReg, textHegith * i)
                        }
                    }
                }


                document.body.appendChild(canvas);
                _exportPath = canvas.toDataURL(MIMEType || 'image/png', 1)
                typeof handler === 'function' && handler(_exportPath)
                document.body.removeChild(canvas)
            }

            return new Promise(function (resolve, reject) {
                handler = resolve
            })
        }


        var svg = document.getElementById("svg");
        svg.onclick = function () {
            var img = export2Base64Img(svg, null, {
                watermark: 'copyright reserved 2020 版權所有',
                background: '#fff'
            });
            img.then(function (base64src) {
                // console.log('路徑:::', base64src)
                downLoad(base64src, '圖.png')
            })
        }
        function downLoad(url, fileName) {
            var oA = document.createElement("a");
            oA.download = fileName || '';
            oA.style.display = 'none'
            oA.href = url;
            document.body.appendChild(oA);
            oA.click();
            oA.remove();
        }

    </script>
</body>

</html>
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM