This repository has been archived on 2026-04-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
knowledge-base/user-guide/演示h5_v2_iPhone16竖屏比例.html
T
sujianhua d05a6cd529 feat(user-guide): 添加图片点击放大和批量导出PNG功能
演示页面增强:
- 引入 html2canvas 库支持导出为 PNG
- 添加顶部工具栏和批量导出按钮
- 图片点击放大/缩小交互
- 单页导出按钮在每个卡片头部
- 兼容 file:// 和 HTTP 协议的导出处理
- 本地文件模式下显示协议限制提示
2026-04-20 02:28:42 +08:00

643 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阿尼坊小程序 - 用户指南(公众号截图版)</title>
<!-- html2canvas 库 - 用于导出PNG -->
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<style>
:root {
--primary-color: #6366f1;
--text-main: #1f2937;
--text-muted: #4b5563;
--bg-color: #1e293b; /* 暗色背景凸显截图卡片 */
--bg-content: #ffffff;
--border-color: #e5e7eb;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
color: var(--text-main);
background-color: var(--bg-color);
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
line-height: 1.7;
}
/* 顶部提示工具栏 */
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
width: 393px;
background-color: var(--bg-content);
padding: 12px 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.toolbar-text {
color: var(--text-muted);
font-size: 14px;
}
.toolbar-btn {
background-color: var(--primary-color);
color: #fff;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.toolbar-btn:hover {
background-color: #4f46e5;
transform: scale(1.02);
}
.toolbar-btn:disabled {
background-color: #94a3b8;
cursor: not-allowed;
transform: none;
}
/* 核心:iPhone16 竖屏卡片 9:19.5 (393x852) - 自适应高度 */
.iphone-card {
width: 393px;
min-height: 852px;
background-color: var(--bg-content);
margin-bottom: 40px;
position: relative;
display: flex;
flex-direction: column;
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.4);
/* 直角边缘方便直接矩形截图 */
border: 1px solid #d1d5db;
}
.card-header {
background-color: var(--primary-color);
color: #ffffff;
height: 54px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
font-weight: 600;
font-size: 16px;
flex-shrink: 0;
}
.card-body {
padding: 28px 24px;
flex: 1;
overflow: visible;
display: flex;
flex-direction: column;
}
.card-footer {
height: 40px;
text-align: center;
color: #94a3b8;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px dashed var(--border-color);
flex-shrink: 0;
}
/* 内部排版样式 */
h1 { font-size: 24px; margin-bottom: 20px; color: var(--primary-color); border-bottom: 2px solid var(--border-color); padding-bottom: 10px; }
h2 { font-size: 20px; margin: 18px 0 10px; color: var(--text-main); }
h3 { font-size: 17px; margin: 14px 0 8px; color: var(--text-main); font-weight: 600; }
p { margin-bottom: 12px; font-size: 16px; color: var(--text-muted); }
ul, ol { margin-bottom: 14px; padding-left: 22px; font-size: 16px; color: var(--text-muted); }
li { margin-bottom: 8px; }
/* 封面特殊排版 */
.cover-body { justify-content: center; align-items: center; text-align: center; }
.cover-icon { font-size: 64px; margin-bottom: 20px; }
.cover-title { font-size: 28px; color: var(--primary-color); margin-bottom: 12px; border: none;}
.cover-subtitle { font-size: 17px; color: var(--text-muted); margin-bottom: 40px; }
.cover-list { text-align: left; background: #f8fafc; padding: 22px 32px; border-radius: 12px; width: 100%; border: 1px solid var(--border-color); }
.cover-list ol { margin-bottom: 0; }
.cover-list li { margin-bottom: 14px; font-weight: 500; font-size: 16px;}
/* 截图图片控制 - 点击放大功能 */
.img-wrapper { text-align: center; margin: 10px 0; flex-shrink: 0; }
.actual-img {
max-width: 100%;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
object-fit: contain;
border: 1px solid #e2e8f0;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.actual-img:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}
.actual-img.enlarged {
transform: scale(1.8);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
z-index: 10;
position: relative;
}
.img-enlarge-hint {
font-size: 12px;
color: #94a3b8;
margin-top: 4px;
opacity: 0.8;
}
/* 表格样式适配移动端 */
table { width: 100%; border-collapse: collapse; margin: 14px 0; background-color: white; font-size: 14px; }
th, td { border: 1px solid var(--border-color); padding: 10px; text-align: left; }
th { background-color: #f8fafc; font-weight: 600; color: var(--text-main); }
/* 提示框样式 */
.alert { padding: 14px 16px; border-radius: 8px; margin: 14px 0; display: flex; flex-direction: column; font-size: 14px; }
.alert.warning { background-color: #fffbeb; border-left: 4px solid #f59e0b; color: #92400e; }
.alert.info { background-color: #eff6ff; border-left: 4px solid #3b82f6; color: #1e40af; }
.alert strong { margin-bottom: 6px; font-size: 16px; }
.alert p { margin-bottom: 4px; font-size: 14px; color: inherit; }
.alert p:last-child { margin-bottom: 0; }
/* 代码块样式 */
pre {
background-color: #1e293b; color: #e2e8f0; padding: 14px; border-radius: 8px;
margin: 12px 0; font-size: 14px; white-space: pre-wrap; word-wrap: break-word;
}
code { font-family: ui-monospace, monospace; }
p code, li code { background-color: #f1f5f9; color: #ef4444; padding: 2px 6px; border-radius: 4px; font-size: 14px; }
/* 导出按钮样式 */
.export-btn {
background: transparent;
color: #fff;
border: 1px solid rgba(255,255,255,0.6);
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
margin-left: 8px;
transition: all 0.2s;
}
.export-btn:hover {
background: rgba(255,255,255,0.2);
border-color: #fff;
}
</style>
</head>
<body>
<div class="toolbar">
<span class="toolbar-text">💡 iPhone 16 竖屏比例 (393x852) - 共 7 页</span>
<button class="toolbar-btn" onclick="exportAllCards()" id="exportAllBtn">📦 批量导出全部</button>
</div>
<!-- Page 1: 封面 -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>01 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body cover-body">
<div class="cover-icon">🦊</div>
<h1 class="cover-title">阿尼坊小程序</h1>
<div class="cover-subtitle">用户操作指南</div>
<div class="cover-list">
<ol>
<li><strong style="color:var(--primary-color)">一、登录流程</strong> - 微信授权登录</li>
<li><strong style="color:var(--primary-color)">二、实名认证</strong> - 完成身份验证</li>
<li><strong style="color:var(--primary-color)">三、申摊流程</strong> - 申请摊位参展</li>
<li><strong style="color:var(--primary-color)">四、预约详情</strong> - 查看申请状态</li>
</ol>
</div>
<p style="margin-top: 30px; font-size: 15px;">欢迎使用阿尼坊!本指南将帮助您快速上手,顺利参与动漫市集。如有问题请通过「我的」-「意见反馈」联系客服。</p>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 2: 登录流程 -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>02 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>一、登录流程</h1>
<p>首次打开阿尼坊小程序时,系统会引导您完成微信登录。</p>
<h3>1. 打开小程序</h3>
<p>通过微信搜索"阿尼坊"或扫描二维码进入小程序。</p>
<h3>2. 触发登录</h3>
<p>当您点击需要登录的功能(如申摊)时,系统会弹出登录提示框。</p>
<div class="img-wrapper">
<img src="screenshots/2_前往登录弹窗.jpg" alt="登录提示弹窗" class="actual-img" crossorigin="anonymous">
<div class="img-enlarge-hint">点击图片可放大查看</div>
</div>
<div class="alert warning" style="margin-top: auto;">
<strong>⚠️ 游客用户限制</strong>
<p>未登录仅可浏览:活动公告、商铺、摊位卡片详情。</p>
<p>登录后可使用:设施预约、摊位申请、评论点赞、收藏。</p>
</div>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 3: 登录流程 (续) -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>03 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>一、登录流程 (续)</h1>
<h3>3. 填写个人信息</h3>
<p>登录表单包含头像选择、昵称填写和用户协议勾选。</p>
<div class="img-wrapper">
<img src="screenshots/3_填写昵称_头像信息.png" alt="登录表单" class="actual-img" crossorigin="anonymous">
<div class="img-enlarge-hint">点击图片可放大查看</div>
</div>
<h3>4. 手机号授权</h3>
<p>系统申请获取微信绑定手机号,用于验证码接收和客服联系。</p>
<div class="img-wrapper">
<img src="screenshots/4_登录后授权手机号弹窗.png" alt="手机号授权" class="actual-img" crossorigin="anonymous">
<div class="img-enlarge-hint">点击图片可放大查看</div>
</div>
<h3>5. 完成登录</h3>
<p>授权完成后,您的头像和昵称将显示在「我的」页面,系统自动生成唯一 UID。</p>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 4: 实名认证 -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>04 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>二、实名认证</h1>
<p>实名认证是使用设施预约和摊位申请功能的前置条件。</p>
<h3>认证入口</h3>
<p>进入「我的」页面,点击「前往实名认证」按钮。</p>
<div class="img-wrapper">
<img src="screenshots/5_我的界面_前往实名认证.png" alt="实名认证入口" class="actual-img" crossorigin="anonymous">
<div class="img-enlarge-hint">点击图片可放大查看</div>
</div>
<h3>认证信息填写</h3>
<table>
<thead>
<tr><th>字段</th><th>说明</th><th>要求</th></tr>
</thead>
<tbody>
<tr><td>真实姓名</td><td>与身份证一致</td><td>2-20个汉字</td></tr>
<tr><td>身份证号码</td><td>18位身份证号</td><td>正确格式验证</td></tr>
</tbody>
</table>
<div class="img-wrapper">
<img src="screenshots/6_填写实名认证信息.png" alt="实名认证表单" class="actual-img" crossorigin="anonymous">
<div class="img-enlarge-hint">点击图片可放大查看</div>
</div>
<div class="alert warning" style="margin-top: auto;">
<strong>⚠️ 认证后不可修改</strong>
<p>实名认证完成后,姓名和身份证号不可更改。如需修改请联系客服。</p>
</div>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 5: 申摊流程 -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>05 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>三、申摊流程</h1>
<p>申请摊位参加阿尼坊动漫市集。</p>
<div class="alert info">
<strong>💡 前置条件</strong>
<p>申请摊位前请确保:<br>✅ 已完成微信登录<br>✅ 已完成实名认证</p>
</div>
<h3>申请入口</h3>
<p>进入「市集」页面,点击右上角「申摊」按钮。</p>
<h3>填写申请信息</h3>
<table>
<thead>
<tr><th>字段</th><th>限制</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td>标题</td><td>50字内</td><td>卡片标题</td></tr>
<tr><td>正文</td><td>500字内</td><td>描述内容,可用 #标签</td></tr>
<tr><td>图片</td><td>1张,≤5MB</td><td>卡片封面图</td></tr>
<tr><td>联系QQ</td><td>可修改</td><td>客服联系渠道</td></tr>
<tr><td>预约场次</td><td>下拉选择</td><td>日期 + 区域</td></tr>
<tr><td>动漫IP</td><td>1-10个</td><td>必选至少1个标签</td></tr>
</tbody>
</table>
<p><strong>填写步骤:</strong></p>
<ol>
<li>输入标题和内容描述(正文最多500字)</li>
<li>上传一张清晰图片作为封面(≤5MB</li>
<li>选择参展场次(日期+区域)</li>
<li>选择动漫IP标签(至少1个,最多10个)</li>
<li>勾选已阅读申摊须知,点击确认提交</li>
</ol>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 6: 申摊流程 (续) -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>06 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>三、申摊流程 (续)</h1>
<h3>申请状态流转</h3>
<table>
<thead>
<tr><th>状态</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td>待抽签</td><td>申请已提交,等待抽签</td></tr>
<tr><td>审核中</td><td>抽签中签,等待审核</td></tr>
<tr><td>待缴费</td><td>审核通过,等待支付</td></tr>
<tr><td>未中签</td><td>抽签未中</td></tr>
<tr><td>审核驳回</td><td>审核未通过(显示原因)</td></tr>
<tr><td>成功</td><td>已公布摊位号</td></tr>
</tbody>
</table>
<div class="alert warning">
<strong>⚠️ 场次限制</strong>
<p>• 每天仅能申请一个场次<br>• 申请前请确认场次信息</p>
</div>
<div class="alert info">
<strong>💡 存稿机制</strong>
<p>系统自动保存上次填写内容,30天后自动清理存稿。</p>
</div>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
<!-- Page 7: 预约详情 -->
<div class="iphone-card">
<div class="card-header"><span>🦊 阿尼坊</span><span>07 / 07</span><button class="export-btn" onclick="exportCard(this)">导出PNG</button></div>
<div class="card-body">
<h1>四、预约详情</h1>
<p>查看摊位申请和设施预约记录。</p>
<h3>入口位置</h3>
<p>进入「我的」页面,点击「预约详情」入口。</p>
<h3>页面结构</h3>
<p>页面分为两个栏目:<br><strong>• 摊位栏目</strong> - 摊位申请记录<br><strong>• 设施栏目</strong> - 设施预约记录</p>
<h3>摊位申请状态</h3>
<table>
<thead>
<tr><th>状态</th><th>显示内容</th><th>操作</th></tr>
</thead>
<tbody>
<tr><td>待缴费</td><td>订单号、金额、截止时间</td><td>支付 / 取消</td></tr>
<tr><td>待公布</td><td>订单号、退款提示</td><td>申请退款</td></tr>
<tr><td>成功</td><td>摊位号 (如: <code>2.1 A区13</code>)</td><td></td></tr>
<tr><td>已失效</td><td>订单号</td><td></td></tr>
</tbody>
</table>
<div class="alert warning">
<strong>⚠️ 支付时限</strong>
<p>请在截止时间前完成支付,超时订单自动取消。</p>
</div>
<h3>退款申请</h3>
<p>符合退款条件时点击「申请退款」按钮,退款成功后状态变更为「已退款」。</p>
</div>
<div class="card-footer">- 官方运营指南 -</div>
</div>
</body>
<script>
// 图片 base64 缓存(解决跨域图片 tainted canvas 问题)
const imageCache = {};
let preloadComplete = false;
let isLocalFile = window.location.protocol === 'file:';
let allowTaintedCanvas = false;
// 显示协议兼容性提示
function showProtocolWarning() {
const toolbar = document.querySelector('.toolbar');
if (toolbar && isLocalFile) {
const warningDiv = document.createElement('div');
warningDiv.style.cssText = 'width:393px;background:#fffbeb;border-left:4px solid #f59e0b;padding:12px 16px;margin-bottom:20px;border-radius:8px;font-size:14px;color:#92400e;';
warningDiv.innerHTML = `
<strong>⚠️ 本地文件模式限制</strong>
<p style="margin:6px 0 0;">当前使用 file:// 协议直接打开,图片导出功能受限。</p>
<p style="margin:4px 0;">推荐方式:启动本地 HTTP 服务器</p>
<code style="background:#fef3c7;padding:2px 6px;border-radius:4px;">npx serve -l 3000</code>
<p style="margin:4px 0;">然后访问 <a href="#" onclick="copyToLocalhost()" style="color:#d97706;">http://localhost:3000/...</a></p>
`;
toolbar.insertAdjacentElement('beforebegin', warningDiv);
allowTaintedCanvas = true;
}
}
// 复制 localhost URL
function copyToLocalhost() {
const localhostUrl = 'http://localhost:3000/' + window.location.pathname.split('/').pop();
navigator.clipboard.writeText(localhostUrl).then(() => {
alert('已复制: ' + localhostUrl + '\n请启动服务器后访问');
});
}
// 预加载图片转为 base64(仅 HTTP/HTTPS 协议有效)
function preloadImages() {
// file:// 协议下 XMLHttpRequest 被 CORS 阻止,跳过预加载
if (isLocalFile) {
console.warn('file:// 协议下无法预加载图片,将使用 allowTaint 模式导出');
preloadComplete = true;
allowTaintedCanvas = true;
return;
}
const images = document.querySelectorAll('.actual-img');
const promises = [];
images.forEach(img => {
const src = img.getAttribute('src');
if (src && !imageCache[src]) {
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
const reader = new FileReader();
reader.onloadend = function() {
imageCache[src] = reader.result;
img.src = reader.result; // 替换为 base64
resolve();
};
reader.readAsDataURL(xhr.response);
};
xhr.onerror = function() {
console.warn('图片预加载失败:', src);
resolve(); // 即使失败也继续,不阻塞导出
};
xhr.open('GET', src);
xhr.responseType = 'blob';
xhr.send();
});
promises.push(promise);
}
});
// 所有图片加载完成后标记
Promise.all(promises).then(() => {
preloadComplete = true;
console.log('图片预加载完成,可以安全导出');
});
}
// 页面加载完成后初始化
window.addEventListener('load', () => {
showProtocolWarning();
preloadImages();
});
// 图片点击放大/缩小切换
document.querySelectorAll('.actual-img').forEach(img => {
img.addEventListener('click', function() {
if (this.classList.contains('enlarged')) {
// 缩小恢复
this.classList.remove('enlarged');
} else {
// 先缩小其他已放大的图片
document.querySelectorAll('.actual-img.enlarged').forEach(other => {
other.classList.remove('enlarged');
});
// 放大当前图片
this.classList.add('enlarged');
}
});
});
// 导出单个卡片为PNG
function exportCard(button) {
// file:// 协议下跳过预加载检查,使用 allowTaint 模式
if (!isLocalFile && !preloadComplete) {
alert('图片正在加载中,请稍后再试导出...\n建议:等待页面完全加载后再点击导出');
return;
}
// file:// 协议下提示用户
if (isLocalFile) {
console.warn('file:// 模式导出:图片可能无法正确渲染,建议使用 HTTP 服务器');
}
const card = button.closest('.iphone-card');
const pageText = card.querySelector('.card-header span:nth-child(2)').textContent.trim();
const pageNum = pageText.split('/')[0].trim().replace('0', '');
const title = getCardTitle(pageNum);
html2canvas(card, {
scale: 2, // 高清导出
useCORS: !isLocalFile, // HTTP 下启用跨域,file:// 下禁用
allowTaint: allowTaintedCanvas, // file:// 下允许污染画布
backgroundColor: '#ffffff',
logging: false
}).then(canvas => {
const link = document.createElement('a');
link.download = `阿尼坊用户指南_${title}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
}).catch(err => {
let errorMsg = err.message;
if (isLocalFile) {
errorMsg += '\n\nfile:// 协议限制:请使用 HTTP 服务器访问';
}
alert('导出失败:' + errorMsg);
console.error('导出错误:', err);
});
}
// 根据页码获取卡片标题
function getCardTitle(pageNum) {
const titles = {
'1': '封面',
'2': '登录流程',
'3': '登录流程续',
'4': '实名认证',
'5': '申摊流程',
'6': '申摊流程续',
'7': '预约详情'
};
return titles[pageNum] || `${pageNum}`;
}
// 批量导出所有卡片
async function exportAllCards() {
// file:// 协议下跳过预加载检查,使用 allowTaint 模式
if (!isLocalFile && !preloadComplete) {
alert('图片正在加载中,请稍后再试批量导出...\n建议:等待页面完全加载后再点击导出');
return;
}
// file:// 协议下提示用户
if (isLocalFile) {
console.warn('file:// 模式批量导出:图片可能无法正确渲染,建议使用 HTTP 服务器');
}
const btn = document.getElementById('exportAllBtn');
const cards = document.querySelectorAll('.iphone-card');
const total = cards.length;
btn.disabled = true;
btn.textContent = `⏳ 正在导出...`;
for (let i = 0; i < total; i++) {
btn.textContent = `⏳ 导出中 (${i+1}/${total})`;
const card = cards[i];
const pageNum = (i + 1).toString();
const title = getCardTitle(pageNum);
try {
const canvas = await html2canvas(card, {
scale: 2,
useCORS: !isLocalFile, // HTTP 下启用跨域,file:// 下禁用
allowTaint: allowTaintedCanvas, // file:// 下允许污染画布
backgroundColor: '#ffffff',
logging: false
});
const link = document.createElement('a');
link.download = `阿尼坊用户指南_${title}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
// 稍作延迟避免浏览器阻塞
await new Promise(r => setTimeout(r, 300));
} catch (err) {
console.error(`${pageNum}页导出失败:`, err);
let errorMsg = err.message;
if (isLocalFile) {
errorMsg += ' (file:// 协议限制)';
}
alert(`${pageNum}页导出失败:${errorMsg}`);
}
}
btn.disabled = false;
btn.textContent = '📦 批量导出全部';
alert('全部导出完成!');
}
</script>
</html>