调用数组对象方法报错及Canvas渲染异常问题解决
问题分析与解决
一、for循环报错Cannot read properties of undefined (reading 'callAllFunctions')的原因及解决
原因
报错本质是循环时访问了数组中undefined的元素。结合场景,可能的触发点:
- 数组初始化或动态添加元素时,存在未正确创建
obj实例的情况(比如误push了undefined); - 若之前代码中存在删除数组元素的逻辑(比如
splice),正向循环时索引与数组长度不匹配,导致访问到超出数组范围的位置。
解决
在循环中增加元素存在性判断,确保只调用有效实例的方法:
setInterval(() => { clear() // 先清画布,同时修正渲染顺序 for (let i = 0; i < balls.length; i++) { if (balls[i]) { // 过滤undefined元素 balls[i].callAllFunctions() } } }, 17)
二、forEach循环无渲染的原因及解决
原因
渲染顺序完全颠倒:当前逻辑是先调用所有对象的draw方法绘制内容,然后立刻执行clear()清空画布,导致刚画出来的元素被瞬间清除,视觉上看不到任何内容。
解决
调整执行顺序,先清空画布,再调用对象的方法(包含绘制逻辑):
setInterval(() => { clear() // 第一步:清空画布 balls.forEach((e) => { e.callAllFunctions() // 第二步:绘制所有对象 }) }, 17)
额外优化点
- 鼠标坐标计算修正:
canvasBounds需在鼠标移动事件内实时获取,否则窗口resize后坐标会偏移:
canvas.addEventListener("mousemove", (e) => { const canvasBounds = canvas.getBoundingClientRect(); mouse.x = e.x - canvasBounds.x; mouse.y = e.y - canvasBounds.y; });
update方法冗余代码删除:原代码中重复对ysp加0.5,导致下落速度异常,删除冗余逻辑:
update() { this.ysp += 0.5 this.x += this.xsp this.y += this.ysp }
修正后的完整JavaScript代码
const canvas = document.getElementById("canvas") const ctx = canvas.getContext("2d") canvas.height = window.innerHeight canvas.width = window.innerWidth canvas.style = "background-color:#556479;" // 窗口resize事件 window.addEventListener("resize", () => { canvas.height = window.innerHeight canvas.width = window.innerWidth }) let RandomNum = (max, min) => { return Math.floor(Math.random() * (max - min + 1)) + min; } function randomArray(arr) { return Math.floor(Math.random() * arr.length); } function distance(x1, y1, x2, y2) { let distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) return distance } function clamp(value, min, max) { return Math.min(Math.max(value, min), max) } class obj { constructor(x, y, c) { this.x = x this.y = y this.ysp = 1 this.xsp = 2 this.c = c } draw() { ctx.fillStyle = this.c ctx.beginPath() ctx.arc(this.x, this.y, 10, Math.PI * 2, 0) ctx.fill() } update() { this.ysp += 0.5 this.x += this.xsp this.y += this.ysp } limit() { this.x = clamp(this.x, 0, canvas.width) this.y = clamp(this.y, 0, canvas.height) } bounce() { if (this.y + this.ysp > canvas.height || this.y + this.ysp < 0) { this.xsp -= 0.05 if (this.xsp < 0.6) { this.xsp = 0 } this.ysp *= -1 } else if (this.x + this.xsp > canvas.width || this.x + this.xsp < 0) { this.xsp -= 0.05 if (this.xsp < 0.6) { this.xsp = 0 } this.xsp *= -1 } } callAllFunctions() { this.bounce() this.update() this.draw() this.limit() } } let mouse = {"x": 20, "y": 20} canvas.addEventListener("mousemove", (e) => { const canvasBounds = canvas.getBoundingClientRect(); mouse.x = e.x - canvasBounds.x; mouse.y = e.y - canvasBounds.y; }); let a = new obj(100,100,"#fff") let balls = [a] canvas.addEventListener("mousedown", (e) => { balls.push(new obj(mouse.x, mouse.y, "#fff")) }) let clear = () => { ctx.clearRect(0, 0, canvas.width, canvas.height) } setInterval(() => { clear() balls.forEach((e) => { e.callAllFunctions() }) }, 17)
内容的提问来源于stack exchange,提问作者user17597013
相关产品推荐
相关产品推荐

