Canvas下落图片闪烁问题求助:Image资源加载与绘制优化
Hey there! Let's work through your problem step by step—you're totally on the right track with figuring out that the draw() function was causing the flicker, so let's build on that.
Why the Flicker Happened
In your original code, you were creating a new Image object every single time draw() ran for each rectangle. This meant the browser had to reload the image from scratch every frame, and while it's loading, drawImage() would render nothing (a blank spot). That constant blank-loaded-blank cycle is exactly what causes the flicker you're seeing.
The Fix: Preload Images + Per-Instance Image References
We need two key changes to fix this:
- Preload all your game images once at the start, so they're ready to use whenever we need them.
- Let each rectangle instance hold its own reference to the correct preloaded image, so we can keep the randomness and avoid reloading.
Step 1: Preload Your Images First
Add this code somewhere before you create any rectangle instances (like in your game initialization):
// Store preloaded images in a single object for easy access const loadedImages = {}; function preloadGameImages() { // List all the image keys you need from your imagesSrc object const imageNames = ['bulletinYes', 'bulletinNo']; imageNames.forEach(name => { const img = new Image(); img.src = imagesSrc[name]; loadedImages[name] = img; }); } // Run this once when your game starts preloadGameImages();
This loads each image exactly once, so we never have to re-request them during gameplay.
Step 2: Update Your rect Class (Constructor + Draw Method)
Now modify your rectangle code to use the preloaded images, and give each instance its own image reference:
// Note: Conventionally, constructor functions start with a capital letter function Rect() { this.size = [rectSize.x, rectSize.y]; // Pick random image source for this instance this.imageKey = rand(0, 1) ? 'bulletinYes' : 'bulletinNo'; // Grab the PRELOADED image for this instance this.bulletin = loadedImages[this.imageKey]; this.position = [rand(0, w - rectSize.x), -rectSize.y]; this.bulletinValue = this.imageKey === 'bulletinYes' ? 'bulletinYesValue' : 'bulletinNoValue'; } Rect.prototype = { draw: function() { // Use the preloaded image stored on this instance—no more reloading! // (Fixed your index bug here too: you had position[2] instead of position[1]) ctx.drawImage(this.bulletin, this.position[0], this.position[1], this.size[0], this.size[1]); }, // Optional: Add a method if you ever need to switch the image dynamically switchImage: function() { this.imageKey = this.imageKey === 'bulletinYes' ? 'bulletinNo' : 'bulletinYes'; this.bulletin = loadedImages[this.imageKey]; this.bulletinValue = this.imageKey === 'bulletinYes' ? 'bulletinYesValue' : 'bulletinNoValue'; } };
Why This Works
- No more flicker: We're using images that were loaded once at game start, so
drawImage()always has a fully loaded image to render. - Random per-instance images: Each new
Rectpicks its own random image key when it's created, so you still get the random behavior you want. - Dynamic switching: If you ever need to change an instance's image mid-game, just call
rectInstance.switchImage()—it'll grab the preloaded image instantly without reloading.
Quick Fix for Your Earlier Attempt
When you moved the bulletin variable outside draw(), you ran into two issues:
this.imagesSrcdoesn't exist yet when that code runs (the constructor hasn't been called for any instances).- All instances would share that single global
bulletinimage, so you couldn't have random images per rectangle.
The per-instancethis.bulletinreference fixes both of these problems.
内容的提问来源于stack exchange,提问作者YoWuzzup

