You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas图片无法渲染问题求助:demo.js加载图片后控制台提示成功但画面不显示

Canvas图片无法渲染问题求助:demo.js加载图片后控制台提示成功但画面不显示

我最近在做一个简单的JavaScript模拟游戏,把代码拆成了两个文件:script.js负责处理启动界面的过渡逻辑(比如点击Start后的文本闪烁、屏幕淡出、锁定点击、隐藏光标,大概4秒后加载游戏场景),相当于"游戏引擎";demo.js则负责存放游戏场景的数据,比如加载背景图片。

现在遇到了一个奇怪的问题:当我在demo.js里尝试加载背景图片时,控制台能正常打印图片加载完成的日志,但Canvas上就是看不到背景;可如果把同样的加载代码放到script.js里,背景就能正常渲染出来。我确认过文件路径是对的,实在搞不懂哪里出问题了,想请大家帮忙看看!

先给大家说下我的逻辑:

  • 启动界面在script.js里管理,点击Start后完成过渡效果,然后加载demo.js的内容
  • demo.js负责游戏场景的内容,比如加载背景图片

script.js

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const testModeButton = document.getElementById('TestMode');

// Canvas setup
canvas.width = 600;
canvas.height = 600;
canvas.style.display = 'block';
canvas.style.margin = '0 auto';
canvas.style.background = 'rgba(0, 0, 0, 0.25)';

// Change mouse cursor
canvas.addEventListener('mouseenter', () => {
    canvas.style.cursor = 'url(file/mouse.cur), auto';
});
canvas.addEventListener('mouseleave', () => {
    canvas.style.cursor = 'auto';
});

// Test mode border on/off
let testModeBorder = false;
ctx.save();
ctx.globalAlpha = 0;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.strokeStyle = 'rgba(255, 99, 71, 0.8)';
ctx.lineWidth = 3;

// Test mode click event
testModeButton.addEventListener('click', function() {
    console.log('Test Mode clicked');
    testModeBorder = !testModeBorder;  // Toggle border drawing
    console.log('Draw border:', testModeBorder);
});

// Background
const titleBackgroundVideo = document.createElement('video');
titleBackgroundVideo.src = 'file/space.mp4';
titleBackgroundVideo.autoplay = true;
titleBackgroundVideo.loop = true;
titleBackgroundVideo.muted = true;

// Title logo
const titleImage = new Image();
titleImage.src = 'file/imp.png';
let titleImageWidth = 400, titleImageHeight = 250;
let titleImageX = (canvas.width - titleImageWidth) / 2, titleImageY = 60;

// Title menu text
let startText = 'Start';
let endText = 'Exit';
let showText = true;

// Function to draw text on the title menu
function drawText(titleText, positionY, color = 'white') {
    if (!showText) return;
    ctx.save();
    ctx.font = '30px Arial';
    ctx.fillStyle = color;
    
    const textWidth = ctx.measureText(titleText).width;
    const titleTextX = (canvas.width - textWidth) / 2;
    ctx.fillText(titleText, titleTextX, positionY);
    ctx.restore();
}

// Blinking text effect
let startBlinkInterval = null;
let endBlinkInterval = null;

function startBlinkingText(target, interval = 100) {
    let textBlinkInterval = setInterval(() => {
        if (target === 'start') {
            startText = startText === 'Start' ? '' : 'Start';
        } else if (target === 'end') {
            endText = endText === 'Exit' ? '' : 'Exit';
        }
    }, interval);
    return textBlinkInterval;
}

// Clickable area rectangle
const clickAreaWidth = 50;
const clickAreaHeight = 50;
const clickAreaX = (canvas.width - clickAreaWidth) / 2;
const clickAreaY = (canvas.height - clickAreaHeight) / 2;

// Draw clickable area
function drawClickArea(x, y, width, height) {
    ctx.save();
    ctx.fillStyle = 'rgba(255, 173, 173, 0)';
    ctx.fillRect(x, y, width, height);
    ctx.restore();
}

// Screen filter (used for fade effects)
let filterOn = false;
let alphaStart = 0;
let alphaEnd = 1;
let startTime = Date.now();
let frameCount = 0;
let filterSettings = { speed: 0.004 };

// Function to animate screen filter
function animateScreenFilter(x, y, width, height, start, end, callback) {
    filterOn = true;
    alphaStart = start;
    alphaEnd = end;

    function animate() {
        if (filterOn) {
            drawScreenFilter(x, y, width, height);
            requestAnimationFrame(animate);
        } else if (callback) {
            callback();
        }
    }

    animate();
}

// Rendering function
function renderTitleScreen() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(titleBackgroundVideo, 0, 0, canvas.width, canvas.height);

    if (testModeBorder) {
        ctx.strokeRect(0, 0, canvas.width, canvas.height);
    }
    
    ctx.drawImage(titleImage, titleImageX, titleImageY, titleImageWidth, titleImageHeight);
    drawText(startText, canvas.height / 2 + 90, 'white');
    drawClickArea(clickAreaX - 38, clickAreaY + 80, clickAreaWidth + 78, clickAreaHeight - 3);
    drawText(endText, canvas.height / 2 + 140, 'white');
    drawClickArea(clickAreaX - 38, clickAreaY + 130, clickAreaWidth + 78, clickAreaHeight - 3);
    requestAnimationFrame(renderTitleScreen);
}

// Mouse click lock function
function enableClickAfterDelay(canvas, delayInSeconds, cursorStyle = 'url(file/mouse.cur), auto') {
    setTimeout(() => {
        isClickDisabled = false;
        console.log('Click is enabled again.');
        canvas.style.cursor = cursorStyle;
        const event = new MouseEvent('mouseenter', { bubbles: true });
        canvas.dispatchEvent(event);
    }, delayInSeconds * 1000);
}

// Menu click event
canvas.addEventListener('click', function(event) {
    if (isClickDisabled) return;
    
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    if (
        mouseX >= clickAreaX - 38 &&
        mouseX <= clickAreaX - 38 + clickAreaWidth + 78 &&
        mouseY >= clickAreaY + 80 &&
        mouseY <= clickAreaY + 80 + clickAreaHeight - 3
    ) {
        console.log('Start Click Area clicked!');
        isClickDisabled = true;
        clearInterval(startBlinkInterval);
        startBlinkInterval = startBlinkingText('start', 100);
        canvas.style.cursor = 'none';
        enableClickAfterDelay(canvas, 3, 'pointer');
        filterSettings.speed = 0.003;
        animateScreenFilter(0, 0, canvas.width, canvas.height, 0, 1);
        titleBackgroundVideo.pause();
    }

    if (
        mouseX >= clickAreaX - 38 &&
        mouseX <= clickAreaX - 38 + clickAreaWidth + 78 &&
        mouseY >= clickAreaY + 130 &&
        mouseY <= clickAreaY + 130 + clickAreaHeight - 3
    ) {
        console.log('Exit Click Area clicked!');
        isClickDisabled = true;
        clearInterval(endBlinkInterval);
        endBlinkInterval = startBlinkingText('end', 100);
        canvas.style.cursor = 'none';
        filterOn = true;
        animateScreenFilter(0, 0, canvas.width, canvas.height, 0, 1);
        titleBackgroundVideo.pause();
        setTimeout(() => {
            window.close();
        }, 1850);
    }
});

requestAnimationFrame(renderTitleScreen);

demo.js

const img = new Image();

function mainScreen() {
    img.src = 'file/demo_back01.jpg';
    img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        console.log('The image has been loaded.');
    };
};

function showMainScreen() {
    mainScreen();
};

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="TestMod" style="color: gray; position: fixed;">MainTestMod</div>
    <canvas id="myCanvas"></canvas>
    <script src="startBar.js"></script>
    <script src="script.js"></script>
</body>
<style>
    body {
        background-color: rgb(63, 23, 126);
        margin: 0;
    }
</style>
</html>

style.css

html, body {
    height: 100%;
    overflow: hidden; /* Prevent scrolling */
}

body {
    background-color: black;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;  /* Set parent element to relative to overlap the canvas and menu bar */
}

canvas {
    width: 1080px;
    height: 100%;
    display: block;
    z-index: 1;  /* Ensure the canvas is below the menu bar */
}

#menu-bar {
    position: fixed;  /* Fixed at the top */
    top: 0;           /* Position at the top of the browser */
    left: 0;
    width: 100%;
    height: 6vh;      /* Occupy 6% of the top area */
    background-color: #c0c0c0;
    z-index: 2;
    opacity: 0;       /* Initially hidden */
    display: flex;    /* Arrange menu items horizontally */
    justify-content: flex-start; /* Align to the left */
    align-items: center; /* Vertically center */
    padding-left: 0; /* Add left padding */
    border: 2px outset white;
}

#menu-bar:hover {
    opacity: 1;       /* Make menu visible when hovered */
}

.menu-item {
    list-style: none;
    font-size: 16px;
    cursor: pointer;
    position: relative;
    margin-right: 30px; /* Space between menu items */
    padding: 0 0 6px 0;
    display: inline-block; /* Arrange items horizontally */
}

.submenu {
    visibility: hidden;     /* Hide submenu by default */
    opacity: 0;
    position: absolute;
    top: 100%;
    left: 0;
    background-color: #c0c0c0;
    border: 2px outset white;
    padding: 5px 0;
    z-index: 3;
    transition: opacity 0.2s ease;
}

我现在怀疑是不是demo.js里的ctx或者canvas变量作用域的问题?或者是renderTitleScreen()的动画循环一直在清空画布,把demo.js画的背景覆盖掉了?希望有大佬能帮我找出问题所在,谢谢啦!


备注:内容来源于stack exchange,提问作者Da.D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:38:12