iconfont-svg-copy

适用于 iconfont 快速复制 SVG 代码

目前为 2023-04-18 提交的版本。查看 最新版本

// ==UserScript==
// @name         iconfont-svg-copy
// @namespace    http://tampermonkey.net/
// @version      0.8
// @description  适用于 iconfont 快速复制 SVG 代码
// @author       [email protected]
// @match        https://www.iconfont.cn/*
// @require      https://cdn.bootcdn.net/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js
// @icon         http://img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg
// @grant        none
// @note         1. 进一步修复图标丢失的BUG
// @license      GPL-3.0-only
// ==/UserScript==


(function() {
    "use strict";


    // 全局常量
    const DELAY = 500;  // 给网页预留的加载时间
    const XML = new XMLSerializer();


    /**
     * 异步的等待 delay_ms 毫秒
     * @param {number} delay_ms 
     * @returns {Promise<void>}
     */
    function sleep(delay_ms) {
        return new Promise(
            resolve => setTimeout(resolve, delay_ms)
        );
    }


    /**
     * 使用xhr异步GET请求目标url,返回响应体blob
     * @param {string} url 
     * @returns {Promise<Blob>} blob
     */
    async function xhrGetBlob(url) {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.responseType = "blob";
        
        return new Promise((resolve, reject) => {
            xhr.onload = () => {
                const code = xhr.status;
                if (code >= 200 && code <= 299) {
                    resolve(xhr.response);
                }
                else {
                    reject(new Error(`Network Error: ${code}`));
                }
            }
            xhr.send();
        });
    }


    /**
     * 加载CDN脚本
     * @param {string} url 
     */
    async function loadWebScript(url) {
        try {
            // xhr+eval方式
            Function(
                await (await xhrGetBlob(url)).text()
            )();
        } catch(e) {
            console.error(e);
            // 嵌入<script>方式
            const script = document.createElement("script");
            script.src = url;
            document.body.append(script);
        }
    }


    /**
     * 元素选择器
     * @param {string} selector 选择器
     * @returns {HTMLElement | null} 元素
     */
    function $(selector) {
        const self = this?.querySelectorAll ? this : document;
        return self.querySelector(selector);
    }


    /**
     * 安全元素选择器,直到元素存在时才返回元素列表,最多等待5秒
     * @param {string} selector 选择器
     * @returns {Promise<Array<HTMLElement>>} 元素列表
     */
    async function $$(selector) {
        const self = this?.querySelectorAll ? this : document;

        for (let i = 0; i < 10; i++) {
            let elems = [...self.querySelectorAll(selector)];
            if (elems.length > 0) {
                return elems;
            }
            await sleep(500);
        }
        throw Error(`"${selector}" not found in 5s`);
    }


    function addStyle() {
        const id = "iconfont-svg-copy-style";
        if ($(`#${id}`)) return;

        const style = document.createElement("style");
        style.id = id;
        style.innerHTML = `
            .block-icon-list li:hover div.icon-cover {
                display: grid;
                grid-template-columns: auto auto;
            }

            .block-icon-list li .icon-cover span.cover-item-line {
                height: auto;
                line-height: 50px;
            }

            .icon-fuzhidaima:before {
                font-size: 24px;
            }

            .copy-icon {
                border: none !important;
                margin: 0 1.25em !important;
                margin: 0 0 0 10px !important;
            }

            .copy-container {
                margin: 8px 16px !important;
                padding: 0 !important;
                font-size: 14px !important;
                
            }
            
            .copy-popup {
                top: 60px;
                padding: 4px 10px !important;
                height: 44px !important;
                font-size: 12px !important;
                width: fit-content !important;
                align-content: center;
                box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset !important;
            }

            .swal2-popup {
                border-radius:0 !important;    
            }
        `;
        document.head.append(style);
    }


    async function showCopySuccess() {
        Swal.fire({
            text: "复制成功,可以粘贴咯~",
            toast: true,
            timer: 2000,
            showConfirmButton: false,
            icon: "success",
            position: "top",
            customClass: {
                popup: "copy-popup",
                htmlContainer: "copy-container",
                icon: "copy-icon"
            }
        });
        await sleep(2000);
    }


    /**
     * 当点击复制图标时复制 svg 到剪贴板
     * @param {PointerEvent} event 
     */
    async function copy(event) {
        // 取得svg
        const card = event.target.closest("li");
        const svg = card.querySelector("svg");

        // 设置大小
        svg.setAttribute("width", "20");
        svg.setAttribute("height", "20");

        // 序列化
        const svg_str = XML.serializeToString(svg);

        // 复制到剪贴板
        try {
            navigator.clipboard.writeText(svg_str);
        } catch (err) {
            console.error(err);
            console.log(svg_str);
        }

        // 提示复制成功
        showCopySuccess();
    }


    /**
     * 导入 sweet alert 2
     * @returns {Promise<void>}
     */
    function importSweetAlert() {
        if (!window.Swal) {
            return loadWebScript(
                "https://cdn.bootcdn.net/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js"
            );
        }
    }

    async function addCopyIcon() {
        // 获取卡片
        const cards = await $$(".block-icon-list > li");
        
        // 制作按钮元素模板
        const template = document.createElement("span");
        template.title = "复制SVG";
        template.classList.add(
            "cover-item", "iconfont", "cover-item-line", "icon-fuzhidaima", "icon-copy"
        );

        cards.forEach(card => {
            // 添加复制图标
            const icon_copy = template.cloneNode();
            // 增加复制功能
            icon_copy.addEventListener("click", copy, true);
            card.querySelector(".icon-cover").append(icon_copy);
        });
    }


    async function mainTask() {
        console.log(Date.now());
        
        // 如果已经存在按钮,退出主函数
        if ($(".icon-cover span.icon-copy")) return;
        console.log("正在建造 [复制SVG] 图标...");

        await addCopyIcon();
        addStyle();

        // 导入 sweet alert
        try {
            await importSweetAlert();
        } catch(err) {
            console.error(err);
            console.log("sweet alert 导入失败,没有提示弹窗了");
        }
        console.log("[复制SVG] 图标 建造完成");
    }


    /**
     * 
     * @param {Array<MutationRecord>} mutations 
     */
    function onIconsChanged(mutations) {
        if (mutations.length <= 3) return;
        mainTask();
    }


    async function monitorIconsChanging() {
        const icons_box = (await $$(".main .block-icon-list")).at(-1);
        const observer = new MutationObserver(onIconsChanged)
        observer.observe(
            icons_box, {
                subtree: true,
                attributes: true
            }
        );
    }


    function main() {
        mainTask();
        monitorIconsChanging();
        window.addEventListener("popstate", mainTask, true);
    }
    

    setTimeout(main, DELAY);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址