Canvas绘图报错:Cannot read property 'beginPath' of null 求助
修复Canvas绘图时的
TypeError: Cannot read property 'beginPath' of null错误 我来帮你解决这个问题!你遇到的错误提示本质是**context对象还没初始化就被调用了**,同时代码里还有几个细节需要调整,咱们一步步来修复:
错误根源分析
- Context初始化时机太晚:目前你只在
dessin方法里初始化this.context = this.canvas.getContext("2d"),但触发触摸/鼠标按下事件时,activationDessin会先执行,这时候context还是null,调用this.context.beginPath()自然报错。 - 全局变量污染问题:你在
activationDessin和deplacementSouris里用的positionX/positionY是全局变量,不是Signature对象的属性,容易导致作用域混乱和取值错误。 - 触摸事件的
this指向错误:触摸事件绑定未使用bind(Signature),导致convertTouchEvent里的this指向canvas元素而非Signature对象,后续事件触发逻辑会出问题。
修复步骤
1. 提前初始化Canvas Context
把context的初始化移到对象创建阶段,确保任何绘图方法执行前context已存在:
var Signature = { // Attributs ecriture : false, canvas : document.getElementById("signature"), // 直接初始化context context : document.getElementById("signature").getContext("2d"), signatureImg : null, // 改为对象属性,避免全局变量 positionX : null, positionY: null, // 新增初始化方法,统一设置绘图参数 init: function() { this.context.lineWidth = 5; }, // ... 其他方法 }
然后在代码末尾调用初始化:
Signature.init();
2. 修复变量作用域问题
把activationDessin和deplacementSouris里的全局变量改为对象自身属性:
修改activationDessin方法:
activationDessin : function() { this.ecriture = true; this.context.beginPath(); // 使用对象自身的position属性 this.context.moveTo(this.positionX, this.positionY); },
修改deplacementSouris方法:
deplacementSouris : function(event) { const sourisPosition = this.getMousePos(event); // 赋值给对象属性 this.positionX = sourisPosition.x; this.positionY = sourisPosition.y; this.dessin(this.positionX, this.positionY); },
3. 修复触摸事件的this指向
绑定触摸事件时用bind(Signature)确保this指向Signature对象,同时修正dispatchEvent的目标:
// 触摸事件绑定修改为 Signature.canvas.addEventListener("touchstart", Signature.convertTouchEvent.bind(Signature)); Signature.canvas.addEventListener("touchmove", Signature.convertTouchEvent.bind(Signature)); Signature.canvas.addEventListener("touchend", Signature.convertTouchEvent.bind(Signature));
然后修改convertTouchEvent里的事件触发行:
// 替换原来的this.dispatchEvent(mouse_ev) ev.target.dispatchEvent(mouse_ev);
完整修复后的代码
var Signature = { // Attributs ecriture : false, canvas : document.getElementById("signature"), context : document.getElementById("signature").getContext("2d"), signatureImg : null, positionX : null, positionY: null, // 初始化方法 init: function() { this.context.lineWidth = 5; }, // 绘图方法 dessin : function(positionX, positionY) { if(this.ecriture){ this.context.lineTo(positionX, positionY); this.context.stroke(); } }, activationDessin : function() { this.ecriture = true; this.context.beginPath(); this.context.moveTo(this.positionX, this.positionY); }, // 获取鼠标/触摸位置 getMousePos : function(event) { const rect = this.canvas.getBoundingClientRect(); return{ x:event.clientX - rect.left, y:event.clientY - rect.top }; }, // 鼠标移动监听 deplacementSouris : function(event) { const sourisPosition = this.getMousePos(event); this.positionX = sourisPosition.x; this.positionY = sourisPosition.y; this.dessin(this.positionX, this.positionY); }, // 停止绘图 desactivationDessin : function() { this.ecriture = false; }, // 触摸事件转鼠标事件 convertTouchEvent : function(ev) { var touch, ev_type, mouse_ev; touch = ev.targetTouches[0]; ev.preventDefault(); switch (ev.type) { case 'touchstart': if (ev.targetTouches.length != 1) { return; } touch = ev.targetTouches[0]; ev_type = 'mousedown'; break; case 'touchmove': if (ev.targetTouches.length != 1) { return; } touch = ev.targetTouches[0]; ev_type = 'mousemove'; break; case 'touchend': if (ev.changedTouches.length != 1) { return; } touch = ev.changedTouches[0]; ev_type = 'mouseup'; break; default: return; } mouse_ev = document.createEvent("MouseEvents"); mouse_ev.initMouseEvent( ev_type, true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey, 0, null ); ev.target.dispatchEvent(mouse_ev); }, // 清除画布 clearCanvas : function() { // 使用canvas自身宽高,适配尺寸变化 this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } // 初始化画布 Signature.init(); // PC端事件绑定 Signature.canvas.addEventListener("mousedown", Signature.activationDessin.bind(Signature)); Signature.canvas.addEventListener("mousemove", Signature.deplacementSouris.bind(Signature)); Signature.canvas.addEventListener("mouseup", Signature.desactivationDessin.bind(Signature)); // 移动端触摸事件绑定 Signature.canvas.addEventListener("touchstart", Signature.convertTouchEvent.bind(Signature)); Signature.canvas.addEventListener("touchmove", Signature.convertTouchEvent.bind(Signature)); Signature.canvas.addEventListener("touchend", Signature.convertTouchEvent.bind(Signature)); // 清除按钮绑定 document.getElementById("boutonEffacer").addEventListener("click", function() { Signature.clearCanvas(); });
额外优化
我把clearCanvas里的固定宽高改成了this.canvas.width和this.canvas.height,这样画布尺寸变化时清除逻辑也能自动适配。
内容的提问来源于stack exchange,提问作者Koscielski
相关产品推荐
相关产品推荐

