迁移至Laravel后Canvas无法正常绘制图片问题咨询
问题描述
我有一个无框架的原生前端项目原本运行正常:页面展示多张照片,点击任意照片会弹出模态框,照片会渲染在模态框内的canvas画布上。
以下是打开imageModal模态框、将照片绘制到名为photoImage的canvas上的JS代码,参数e代表被点击的图片元素:
var canvas = document.getElementById("photoImage"); var canvasCtx = canvas.getContext("2d"); function viewPhoto(e) { $("#imageModal").modal("show"); var image = new Image(); image.src = e.src; canvas.width = image.width; // 宽度设置不生效 canvas.height = image.height; // 高度设置不生效 canvasCtx.drawImage(image, 0, 0); // 照片无法绘制 }
把完全相同的代码迁移到Laravel项目后,照片没法正常绘制到canvas上。我对比过两个项目的canvas属性,完全一致:两个项目都能正确拿到图片的height属性,但Laravel项目里这个值改不了canvas的实际尺寸。
根本原因
这个问题和Laravel框架没有直接关系,核心是代码存在异步时序错误,原项目能正常运行完全是浏览器缓存带来的巧合:
- 浏览器加载
Image对象的资源是异步行为,给image.src赋值后,浏览器会发起资源请求,此时图片还没加载完成,直接读取image.width/image.height拿到的是默认值0,自然没法正确设置canvas尺寸、绘制图片。 - 原项目能跑通,是因为页面上已经展示了对应缩略图,图片资源早就被浏览器缓存了,给
Image对象设置同源缓存地址时,浏览器会直接同步返回缓存资源,此时立刻读宽高能拿到正确值。迁移到Laravel项目后,大概率是响应头配置、资源路径、缓存策略和原项目有差异,图片资源没有命中即时缓存,异步加载的时序问题就暴露出来了。
另外还有个次要影响因素:Bootstrap模态框在调用modal('show')的瞬间还处于隐藏状态,部分浏览器对display: none状态下的canvas做尺寸修改、绘制操作时,会出现渲染不生效的问题。
修复方法
调整代码逻辑,等两个前置条件满足后再操作canvas:一是图片资源加载完成,二是模态框完全显示(兼容极端浏览器渲染问题),修复后代码如下:
const canvas = document.getElementById("photoImage"); const canvasCtx = canvas.getContext("2d"); function viewPhoto(e) { $("#imageModal").one("shown.bs.modal", function () { const image = new Image(); // 注意:必须先绑定onload回调,再设置src,避免缓存场景下错过load事件 image.onload = function () { canvas.width = image.width; canvas.height = image.height; canvasCtx.drawImage(image, 0, 0); } image.src = e.src; }).modal("show"); }
两个关键注意点:
- 永远先给Image对象绑定
onload回调,再设置src属性,否则如果资源已经被缓存,load事件可能在绑定回调前就触发,导致回调永远不执行。 - 用
one而不是on绑定模态框事件,避免每次点击都重复绑定回调,造成内存泄漏。
内容的提问来源于stack exchange,提问作者RGriffiths
相关产品推荐
相关产品推荐

