Skip to content

Commit 7da6c2c

Browse files
committedMar 18, 2025·
feat: improve svg to image conversion
- Replace manual canvas drawing with html2canvas library - Add robust error handling for SVG conversion - Implement dynamic sizing and scaling for different image formats - Improve conversion quality with scale factor - Handle edge cases like ICO format conversion The new implementation provides more reliable and flexible SVG to image conversion with better error management and output quality.
1 parent e4348f2 commit 7da6c2c

File tree

2 files changed

+54
-39
lines changed

2 files changed

+54
-39
lines changed
 

‎app/svg-converter/page.tsx

+53-39
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { GridBackground } from '@/components/grid-background';
1414
import NextImage from 'next/image';
1515
import { MobileNav } from '@/components/mobile-nav';
1616
import ConverterFaq from '@/components/faq/converter-faq';
17+
import html2canvas from 'html2canvas';
1718

1819
export default function ConverterPage() {
1920
const [svgCode, setSvgCode] = useState<string>('<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400" fill="none">\n <rect width="400" height="400" rx="200" fill="#2563eb"/>\n <circle cx="200" cy="200" r="80" fill="black"/>\n <rect x="240" y="240" width="120" height="120" rx="20" fill="white" transform="rotate(-45 240 240)"/>\n</svg>');
@@ -87,51 +88,64 @@ export default function ConverterPage() {
8788
URL.revokeObjectURL(url);
8889
};
8990

90-
const convertSvgToImage = () => {
91+
const convertSvgToImage = async () => {
9192
if (!svgCode) return;
9293

93-
const parser = new DOMParser();
94-
const svgDoc = parser.parseFromString(svgCode, 'image/svg+xml');
95-
const svgElement = svgDoc.documentElement;
96-
97-
// Create a canvas element
98-
const canvas = document.createElement('canvas');
99-
const ctx = canvas.getContext('2d');
100-
if (!ctx) return;
101-
102-
// Set canvas dimensions based on SVG and scale
103-
const svgWidth = parseInt(svgElement.getAttribute('width') || '400');
104-
const svgHeight = parseInt(svgElement.getAttribute('height') || '400');
105-
106-
// For ICO format, we need to ensure the dimensions are appropriate
107-
if (format === 'ico') {
108-
canvas.width = icoSize;
109-
canvas.height = icoSize;
110-
} else {
111-
canvas.width = svgWidth * scale;
112-
canvas.height = svgHeight * scale;
113-
}
114-
115-
// Create an image from the SVG
116-
const img = document.createElement('img');
117-
const svgBlob = new Blob([svgCode], { type: 'image/svg+xml;charset=utf-8' });
118-
const url = URL.createObjectURL(svgBlob);
119-
120-
img.onload = () => {
121-
// Draw the image on the canvas
122-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
94+
try {
95+
// 创建一个临时的 div 来放置 SVG
96+
const tempDiv = document.createElement('div');
97+
tempDiv.innerHTML = svgCode;
98+
tempDiv.style.position = 'absolute';
99+
tempDiv.style.left = '-9999px';
100+
document.body.appendChild(tempDiv);
101+
102+
// 获取 SVG 元素
103+
const svgElement = tempDiv.querySelector('svg');
104+
if (!svgElement) {
105+
throw new Error('No SVG element found');
106+
}
107+
108+
// 设置 SVG 尺寸
109+
const svgWidth = parseInt(svgElement.getAttribute('width') || '400');
110+
const svgHeight = parseInt(svgElement.getAttribute('height') || '400');
123111

124-
// Convert canvas to data URL
112+
// 根据格式设置输出尺寸
113+
let outputWidth = svgWidth * scale;
114+
let outputHeight = svgHeight * scale;
115+
116+
if (format === 'ico') {
117+
outputWidth = icoSize;
118+
outputHeight = icoSize;
119+
}
120+
121+
// 使用 html2canvas 转换
122+
const canvas = await html2canvas(tempDiv, {
123+
width: outputWidth,
124+
height: outputHeight,
125+
scale: 2, // 提高输出质量
126+
useCORS: true,
127+
backgroundColor: null,
128+
logging: false,
129+
});
130+
131+
// 转换为 data URL
125132
const dataUrl = format === 'ico'
126-
? canvas.toDataURL('image/png') // ICO will be converted from PNG
133+
? canvas.toDataURL('image/png')
127134
: canvas.toDataURL(`image/${format}`);
128-
setDataUrl(dataUrl);
129135

130-
// Clean up
131-
URL.revokeObjectURL(url);
132-
};
133-
134-
img.src = url;
136+
setDataUrl(dataUrl);
137+
138+
// 清理临时元素
139+
document.body.removeChild(tempDiv);
140+
141+
} catch (error) {
142+
console.error('Error converting SVG:', error);
143+
toast({
144+
title: "转换失败",
145+
description: "无法将 SVG 转换为目标格式",
146+
variant: "destructive",
147+
});
148+
}
135149
};
136150

137151
const handleDownloadImage = async () => {

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"eslint": "8.49.0",
5555
"eslint-config-next": "13.5.1",
5656
"gray-matter": "^4.0.3",
57+
"html2canvas": "^1.4.1",
5758
"input-otp": "^1.2.4",
5859
"lucide-react": "^0.446.0",
5960
"next": "13.5.1",

0 commit comments

Comments
 (0)
Please sign in to comment.