[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG转PNG - 在线转换工具</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
neutral: '#1F2937',
light: '#F9FAFB',
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.shadow-soft {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.transition-all-300 {
transition: all 300ms ease-in-out;
}
}
</style>
</head>
<body class="font-inter bg-gray-50 min-h-screen flex flex-col">
<!-- 顶部导航 -->
<header class="bg-white shadow-sm sticky top-0 z-50 transition-all duration-300">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fa fa-exchange text-primary text-2xl"></i>
<h1 class="text-xl md:text-2xl font-bold text-neutral">SVG 转 PNG</h1>
</div>
<div class="hidden md:flex items-center space-x-6">
<a href="#" class="text-gray-600 hover:text-primary transition-colors">首页</a>
<a href="#" class="text-gray-600 hover:text-primary transition-colors">使用指南</a>
<a href="#" class="text-gray-600 hover:text-primary transition-colors">关于我们</a>
</div>
<button class="md:hidden text-gray-600 focus:outline-none">
<i class="fa fa-bars text-xl"></i>
</button>
</div>
</header>
<!-- 主要内容 -->
<main class="flex-grow container mx-auto px-4 py-8 md:py-16">
<!-- 介绍部分 -->
<section class="text-center mb-12">
<h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-neutral mb-4">简单高效的 SVG 转 PNG 工具</h2>
<p class="text-gray-600 max-w-2xl mx-auto text-lg">上传您的 SVG 文件,一键转换为高质量 PNG 图像,支持自定义尺寸和下载设置。</p>
</section>
<!-- 转换区域 -->
<section class="max-w-5xl mx-auto">
<div class="bg-white rounded-xl shadow-soft p-6 md:p-8 mb-8">
<!-- 上传区域 -->
<div id="upload-area" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-primary transition-all-300 cursor-pointer">
<input type="file" id="svg-file" accept="image/svg+xml" class="hidden">
<div class="space-y-4">
<i class="fa fa-cloud-upload text-5xl text-gray-400"></i>
<h3 class="text-xl font-medium text-neutral">拖放 SVG 文件到此处,或点击上传</h3>
<p class="text-gray-500">支持标准 SVG 格式文件</p>
<button id="browse-btn" class="bg-primary hover:bg-primary/90 text-white py-2 px-6 rounded-lg transition-all-300 font-medium">
<i class="fa fa-folder-open mr-2"></i>浏览文件
</button>
</div>
</div>
<!-- 预览和设置区域 (默认隐藏) -->
<div id="preview-section" class="mt-8 hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- SVG 预览 -->
<div>
<h3 class="text-lg font-medium text-neutral mb-4">SVG 预览</h3>
<div id="svg-preview-container" class="border border-gray-200 rounded-lg p-4 bg-gray-50 min-h-[200px] flex items-center justify-center">
<div id="svg-preview" class="max-w-full max-h-[300px]"></div>
</div>
</div>
<!-- 转换设置 -->
<div>
<h3 class="text-lg font-medium text-neutral mb-4">转换设置</h3>
<div class="space-y-4">
<div>
<label class="block text-gray-700 mb-1">宽度 (像素)</label>
<input type="number" id="png-width" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary" value="800">
</div>
<div>
<label class="block text-gray-700 mb-1">高度 (像素)</label>
<input type="number" id="png-height" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary" value="600">
</div>
<div>
<label class="block text-gray-700 mb-1">背景颜色</label>
<div class="flex space-x-2">
<input type="color" id="bg-color" class="w-10 h-10 p-0 border border-gray-300 rounded" value="#ffffff">
<input type="text" id="bg-color-text" class="flex-grow px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary" value="#FFFFFF">
</div>
</div>
<div>
<label class="flex items-center">
<input type="checkbox" id="preserve-aspect" class="mr-2" checked>
<span class="text-gray-700">保持纵横比</span>
</label>
</div>
</div>
</div>
</div>
<!-- 转换和下载按钮 -->
<div class="mt-8 flex justify-center">
<button id="convert-btn" class="bg-secondary hover:bg-secondary/90 text-white py-3 px-8 rounded-lg transition-all-300 font-medium text-lg mr-4 flex items-center">
<i class="fa fa-magic mr-2"></i>转换为 PNG
</button>
<button id="download-btn" class="bg-primary hover:bg-primary/90 text-white py-3 px-8 rounded-lg transition-all-300 font-medium text-lg hidden flex items-center">
<i class="fa fa-download mr-2"></i>下载 PNG
</button>
</div>
<!-- PNG 预览 (默认隐藏) -->
<div id="png-preview-section" class="mt-8 hidden">
<h3 class="text-lg font-medium text-neutral mb-4">PNG 预览</h3>
<div class="border border-gray-200 rounded-lg p-4 bg-gray-50 min-h-[200px] flex items-center justify-center">
<img id="png-preview" class="max-w-full max-h-[300px]" alt="PNG 预览图">
</div>
</div>
</div>
</div>
<!-- 转换历史 (默认隐藏) -->
<div id="history-section" class="bg-white rounded-xl shadow-soft p-6 md:p-8 hidden">
<h3 class="text-xl font-medium text-neutral mb-4 flex items-center">
<i class="fa fa-history mr-2 text-primary"></i>转换历史
</h3>
<div id="conversion-history" class="space-y-3">
<!-- 历史记录将在这里动态添加 -->
</div>
</div>
</section>
<!-- 功能特点 -->
<section class="mt-16">
<h2 class="text-2xl font-bold text-center text-neutral mb-10">为什么选择我们的转换工具</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto">
<div class="bg-white p-6 rounded-xl shadow-soft hover:shadow-lg transition-all-300">
<div class="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
<i class="fa fa-bolt text-primary text-xl"></i>
</div>
<h3 class="text-lg font-medium text-neutral mb-2">快速转换</h3>
<p class="text-gray-600">本地处理您的文件,无需上传到服务器,转换速度快且保护隐私</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-soft hover:shadow-lg transition-all-300">
<div class="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
<i class="fa fa-sliders text-primary text-xl"></i>
</div>
<h3 class="text-lg font-medium text-neutral mb-2">自定义设置</h3>
<p class="text-gray-600">支持调整输出尺寸、背景颜色等参数,满足您的个性化需求</p>
</div>
<div class="bg-white p-6 rounded-xl shadow-soft hover:shadow-lg transition-all-300">
<div class="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
<i class="fa fa-shield text-primary text-xl"></i>
</div>
<h3 class="text-lg font-medium text-neutral mb-2">安全可靠</h3>
<p class="text-gray-600">所有转换都在您的浏览器中完成,不会上传您的文件,保障数据安全</p>
</div>
</div>
</section>
<!-- 使用统计 -->
<section class="mt-16 bg-white rounded-xl shadow-soft p-6 md:p-8 max-w-5xl mx-auto">
<h2 class="text-2xl font-bold text-neutral mb-6">使用统计</h2>
<div class="h-[300px]">
<canvas id="stats-chart"></canvas>
</div>
</section>
</main>
<!-- 页脚 -->
<footer class="bg-neutral text-white py-12">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<div class="flex items-center space-x-2 mb-4">
<i class="fa fa-exchange text-primary text-2xl"></i>
<h3 class="text-xl font-bold">SVG 转 PNG</h3>
</div>
<p class="text-gray-400">一款简单高效的在线 SVG 转 PNG 转换工具,帮助您轻松处理图像格式转换需求。</p>
</div>
<div>
<h4 class="text-lg font-medium mb-4">快速链接</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#" class="hover:text-primary transition-colors">首页</a></li>
<li><a href="#" class="hover:text-primary transition-colors">使用指南</a></li>
<li><a href="#" class="hover:text-primary transition-colors">常见问题</a></li>
<li><a href="#" class="hover:text-primary transition-colors">联系我们</a></li>
</ul>
</div>
<div>
<h4 class="text-lg font-medium mb-4">关注我们</h4>
<div class="flex space-x-4">
<a href="#" class="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center hover:bg-primary transition-colors">
<i class="fa fa-twitter"></i>
</a>
<a href="#" class="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center hover:bg-primary transition-colors">
<i class="fa fa-github"></i>
</a>
<a href="#" class="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center hover:bg-primary transition-colors">
<i class="fa fa-linkedin"></i>
</a>
</div>
<p class="mt-4 text-gray-400">订阅我们的更新</p>
<div class="mt-2 flex">
<input type="email" placeholder="您的邮箱地址" class="px-3 py-2 rounded-l-lg focus:outline-none text-gray-800 flex-grow">
<button class="bg-primary hover:bg-primary/90 px-4 py-2 rounded-r-lg transition-colors">
<i class="fa fa-paper-plane"></i>
</button>
</div>
</div>
</div>
<div class="mt-8 pt-8 border-t border-gray-700 text-center text-gray-500">
<p>© 2023 SVG 转 PNG 工具 - 保留所有权利</p>
</div>
</div>
</footer>
<!-- 通知提示 -->
<div id="notification" class="fixed bottom-4 right-4 bg-neutral text-white px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all-300 flex items-center">
<i id="notification-icon" class="fa fa-check-circle mr-2"></i>
<span id="notification-message"></span>
</div>
<script>
// DOM 元素
const uploadArea = document.getElementById('upload-area');
const svgFileInput = document.getElementById('svg-file');
const browseBtn = document.getElementById('browse-btn');
const previewSection = document.getElementById('preview-section');
const svgPreview = document.getElementById('svg-preview');
const pngWidthInput = document.getElementById('png-width');
const pngHeightInput = document.getElementById('png-height');
const bgColorInput = document.getElementById('bg-color');
const bgColorTextInput = document.getElementById('bg-color-text');
const preserveAspectCheckbox = document.getElementById('preserve-aspect');
const convertBtn = document.getElementById('convert-btn');
const downloadBtn = document.getElementById('download-btn');
const pngPreviewSection = document.getElementById('png-preview-section');
const pngPreview = document.getElementById('png-preview');
const historySection = document.getElementById('history-section');
const conversionHistory = document.getElementById('conversion-history');
const notification = document.getElementById('notification');
const notificationIcon = document.getElementById('notification-icon');
const notificationMessage = document.getElementById('notification-message');
// 全局变量
let currentSvgData = null;
let currentPngData = null;
let svgOriginalWidth = 0;
let svgOriginalHeight = 0;
// 初始化统计图表
function initStatsChart() {
const ctx = document.getElementById('stats-chart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
datasets: [{
label: '转换次数',
data: [120, 190, 300, 500, 450, 600, 550],
backgroundColor: 'rgba(59, 130, 246, 0.7)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
// 显示通知
function showNotification(message, isSuccess = true) {
notificationMessage.textContent = message;
notificationIcon.className = isSuccess ? 'fa fa-check-circle mr-2' : 'fa fa-exclamation-circle mr-2 text-red-500';
notification.classList.remove('translate-y-20', 'opacity-0');
notification.classList.add('translate-y-0', 'opacity-100');
setTimeout(() => {
notification.classList.remove('translate-y-0', 'opacity-100');
notification.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
// 处理文件上传
function handleFileUpload(file) {
if (!file.type.match('image/svg+xml')) {
showNotification('请上传SVG格式的文件', false);
return;
}
const reader = new FileReader();
reader.onload = function(e) {
currentSvgData = e.target.result;
displaySvgPreview(currentSvgData);
previewSection.classList.remove('hidden');
downloadBtn.classList.add('hidden');
pngPreviewSection.classList.add('hidden');
// 显示历史记录区域
historySection.classList.remove('hidden');
showNotification('SVG文件已加载');
};
reader.readAsDataURL(file);
}
// 显示SVG预览
function displaySvgPreview(svgData) {
svgPreview.innerHTML = '';
const img = new Image();
img.onload = function() {
// 获取原始尺寸
svgOriginalWidth = img.width;
svgOriginalHeight = img.height;
// 设置默认尺寸
pngWidthInput.value = svgOriginalWidth;
pngHeightInput.value = svgOriginalHeight;
svgPreview.appendChild(img);
};
img.src = svgData;
}
// 转换SVG为PNG
function convertSvgToPng() {
const width = parseInt(pngWidthInput.value) || 800;
const height = parseInt(pngHeightInput.value) || 600;
const bgColor = bgColorInput.value;
// 创建一个临时的SVG元素
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute('width', width);
svg.setAttribute('height', height);
svg.style.backgroundColor = bgColor;
// 解析SVG数据
const parser = new DOMParser();
const svgDoc = parser.parseFromString(atob(currentSvgData.split(',')[1]), "image/svg+xml");
const svgElement = svgDoc.querySelector('svg');
// 如果SVG有viewBox,我们使用它来保持比例
if (svgElement.hasAttribute('viewBox')) {
const viewBox = svgElement.getAttribute('viewBox').split(' ').map(Number);
const svgWidth = viewBox[2] - viewBox[0];
const svgHeight = viewBox[3] - viewBox[1];
// 创建一个group元素来应用变换
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
// 计算缩放比例
let scaleX = width / svgWidth;
let scaleY = height / svgHeight;
let scale = Math.min(scaleX, scaleY);
// 如果不保持比例,则使用单独的缩放
if (!preserveAspectCheckbox.checked) {
scale = 1;
scaleX = width / svgWidth;
scaleY = height / svgHeight;
} else {
scaleX = scale;
scaleY = scale;
}
// 应用缩放和平移使图像居中
const translateX = (width - svgWidth * scaleX) / 2;
const translateY = (height - svgHeight * scaleY) / 2;
g.setAttribute('transform', `translate(${translateX}, ${translateY}) scale(${scaleX}, ${scaleY})`);
// 将原始SVG的内容添加到group中
while (svgElement.firstChild) {
g.appendChild(svgElement.firstChild);
}
svg.appendChild(g);
} else {
// 如果没有viewBox,直接使用原始尺寸
svgElement.setAttribute('width', width);
svgElement.setAttribute('height', height);
svg.appendChild(svgElement);
}
// 创建canvas元素
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 将SVG转换为数据URL
const svgDataUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(new XMLSerializer().serializeToString(svg));
// 绘制到canvas
const img = new Image();
img.onload = function() {
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, width, height);
ctx.drawImage(img, 0, 0);
// 获取PNG数据URL
currentPngData = canvas.toDataURL('image/png');
// 显示PNG预览
pngPreview.src = currentPngData;
pngPreviewSection.classList.remove('hidden');
// 显示下载按钮
downloadBtn.classList.remove('hidden');
// 添加到历史记录
addToHistory(currentSvgData, currentPngData);
showNotification('SVG已成功转换为PNG');
};
img.src = svgDataUrl;
}
// 下载PNG文件
function downloadPng() {
if (!currentPngData) return;
const a = document.createElement('a');
a.href = currentPngData;
a.download = 'converted-image.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
showNotification('PNG文件已下载');
}
// 添加到转换历史
function addToHistory(svgData, pngData) {
const date = new Date();
const timeString = date.toLocaleTimeString();
const historyItem = document.createElement('div');
historyItem.className = 'p-3 border border-gray-200 rounded-lg flex justify-between items-center hover:bg-gray-50 transition-all-300';
historyItem.innerHTML = `
<div>
<p class="text-gray-700">转换于 ${timeString}</p>
<p class="text-gray-500 text-sm">${pngWidthInput.value} × ${pngHeightInput.value} 像素</p>
</div>
<button class="text-primary hover:text-primary/80 transition-colors" data-png="${pngData}">
<i class="fa fa-download"></i>
</button>
`;
// 添加下载事件
historyItem.querySelector('button').addEventListener('click', function() {
const a = document.createElement('a');
a.href = this.getAttribute('data-png');
a.download = `image-${Date.now()}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
showNotification('历史文件已下载');
});
// 添加到历史记录顶部
if (conversionHistory.firstChild) {
conversionHistory.insertBefore(historyItem, conversionHistory.firstChild);
} else {
conversionHistory.appendChild(historyItem);
}
}
// 事件监听
uploadArea.addEventListener('click', () => svgFileInput.click());
browseBtn.addEventListener('click', () => svgFileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('border-primary', 'bg-primary/5');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('border-primary', 'bg-primary/5');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('border-primary', 'bg-primary/5');
if (e.dataTransfer.files.length) {
handleFileUpload(e.dataTransfer.files[0]);
}
});
svgFileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFileUpload(e.target.files[0]);
}
});
// 背景颜色同步
bgColorInput.addEventListener('input', () => {
bgColorTextInput.value = bgColorInput.value.toUpperCase();
});
bgColorTextInput.addEventListener('input', () => {
if (/^#[0-9A-F]{6}$/i.test(bgColorTextInput.value)) {
bgColorInput.value = bgColorTextInput.value;
}
});
// 尺寸调整时保持比例
pngWidthInput.addEventListener('input', () => {
if (preserveAspectCheckbox.checked && svgOriginalWidth && svgOriginalHeight) {
const ratio = svgOriginalHeight / svgOriginalWidth;
pngHeightInput.value = Math.round(parseInt(pngWidthInput.value) * ratio);
}
});
pngHeightInput.addEventListener('input', () => {
if (preserveAspectCheckbox.checked && svgOriginalWidth && svgOriginalHeight) {
const ratio = svgOriginalWidth / svgOriginalHeight;
pngWidthInput.value = Math.round(parseInt(pngHeightInput.value) * ratio);
}
});
convertBtn.addEventListener('click', convertSvgToPng);
downloadBtn.addEventListener('click', downloadPng);
// 初始化图表
initStatsChart();
</script>
</body>
</html>