咱们分享信息卡提示词后,收到了很多朋友的建议和咨询,其中很集中的一个问题是:Gemini 3、Grok 4.1 和 Kimi K2 这些 AI 模型生成 HTML 内容后,怎么截图呢?
其实我之前是手动贴到浏览器做的预览和手动截图,因为有时还想自己微调一些地方。而在咱们的预览效果稳定后,1:1 完全复刻截图,就可行了,今天咱们就把这个支持「粘贴和编辑 HTML 内容、HTML 预览、截图导出图片、支持背景和留白」功能的 HTML 网页贴在下面。
Vibe Coding 半小时的成果,为了让非软件开发背景的朋友们都能用,都能自己动手做,这回 Vibe Coding 咱们没用 Cursor 和 Claude Code 这种开发工具,而是直接在 Gemini App、Grok App 这种 AI 工具里通过 Prompt 来完成的,大家可以用 Kimi、Qwen、ChatGPT 等任意 AI 助手来做自己的 HTML 功能。
开始前咱们再回顾一下信息卡提示词系列:
提示词分享:用 AI 模型给文章生成信息卡片(Gemini 3 效果最佳、Kimi K2 最听话稳定)
废话不多说,朋友们直接复制下方 HTML 内容,保存为一个 HTML 后缀的文件,再双击在浏览器中打开,就可以用了。
HTML 效果:
HTML 源码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mistral Studio V13 - 零空白终极版</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<style>
body, html { height: 100%; margin: 0; overflow: hidden; background: #09090b; color: #fff; font-family: -apple-system, sans-serif; }
.app-layout { display: flex; height: 100vh; width: 100vw; }
.editor-col {
width: 420px; background: #18181b; border-right: 1px solid #27272a;
display: flex; flex-direction: column; transition: all 0.3s; z-index: 50; flex-shrink: 0;
}
.editor-col.closed { width: 0; border: none; overflow: hidden; padding: 0; }
.editor-header {
height: 50px; background: #27272a; border-bottom: 1px solid #3f3f46;
display: flex; align-items: center; justify-content: space-between; padding: 0 16px;
}
textarea {
flex: 1; background: #18181b; color: #e4e4e7; border: none; padding: 20px;
font-family: 'JetBrains Mono', monospace; font-size: 13.5px; resize: none; outline: none; line-height: 1.6;
}
.preview-col { flex: 1; display: flex; flex-direction: column; background: #09090b; }
.toolbar {
height: 60px; background: #18181b; border-bottom: 1px solid #27272a;
display: flex; align-items: center; justify-content: space-between; padding: 0 24px;
}
.stage {
flex: 1; overflow: auto; display: flex; justify-content: center; align-items: flex-start;
padding: 60px; background-color: #dcd9d0;
background-image: radial-gradient(rgba(0,0,0,0.1) 1px, transparent 1px);
background-size: 20px 20px;
}
.preview-iframe-wrap {
width: 100%; max-width: 800px; background: white;
box-shadow: 0 20px 40px -10px rgba(0,0,0,0.3); border-radius: 8px; overflow: hidden;
}
iframe { width: 100%; height: 100%; border: none; display: block; background: #fff; }
.float-btn {
position: absolute; left: 20px; top: 80px; width: 44px; height: 44px; z-index: 100;
background: #27272a; border: 1px solid #3f3f46; color: #fff; border-radius: 10px;
display: none; align-items: center; justify-content: center; cursor: pointer; font-size: 18px;
}
.btn-primary {
background: #b83b18; color: white; padding: 10px 24px; border-radius: 8px;
font-size: 14px; font-weight: 600; display: flex; gap: 8px; align-items: center; cursor: pointer;
}
.btn-primary:hover { background: #d9461e; }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.setting-group { display: flex; align-items: center; gap: 20px; font-size: 13.5px; color: #d4d4d4; }
</style>
</head>
<body>
<div class="app-layout">
<div id="floatBtn" class="float-btn" onclick="toggleSidebar()">Code</div>
<div class="editor-col" id="editorCol">
<div class="editor-header">
<span class="font-bold text-gray-300">HTML SOURCE</span>
<div class="flex gap-2">
<button class="btn-icon" onclick="render()" title="刷新预览">Refresh</button>
<button class="btn-icon" onclick="toggleSidebar()">Collapse</button>
</div>
</div>
<textarea id="htmlInput" spellcheck="false"></textarea>
</div>
<div class="preview-col">
<div class="toolbar">
<div class="setting-group">
<label class="flex items-center gap-2 cursor-pointer select-none">
<input type="checkbox" id="paddingCheck" checked class="accent-orange-600 w-4 h-4 rounded">
<span>背景留白 + 投影</span>
</label>
<div class="h-5 w-[1px] bg-zinc-600"></div>
<div class="flex items-center gap-2">
<input type="color" id="bgColor" value="#dcd9d0">
<span class="text-xs font-mono">背景色</span>
</div>
</div>
<button class="btn-primary" onclick="captureEngine()">
Export HD Image 导出高清图片
</button>
</div>
<div class="stage" id="stage">
<div class="preview-iframe-wrap">
<iframe id="previewFrame" sandbox="allow-scripts allow-same-origin"></iframe>
</div>
</div>
</div>
</div>
<script>
const htmlInput = document.getElementById('htmlInput');
const previewFrame = document.getElementById('previewFrame');
const stage = document.getElementById('stage');
const bgColorInput = document.getElementById('bgColor');
const paddingCheck = document.getElementById('paddingCheck');
const editorCol = document.getElementById('editorCol');
const floatBtn = document.getElementById('floatBtn');
const defaultCode = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@900&family=Inter:wght@400;700&display=swap');
body { margin: 0; padding: 60px; background: transparent; font-family: 'Inter', sans-serif; }
.card {
max-width: 640px; margin: 0 auto; background: white; padding: 60px; border: 4px solid #000;
box-shadow: 20px 20px 0 rgba(0,0,0,0.1); border-radius: 16px;
}
h1 { font-family: 'Noto Serif SC', serif; font-size: 5rem; margin: 0 0 30px 0; line-height: 1; color: #1a1a1a; }
.box { height: 600px; background: linear-gradient(135deg, #ff9a9e, #fad0c4);
border: 5px dashed #e11d48; border-radius: 20px; margin: 50px 0;
display: flex; align-items: center; justify-content: center;
font-size: 2.5rem; font-weight: bold; color: #881337; }
.end { height: 300px; background: #1e293b; color: white; border-radius: 20px;
display: flex; align-items: center; justify-content: center; font-size: 3rem; }
</style>
</head>
<body>
<div class="card">
<h1>完美贴合<br>零空白</h1>
<p>现在底部再也没有多余白边了!</p>
<div class="box">600px 测试块<br>完整显示</div>
<div class="end">内容到底了!</div>
</div>
</body>
</html>`;
window.onload = () => {
htmlInput.value = defaultCode;
render();
updateBg();
};
function toggleSidebar() {
editorCol.classList.toggle('closed');
floatBtn.style.display = editorCol.classList.contains('closed') ? 'flex' : 'none';
}
htmlInput.addEventListener('keydown', e => { if (e.ctrlKey && e.key === 'Enter') render(); });
bgColorInput.addEventListener('input', updateBg);
function updateBg() { stage.style.background = bgColorInput.value; }
function render() {
previewFrame.srcdoc = htmlInput.value;
previewFrame.onload = () => {
setTimeout(() => {
const doc = previewFrame.contentDocument;
const h = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight);
previewFrame.style.height = h + 'px';
}, 400);
};
}
// V13 终极零空白截图引擎
async function captureEngine() {
const btn = document.querySelector('.btn-primary');
const oldText = btn.innerHTML;
btn.innerHTML = 'Export HD Image 渲染中...';
btn.disabled = true;
try {
const iframe = previewFrame;
const doc = iframe.contentDocument || iframe.contentWindow.document;
if (!doc) throw new Error("iframe 未加载");
// 等待字体加载
if (doc.fonts && doc.fonts.ready) await doc.fonts.ready;
const body = doc.body;
const html = doc.documentElement;
// 关键:计算真实内容高度(不加任何保险)
const realHeight = Math.max(
body.scrollHeight,
body.offsetHeight,
html.scrollHeight,
html.offsetHeight
);
// 为了防止 1px 误差,只把容器撑开一点点(不影响最终截图)
const safeHeight = realHeight + 40;
iframe.style.height = safeHeight + 'px';
body.style.minHeight = safeHeight + 'px';
html.style.height = safeHeight + 'px';
body.style.overflow = 'visible';
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
// 注入 html2canvas(如果还没)
if (!iframe.contentWindow.html2canvas) {
const script = doc.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
doc.head.appendChild(script);
await new Promise(r => script.onload = r);
}
// 核心:传给 html2canvas 的必须是 realHeight!!
const canvas = await iframe.contentWindow.html2canvas(doc.documentElement, {
scale: 2,
useCORS: true,
allowTaint: true,
backgroundColor: null,
logging: false,
scrollX: 0,
scrollY: 0,
width: html.scrollWidth,
height: realHeight, // 精确高度
windowWidth: html.scrollWidth,
windowHeight: realHeight, // 精确高度
foreignObjectRendering: true,
onclone: (cloned) => {
cloned.body.style.minHeight = realHeight + 'px';
cloned.documentElement.style.height = realHeight + 'px';
}
});
// 加背景留白与投影(可选)
let finalCanvas = canvas;
if (paddingCheck.checked) {
const padding = 120;
finalCanvas = document.createElement('canvas');
finalCanvas.width = canvas.width + padding * 2;
finalCanvas.height = canvas.height + padding * 2;
const ctx = finalCanvas.getContext('2d');
ctx.fillStyle = bgColorInput.value;
ctx.fillRect(0, 0, finalCanvas.width, finalCanvas.height);
ctx.shadowColor = "rgba(0,0,0,0.25)";
ctx.shadowBlur = 70;
ctx.shadowOffsetY = 35;
ctx.drawImage(canvas, padding, padding);
}
const a = document.createElement('a');
a.download = `perfect-${Date.now()}.png`;
a.href = finalCanvas.toDataURL('image/png');
a.click();
} catch (e) {
console.error(e);
alert("截图失败:" + e.message);
} finally {
btn.innerHTML = oldText;
btn.disabled = false;
}
}
</script>
</body>
</html>
.cls-1{fill:
#001e36
;}.cls-2{fill:
#31a8ff
;}
.cls-1{fill:
#001e36
;}.cls-2{fill:
#31a8ff
;}
