Da.D
Da.D

Reputation: 1

Unable to render the image on canvas

I am trying to create a simple game using JavaScript. I do JavaScript as a hobby, and I did not major in computer. I used ChatGPT's help to write my code, so it might be unnecessarily large and messy.

I will briefly explain my current code. I am trying to make a simple simulation game. On the first screen, when the user clicks "Start", the following effects are implemented: blinking text, fade-out, click lock, and hiding the mouse cursor. After about 4 seconds, the necessary code for the next screen is loaded.

I manage this main screen with script.js, and once the main screen transitions, I plan to manage the game with another file called demo.js. To put it simply, script.js works like the game engine, while demo.js contains the game data.

The problem I encountered is that when I try to load the background image in demo.js, the console log correctly prints a message indicating that the image has loaded, but the background does not actually render.

However, when I load the background inside script.js, it renders correctly. So, I don't think it's an issue with the folder path.

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%;              /* Position submenu below parent menu item */
    left: 0;
    background-color: #c0c0c0;
    padding: 0;
    border: 2px outset white;
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    z-index: 3;
}

.submenu.open {
    visibility: visible;
    opacity: 1;
}

.menu-item:hover .submenu {
    visibility: visible;
    opacity: 1;
}

/* Hide menu items */
.menu-item.hidden {
    display: none !important; /* Completely hide menu item */
}

.submenu li {
    padding: 10px;
    cursor: pointer;
    white-space: nowrap;  /* Prevent line breaks */
    list-style-type: none;
}

.submenu li:hover {
    background-color: #000082;
    color: white;
}

/* Default popup style */
.popup {
    display: none; /* Hidden by default */
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    z-index: 10; /* Display above the top menu */
}

.popup-content {
    display: flex;
    background-color: #c0c0c0;
    border: 2px outset white;
    padding: 20px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Popup shadow effect */
    width: 50%;
    max-width: 800px;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

.popup-left {
    flex: 1;
    padding-right: 20px;
}

.popup-left img {
    width: 100%;
    height: auto;
    border-radius: 5px;
}

.popup-right {
    flex: 2;
}

.popup-right p {
    font-size: 16px;
    line-height: 1.5;
}

When I tried this code in script.js, it rendered properly. So I don't think it's a problem with the folder path.

Upvotes: -2

Views: 78

Answers (2)

chrwahl
chrwahl

Reputation: 13145

Maybe the image is already loaded when you set the onload function. You need to set the src property after the event listener for the load event.

Here I add a data URI with an SVG image -- in your case a path like file/demo_back01.jpg.

const canvas = document.getElementById('canvas01');
const ctx = canvas.getContext('2d');

mainScreen();

function mainScreen() {
  // create image
  let img = new Image();
  // add event listener (or set the property img.onload)
  img.addEventListener('load', e => {
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    console.log('The image has been loaded.');
  });
  // set the source of the image
  img.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8Y2lyY2xlIGZpbGw9Im9yYW5nZSIgY3g9IjUwIiBjeT0iNTAiIHI9IjUwIiAvPgo8L3N2Zz4=';
};
<canvas id="canvas01" width="200" height="200"></canvas>

Upvotes: 0

Ray Wallace
Ray Wallace

Reputation: 1942

In mainScreen() you forgot to create the img. So add the line:

let img = new Image()

The reason you don't see your background image is that as soon as you draw it, your requestAnimationFrame() code draws your video frame over top of it.

For debug/testing purposes; If you comment out your requestAnimationFrame() in the last line of script.js, then call showMainScreen(), you will see your background image.

So when you want to see your background image, you need to stop your requestAnimationFrame() loop from running (or stop the code from drawing to the canvas).

Upvotes: 0

Related Questions