
AJAX 加载的 HTML 中包含 和内联 时,脚本不会自动执行,且 document.getElementById() 在 DOM 尚未就绪时返回 null,导致绘图失败;需确保 Canvas 元素已挂载、上下文有效,并延迟执行绘图逻辑。
ajax 加载的 html 中包含 `canvas>` 和内联 `<script>` 时,脚本不会自动执行,且 `document.getelementbyid()` 在 dom 尚未就绪时返回 null,导致绘图失败;需确保 canvas 元素已挂载、上下文有效,并延迟执行绘图逻辑。</script>
当通过 AJAX(如 innerHTML 方式)动态插入包含
✅ 正确做法:分离结构与行为,主动触发绘图
不应将 <script> 直接拼接进 PHP 输出的 HTML 字符串中(例如 "<script>funStats('statsID123', ...)</script>"),而应在 AJAX 成功回调中显式查找新插入的 canvas 元素,并调用绘图函数。以下是推荐实现:
用户要生成可打印的中文字帖/练习纸、导出多页 A4 PDF 报告,或把 SVG 设计稿零误差还原到 Canvas 时使用。本技能是「Canvas 内容工厂闭环」的总控,编排:网格渲染引擎(13 种教育网格+拼音标注) → 多页 PDF 导出(A4 合成) → SVG 精准复刻(坐标误差<0.001px)。触发词:生成字帖、练习纸、导出 PDF、SVG 转 Canvas、印刷级还原、A4 报告、米字格田字格。
1. 修改 PHP 输出(仅输出 HTML 结构)
<!-- search.php --> <canvas id="statsID<?php echo $id; ?>" class="stat-canvas" data-hp="<?php echo $hp; ?>" data-att="<?php echo $att; ?>" data-spa="<?php echo $spa; ?>" data-def="<?php echo $def; ?>" data-spd="<?php echo $spd; ?>" data-spe="<?php echo $spe; ?>"></canvas>
2. AJAX 回调中批量初始化 Canvas
function funSearch(val) {
const xhr = new XMLHttpRequest();
xhr.open("GET", "search.php?search=" + encodeURIComponent(val), true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const container = document.getElementById('results');
container.innerHTML = xhr.responseText;
// ✅ 关键:在 DOM 更新后,查找所有新插入的 stat-canvas 并绘图
const canvases = container.querySelectorAll('.stat-canvas');
canvases.forEach(canvas => {
const id = canvas.id;
const hp = parseFloat(canvas.dataset.hp) || 0;
const att = parseFloat(canvas.dataset.att) || 0;
const spa = parseFloat(canvas.dataset.spa) || 0;
const def = parseFloat(canvas.dataset.def) || 0;
const spd = parseFloat(canvas.dataset.spd) || 0;
const spe = parseFloat(canvas.dataset.spe) || 0;
funStats(id, hp, att, spa, def, spd, spe);
});
}
};
xhr.send();
}
3. 优化 funStats():复用上下文,避免重复获取
原函数中多次调用 canvas.getContext('2d') 并创建多个上下文对象,不仅低效,还可能因 Canvas 尺寸未及时生效导致绘图异常。优化如下:
function funStats(id, hp, att, spa, def, spd, spe) {
const canvas = document.getElementById(id);
if (!canvas) return; // 安全防护:元素不存在则退出
// ✅ 设置尺寸(必须在获取上下文前)
canvas.width = 60;
canvas.height = 60;
const ctx = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const startAngle = 1.5 * Math.PI;
const maxVal = 256 / 2; // 归一化基准
// 绘制六层同心圆弧(HP, ATT, SPA, DEF, SPD, SPE)
const rings = [
{ radius: 28, value: hp, color: '#42af4f' },
{ radius: 24, value: att, color: '#d4bb49' },
{ radius: 20, value: spa, color: '#4a95ca' },
{ radius: 16, value: def, color: '#cc6a14' },
{ radius: 12, value: spd, color: '#8e55d3' },
{ radius: 8, value: spe, color: '#ce58b0' }
];
rings.forEach(ring => {
const endAngle = ((ring.value / maxVal) + 1.5) * Math.PI;
ctx.beginPath();
ctx.arc(centerX, centerY, ring.radius, startAngle, endAngle, false);
ctx.lineWidth = 2;
ctx.strokeStyle = ring.color;
ctx.stroke();
});
}
⚠️ 注意事项
- 不要依赖内联 <script></script>:innerHTML 不执行脚本,eval() 或 new Function() 均存在严重 XSS 风险,应绝对避免。
- 确保 DOM 就绪:绘图前务必确认 canvas 已存在于文档中(getElementById 返回非 null)。
- 尺寸设置时机:Canvas 的 width/height 属性必须显式设置(而非仅 CSS),且应在 getContext() 之前完成,否则绘图可能被缩放或清空。
- 性能考虑:若结果量大,可使用 requestAnimationFrame 或节流批量渲染,避免阻塞主线程。
通过结构与逻辑解耦、DOM 操作后主动初始化,即可稳定实现 AJAX 动态内容中的 Canvas 绘图功能。










