Khan Academy记忆瓦片项目hover状态失效问题求助
解决记忆瓦片游戏中Hover状态闪烁消失的问题
嘿,我完全懂你卡在这里的挫败感——这个hover效果一闪而过的问题确实戳中了Canvas绘图的核心逻辑:draw()函数会每一帧清空画布并重绘所有元素,你之前在mouseMoved()里直接调用hoverState()绘制的内容,下一秒就被draw()循环里的tiles[i].draw()给覆盖掉了。加上代码里还有几个小bug(比如循环越界、调用不存在的方法),导致效果完全出不来。
下面是一步步的解决方案:
1. 给Tile类添加hover状态标记
我们不需要直接在鼠标事件里绘制,而是给每个瓦片加一个isHovered属性,用来标记它是否处于hover状态,让draw()函数根据这个状态来渲染样式:
var Tile = function(x, y, face) { this.x = x; this.y = y; this.width = 70; this.face = face; this.isFaceUp = false; this.isMatch = false; this.isHovered = false; // 新增:标记是否处于hover状态 };
2. 重构Tile的draw方法,支持状态驱动绘制
把所有绘制逻辑统一到draw()里,让它根据isMatch、isFaceUp、isHovered的优先级来渲染对应的样式:
Tile.prototype.draw = function() { strokeWeight(2); // 根据状态依次判断渲染逻辑 if (this.isMatch) { // 匹配成功的瓦片,保持显示正面 fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(this.face, this.x, this.y, this.width, this.width); } else if (this.isFaceUp) { // 已翻面的瓦片,显示正面 fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(this.face, this.x, this.y, this.width, this.width); } else if (this.isHovered) { // hover状态的瓦片,显示你自定义的样式 fill(74, 122, 128); rect(this.x, this.y, this.width, this.width, 10); image(getImage("avatars/leaf-yellow"), this.x, this.y, this.width, this.width); } else { // 默认未翻面状态 fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(getImage("avatars/leaf-green"), this.x, this.y, this.width, this.width); } };
3. 修复mouseMoved函数,仅更新状态而非直接绘制
原来的mouseMoved有两个问题:循环条件越界(i <= tiles.length)、调用不存在的drawFaceUp()/drawFaceDown()方法。现在我们改成只更新瓦片的isHovered状态:
mouseMoved = function() { // 先重置所有瓦片的hover状态 for (var i = 0; i < tiles.length; i++) { tiles[i].isHovered = false; } // 找到鼠标下方的瓦片,设置hover状态(仅未匹配、未翻面的瓦片生效) for (var i = 0; i < tiles.length; i++) { var tile = tiles[i]; if (tile.isUnderMouse(mouseX, mouseY) && !tile.isFaceUp && !tile.isMatch) { tile.isHovered = true; } } // 触发重绘,让draw()函数渲染最新状态 loop(); };
4. 清理冗余代码
删掉原来的Tile.prototype.hoverState()方法,因为我们已经把hover的绘制逻辑整合到draw()里了,不需要单独的方法。
为什么这样能解决问题?
现在所有的绘制逻辑都由draw()函数统一管理,mouseMoved()只负责更新瓦片的状态标记。每一帧draw()都会根据最新的状态(包括hover)重新绘制所有元素,再也不会出现“刚绘制的hover效果被下一帧清空”的情况了。
完整修改后的代码
var Tile = function(x, y, face) { this.x = x; this.y = y; this.width = 70; this.face = face; this.isFaceUp = false; this.isMatch = false; this.isHovered = false; // 新增hover状态标记 }; Tile.prototype.draw = function() { strokeWeight(2); if (this.isMatch) { fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(this.face, this.x, this.y, this.width, this.width); } else if (this.isFaceUp) { fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(this.face, this.x, this.y, this.width, this.width); } else if (this.isHovered) { fill(74, 122, 128); rect(this.x, this.y, this.width, this.width, 10); image(getImage("avatars/leaf-yellow"), this.x, this.y, this.width, this.width); } else { fill(214, 247, 202); rect(this.x, this.y, this.width, this.width, 10); image(getImage("avatars/leaf-green"), this.x, this.y, this.width, this.width); } }; Tile.prototype.isUnderMouse = function(x, y) { return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.width; }; // Global config var NUM_COLS = 5; var NUM_ROWS = 4; // Declare an array of all possible faces var faces = [ getImage("avatars/leafers-seed"), getImage("avatars/leafers-seedling"), getImage("avatars/leafers-sapling"), getImage("avatars/leafers-tree"), getImage("avatars/leafers-ultimate"), getImage("avatars/marcimus"), getImage("avatars/mr-pants"), getImage("avatars/mr-pink"), getImage("avatars/old-spice-man"), getImage("avatars/robot_female_1") ]; // Make an array which has 2 of each, then randomize it var possibleFaces = faces.slice(0); var selected = []; for (var i = 0; i < (NUM_COLS * NUM_ROWS) / 2; i++) { var randomInd = floor(random(possibleFaces.length)); var face = possibleFaces[randomInd]; selected.push(face); selected.push(face); possibleFaces.splice(randomInd, 1); } // Now shuffle the elements of that array var shuffleArray = function(array) { var counter = array.length; while (counter > 0) { var ind = Math.floor(Math.random() * counter); counter--; var temp = array[counter]; array[counter] = array[ind]; array[ind] = temp; } }; shuffleArray(selected); // Create the tiles var tiles = []; for (var i = 0; i < NUM_COLS; i++) { for (var j = 0; j < NUM_ROWS; j++) { var tileX = i * 78 + 10; var tileY = j * 78 + 40; var tileFace = selected.pop(); tiles.push(new Tile(tileX, tileY, tileFace)); } } background(255, 255, 255); var numTries = 0; var numMatches = 0; var flippedTiles = []; var delayStartFC = null; mouseClicked = function() { for (var i = 0; i < tiles.length; i++) { var tile = tiles[i]; if (tile.isUnderMouse(mouseX, mouseY)) { if (flippedTiles.length < 2 && !tile.isFaceUp) { tile.isFaceUp = true; flippedTiles.push(tile); if (flippedTiles.length === 2) { numTries++; if (flippedTiles[0].face === flippedTiles[1].face) { flippedTiles[0].isMatch = true; flippedTiles[1].isMatch = true; flippedTiles.length = 0; numMatches++; } delayStartFC = frameCount; } } loop(); } } }; draw = function() { background(255, 255, 255); if (delayStartFC && (frameCount - delayStartFC) > 30) { for (var i = 0; i < tiles.length; i++) { var tile = tiles[i]; if (!tile.isMatch) { tile.isFaceUp = false; } } flippedTiles = []; delayStartFC = null; noLoop(); } for (var i = 0; i < tiles.length; i++) { tiles[i].draw(); } if (numMatches === tiles.length/2) { fill(0, 0, 0); textSize(20); text("You found them all in " + numTries + " tries!", 20, 375); } }; noLoop(); mouseMoved = function() { // 重置所有hover状态 for (var i = 0; i < tiles.length; i++) { tiles[i].isHovered = false; } // 标记鼠标下的瓦片为hover状态 for (var i = 0; i < tiles.length; i++) { var tile = tiles[i]; if (tile.isUnderMouse(mouseX, mouseY) && !tile.isFaceUp && !tile.isMatch) { tile.isHovered = true; } } loop(); };
内容的提问来源于stack exchange,提问作者Graham Billington
相关产品推荐
相关产品推荐

