update at 2026-02-11 19:35:34

This commit is contained in:
douboer
2026-02-11 19:35:34 +08:00
parent cc6c9c8a99
commit 63f11b0067
6 changed files with 23 additions and 10 deletions

View File

@@ -1 +1 @@
import{_ as v}from"./index-_4VXTXt_.js";function p(t,e,n="text/plain"){const o=new Blob([t],{type:n});s(o,e)}function s(t,e){const n=URL.createObjectURL(t),o=document.createElement("a");o.href=n,o.download=e,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function P(t,e){p(t,e,"image/svg+xml")}async function F(t,e="font2svg-export.zip"){const n=(await v(async()=>{const{default:r}=await import("./jszip.min-D7KnG0-e.js").then(i=>i.j);return{default:r}},[])).default,o=new n;for(const r of t)o.file(r.name,r.content);const a=await o.generateAsync({type:"blob"});s(a,e)}function d(t){if(!t)return null;const e=t.match(/-?\d+(\.\d+)?/);if(!e)return null;const n=Number(e[0]);return Number.isFinite(n)?n:null}function x(t){const n=new DOMParser().parseFromString(t,"image/svg+xml").documentElement,o=d(n.getAttribute("width")),a=d(n.getAttribute("height"));if(o&&a)return{width:o,height:a};const r=n.getAttribute("viewBox");if(r){const i=r.trim().split(/[\s,]+/).map(Number);if(i.length===4&&Number.isFinite(i[2])&&Number.isFinite(i[3]))return{width:Math.max(1,i[2]),height:Math.max(1,i[3])}}return{width:1024,height:1024}}async function _(t,e){const n=x(t),o=e?.scale??1,a=Math.max(1,Math.round((e?.width??n.width)*o)),r=Math.max(1,Math.round((e?.height??n.height)*o)),i=document.createElement("canvas");i.width=a,i.height=r;const l=i.getContext("2d");if(!l)throw new Error("无法创建 PNG 画布");e?.backgroundColor?(l.fillStyle=e.backgroundColor,l.fillRect(0,0,a,r)):l.clearRect(0,0,a,r);const f=new Blob([t],{type:"image/svg+xml;charset=utf-8"}),u=URL.createObjectURL(f);try{const c=new Image;await new Promise((w,b)=>{c.onload=()=>w(),c.onerror=()=>b(new Error("SVG 转 PNG 失败")),c.src=u}),l.drawImage(c,0,0,a,r)}finally{URL.revokeObjectURL(u)}const g=await new Promise(c=>{i.toBlob(c,"image/png")});if(!g)throw new Error("PNG 编码失败");return g}async function R(t,e,n){const o=await _(t,n);s(o,e)}function m(t){return t.replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g,"_").substring(0,200)}function h(t,e){const n=m(Array.from(t).slice(0,8).join(""));return`${m(e.substring(0,20))}_${n}`}function B(t,e){return`${h(t,e)}.svg`}function L(t,e){return`${h(t,e)}.png`}export{_ as convertSvgToPngBlob,s as downloadBlob,F as downloadMultipleFiles,R as downloadPngFromSvg,P as downloadSvg,p as downloadText,L as generatePngFilename,B as generateSvgFilename,m as sanitizeFilename}; import{_ as v}from"./index-h1zOmI1c.js";function p(t,e,n="text/plain"){const o=new Blob([t],{type:n});s(o,e)}function s(t,e){const n=URL.createObjectURL(t),o=document.createElement("a");o.href=n,o.download=e,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function P(t,e){p(t,e,"image/svg+xml")}async function F(t,e="font2svg-export.zip"){const n=(await v(async()=>{const{default:r}=await import("./jszip.min-D7KnG0-e.js").then(i=>i.j);return{default:r}},[])).default,o=new n;for(const r of t)o.file(r.name,r.content);const a=await o.generateAsync({type:"blob"});s(a,e)}function d(t){if(!t)return null;const e=t.match(/-?\d+(\.\d+)?/);if(!e)return null;const n=Number(e[0]);return Number.isFinite(n)?n:null}function x(t){const n=new DOMParser().parseFromString(t,"image/svg+xml").documentElement,o=d(n.getAttribute("width")),a=d(n.getAttribute("height"));if(o&&a)return{width:o,height:a};const r=n.getAttribute("viewBox");if(r){const i=r.trim().split(/[\s,]+/).map(Number);if(i.length===4&&Number.isFinite(i[2])&&Number.isFinite(i[3]))return{width:Math.max(1,i[2]),height:Math.max(1,i[3])}}return{width:1024,height:1024}}async function _(t,e){const n=x(t),o=e?.scale??1,a=Math.max(1,Math.round((e?.width??n.width)*o)),r=Math.max(1,Math.round((e?.height??n.height)*o)),i=document.createElement("canvas");i.width=a,i.height=r;const l=i.getContext("2d");if(!l)throw new Error("无法创建 PNG 画布");e?.backgroundColor?(l.fillStyle=e.backgroundColor,l.fillRect(0,0,a,r)):l.clearRect(0,0,a,r);const f=new Blob([t],{type:"image/svg+xml;charset=utf-8"}),u=URL.createObjectURL(f);try{const c=new Image;await new Promise((w,b)=>{c.onload=()=>w(),c.onerror=()=>b(new Error("SVG 转 PNG 失败")),c.src=u}),l.drawImage(c,0,0,a,r)}finally{URL.revokeObjectURL(u)}const g=await new Promise(c=>{i.toBlob(c,"image/png")});if(!g)throw new Error("PNG 编码失败");return g}async function R(t,e,n){const o=await _(t,n);s(o,e)}function m(t){return t.replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g,"_").substring(0,200)}function h(t,e){const n=m(Array.from(t).slice(0,8).join(""));return`${m(e.substring(0,20))}_${n}`}function B(t,e){return`${h(t,e)}.svg`}function L(t,e){return`${h(t,e)}.png`}export{_ as convertSvgToPngBlob,s as downloadBlob,F as downloadMultipleFiles,R as downloadPngFromSvg,P as downloadSvg,p as downloadText,L as generatePngFilename,B as generateSvgFilename,m as sanitizeFilename};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,8 +8,8 @@
<link rel="apple-touch-icon" href="/favicon_new.png" /> <link rel="apple-touch-icon" href="/favicon_new.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Font2SVG - 字体转SVG工具</title> <title>Font2SVG - 字体转SVG工具</title>
<script type="module" crossorigin src="/assets/index-_4VXTXt_.js"></script> <script type="module" crossorigin src="/assets/index-h1zOmI1c.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Dl9L1Xmr.css"> <link rel="stylesheet" crossorigin href="/assets/index-DQCk60Dv.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -50,6 +50,7 @@ let previewGenerateTimer: ReturnType<typeof setTimeout> | null = null
let previewGenerationToken = 0 let previewGenerationToken = 0
let hasTriggeredInitialGenerate = false let hasTriggeredInitialGenerate = false
let previewLazyLoadObserver: IntersectionObserver | null = null let previewLazyLoadObserver: IntersectionObserver | null = null
let batchOwnerToken: number | null = null
const previewApiCache = new Map<string, PreviewApiCacheItem>() const previewApiCache = new Map<string, PreviewApiCacheItem>()
@@ -375,16 +376,26 @@ async function generatePreviewBatch(
} }
async function loadNextPreviewBatch(generationToken: number) { async function loadNextPreviewBatch(generationToken: number) {
if (isBatchGenerating.value || isStaleGeneration(generationToken)) { if (isStaleGeneration(generationToken)) {
return return
} }
if (isBatchGenerating.value) {
if (batchOwnerToken === generationToken) {
return
}
// 旧批次仍在收尾时,允许新批次接管
isBatchGenerating.value = false
batchOwnerToken = null
}
const startIndex = processedFontCount.value const startIndex = processedFontCount.value
if (startIndex >= activePreviewFonts.value.length) { if (startIndex >= activePreviewFonts.value.length) {
return return
} }
isBatchGenerating.value = true isBatchGenerating.value = true
batchOwnerToken = generationToken
try { try {
const batchItems = await generatePreviewBatch( const batchItems = await generatePreviewBatch(
@@ -418,8 +429,9 @@ async function loadNextPreviewBatch(generationToken: number) {
console.error('Failed to load preview batch:', error) console.error('Failed to load preview batch:', error)
previewErrorMessage.value = `预览生成失败:${error instanceof Error ? error.message : String(error)}` previewErrorMessage.value = `预览生成失败:${error instanceof Error ? error.message : String(error)}`
} finally { } finally {
if (!isStaleGeneration(generationToken)) { if (batchOwnerToken === generationToken) {
isBatchGenerating.value = false isBatchGenerating.value = false
batchOwnerToken = null
} }
} }
} }
@@ -517,6 +529,7 @@ onBeforeUnmount(() => {
} }
disconnectPreviewLazyLoadObserver() disconnectPreviewLazyLoadObserver()
previewGenerationToken += 1 previewGenerationToken += 1
batchOwnerToken = null
}) })
function toggleSelectItem(item: PreviewItemType) { function toggleSelectItem(item: PreviewItemType) {