JavaScript中canvas上传新图片无法完整绘制的问题排查
问题原因
- 这是浏览器canvas实现的常见渲染时序问题:当同步修改canvas的width/height属性后,浏览器内核会同步重置画布内容和上下文状态,但部分场景下后续立刻执行的drawImage仍会沿用修改前的画布视口尺寸做裁剪,超出原尺寸的内容会被截断,这就是你只能看到前一次尺寸部分的原因。
- 两次调用drawImage可以生效的逻辑是:第一次绘制触发了浏览器完成canvas尺寸的同步更新,第二次绘制就可以基于新的视口尺寸正常渲染,但确实属于绕路的非规范解法。
修复方案
修改img.onload内的逻辑,修改完canvas高度后,先强制清空画布触发浏览器更新视口,再执行绘制即可,同时优化了原代码里的小兼容性问题:
var imageLoader = document.getElementById('imageLoader'); imageLoader.addEventListener('change', handleImage, false); var canvas = document.getElementById('imageCanvas'); var ctx = canvas.getContext('2d'); var img; var ratio; var overlay = document.getElementById("overlay") canvas.width = 400; function handleImage(e){ var reader = new FileReader(); reader.onload = function(event){ img = new Image(); img.onload = function(){ ratio = this.height / this.width; // 修改canvas尺寸 canvas.height = canvas.width * ratio; // 新增:强制清空画布触发视口尺寸更新,解决绘制截断问题 ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img,0,0,canvas.width, canvas.height); } // 优化兼容性写法 overlay.style.display = "none"; img.src = event.target.result; window.onbeforeunload = function() { return true; }; } reader.readAsDataURL(e.target.files[0]); }
如果上述方案仍存在兼容问题,可以把drawImage放到requestAnimationFrame回调中,确保绘制时canvas尺寸已经完成渲染更新:
img.onload = function(){ ratio = this.height / this.width; canvas.height = canvas.width * ratio; requestAnimationFrame(() => { ctx.drawImage(img,0,0,canvas.width, canvas.height); }) }
内容的提问来源于stack exchange,提问作者Emil
相关产品推荐
相关产品推荐

