Struggling with the performance of my JavaScript canvas map script

Below is the script I've written for a prototype that utilizes 128x128 tiles to create a map on a canvas that can be dragged around by the user.

The script is functioning, but I'm facing some challenges: 1. Poor performance that needs to be addressed. 2. Needing a method to buffer the tiles before drawing them. 3. Any other suggestions to improve the overall performance would be greatly appreciated.

Here's a breakdown of the script:

Variables:

coordinates: Specifies the images to be displayed. Image file names follow the pattern '0_1.jpg', where 0 represents Y and 1 represents X.

mouse_position: Tracks the position of the mouse.

position: Poorly named variable that determines the context's position on the canvas, changing as the user drags the view.


Any help or guidance is highly appreciated. Thank you.

var coordinates = [0, 0];
var mouse_position = [0, 0];
var position = [-128, -128];

var canvas = document.getElementById('map_canvas');
var context = canvas.getContext('2d');

var buffer = [];
var buffer_x = Math.floor(window.innerWidth/128)+4;
var buffer_y = Math.floor(window.innerHeight/128)+4;


var animation_frame_request = function() {
    var a = window.requestAnimationFrame;
    var b = window.webkitRequestAnimationFrame;
    var c = window.mozRequestAnimationFrame;
    var d = function(callback) {
        window.setTimeout(callback, 1000/60);
    }

    return a || b || c || d;
}





var resizeCanvas = function() {
    window.canvas.width = window.innerWidth;
    window.canvas.height = window.innerHeight;


    window.buffer_x = Math.floor(window.innerWidth/128)+4;
    window.buffer_y = Math.floor(window.innerHeight/128)+4;


    window.buffer = [];

    for (row = 0; row < window.buffer_y; row++) {
        x = [];
        for (col = 0; col < window.buffer_x; col++) {
            x.push(new Image());
        }

        window.buffer.push(x);
    }
}




var render = function() {
    animation_frame_request(render);

    for (row = 0; row < window.buffer_y; row++) {
        for (col = 0; col < window.buffer_x; col++) {
            cy = window.coordinates[1]+row;
            cx = window.coordinates[0]+col;
            window.buffer[row][col].src =  'map/'+cy+'_'+cx+'.jpg';
        }
    }

    for (row = 0; row < window.buffer_y; row++) {
        for (col = 0; col < window.buffer_x; col++) {
            window.context.drawImage(window.buffer[row][col],
                                     window.position[0]+col*128,
                                     window.position[1]+row*128, 128, 128);
        }
    }
}




var events = function() {

    window.canvas.onmousemove = function(e) {

        if (e['buttons'] == 1) {

            window.position[0] += (e.clientX-window.mouse_position[0]);
            window.position[1] += (e.clientY-window.mouse_position[1]);

            if (window.position[0] >= 0) {
                window.position[0] = -128;
                window.coordinates[0] -= 1;
            } else if (window.position[0] < -128) {
                window.position[0] = 0;
                window.coordinates[0] += 1;
            }

            if (window.position[1] >= 0) {
                window.position[1] = -128;
                window.coordinates[1] -= 1;
            } else if (window.position[1] < -128) {
                window.position[1] = 0;
                window.coordinates[1] += 1;
            }

            render();
        }

        window.mouse_position[0] = e.clientX;
        window.mouse_position[1] = e.clientY;
    }
}


window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('load', resizeCanvas, false);
window.addEventListener('mousemove', events, false);

resizeCanvas();

Answer №1

If you want to optimize performance, it's recommended to avoid changing the source attribute of image nodes and instead, move them around within the layout.

One efficient way to minimize manipulation of image nodes (except for screen positioning) is by implementing an LRU (Least Recently Used) cache.

Essentially, you maintain a cache of the last 100 image nodes (enough to cover at least one screen) by using a dictionary to map the image source URL to a node object and organizing them in a doubly-linked list.

When a tile is needed, you check the cache first. If the tile is present, you move it to the front of the LRU list and update the image coordinates. If not, you create a new node, set the source, or if the cache limit is reached, reuse the last node in the linked list. This can be achieved through the following code snippet:

function setTile(x, y, src) {
    // code implementation here
}

Additionally, adding the requested tile to the container ensures that it remains in front of other tiles without requiring removal of old tiles.

For updating the screen, iterate over the necessary tiles and request them using the following function:

function setView(x0, y0) {
    // code implementation here
}

Most of the time, the setTile function will update the coordinates of an existing image tag without altering anything else. Additionally, the number of image nodes on the screen will not exceed MAXCACHE.

You can explore a working example at:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for updating the state in React once all the fetch requests inside a loop have been successfully completed

Currently, I am working on a React application where I am facing an issue with setState in my App. I want to set the state only after all the AJAX fetches have been resolved to prevent multiple unnecessary re-rendering. Here is what I have attempted so fa ...

Encountering an issue with setting up Pinia in Vue3, as it is showing an error message stating "

I am facing a challenge with my Vue app where the normal reactive store is not working correctly and does not retain the values I have set. Hence, I would like to switch to a Pinia store. After installing Pinia, my main.js file looks like this: import { cr ...

Rendering images from Laravel's storage folder in a React component

When uploading an image from a React frontend to a Laravel backend, I encountered an issue where the uploaded image path was saved in the database but the image wouldn't display. React code ` useEffect(() => { axiosClient .g ...

Displaying a custom error page in Next.js with the appropriate status code

I recently implemented a custom error page following the instructions provided in the documentation. My goal was to use this error page for specific errors that may occur during the getStaticProps function. Here's how I structured it: const Page: Next ...

Difficulty establishing a connection between NodeJS and Google Drive API

I am currently facing a challenge in my NodeJS application where I'm attempting to set up a gallery page. Despite having all the necessary configurations and connections with Google Drive API, something seems amiss when accessing the /gallery route. T ...

What do you want to know about Angular JS $http request?

My goal is to send a request using $http with angular js in order to retrieve a json object from google maps. $http.get('http://maps.googleapis.com/maps/api/geocode/json?address=' + data[ 'street' ] + ',' + data[ 'city&a ...

How can I effectively utilize executeScript in Selenium and webdriver.io?

I'm currently working on testing a basic form using Selenium, WebDriver.io, and Node.js (with Mocha). Here is a snippet of the code I have: var webdriverio = require('webdriverio'); var expect = require('expect'); describe(' ...

When the next button is clicked, the button content disappears

I am struggling with a problem involving two buttons that store tables. The issue is, I want the table to disappear when the second button is clicked and show the contents of the second button immediately after clicking it once, rather than having to click ...

Retrieving information through a jQuery ajax call

I've been working on creating an AJAX filter for a car list, but I've hit a roadblock in the final stage. My setup involves two files: index.php and filter.php. Within index.php, I have a form with dropdown lists and sliders. Here's the cod ...

What is the best way to accomplish this in Next.js?

I am working on a NextJs application and I need to include a new route /cabinet with its own navigation. The challenge is that the application already has a navigation bar defined in _app.js. Is it possible to create a similar setup like _app.js for /cab ...

Tips for establishing a fixed point at which divs cease to shrink as the browser size decreases

There are numerous dynamically designed websites where divs or images shrink as the browser size decreases. A great example of this is http://en.wikipedia.org/wiki/Main_Page The div containing the text shrinks proportionally to the browser size until it ...

Retrieving HTML Content with Ajax

Currently using this script for an assignment in my class. Still learning! The script checks whether a site is down or not. I want to expand this script to display statuses for each website in the table without duplicating the script. Any suggestions on ...

Error in Browserify Express App: Unexpected token while parsing the file

I have been attempting to browserify a javascript file. When I run the command: browserify global.js -o bundle.js An error message is returned: Error: Parsing file C:\ocquiz\public\javascripts\global.js: Unexpected token (756 ...

Using Golang to encode JSON for parsing in JavaScript

I am working with a struct that looks like this: type User struct { Login string `json:",string"` PasswordNonce Nonce `json:",string"` PasswordHash HashValue `json:",string"` CreatedOn time.Time `json:",string"` Email ...

Animation glitch when clicking on hamburger menu symbol

I am facing an issue where, in mobile window mode, if I click on the burger icon and then zoom out to a larger resolution and zoom back in to mobile size, clicking on the burger icon does not activate the slide-down menu as expected. You can see the issue ...

Is it possible to extract a user's password from the WordPress database for use in a React application

Is it feasible to create a React application integrated with Postgres within Wordpress that operates on MySql? I am interested in leveraging Wordpress's built-in features for theming, posts, and user management accounts. However, I am unsure if I can ...

Is there a way to deactivate the Edge mini menu while selecting text in a React application?

I have recently been working on a React web application and managed to create a specialized text selection menu. However, I am facing a challenge in programmatically disabling the default text selection mini menu in React. The image attached illustrates bo ...

Enhance the functionality of AngularJS (Restangular) by encapsulating service methods with a

Initially, when using basic $http, I had this code snippet in a service: var fetchSomeData = function() { var deferred = $q.defer(); $timeout(function() { $http.get('...mylongurl', { headers: { 'Content-Type& ...

Querying specific data from the API using unique identifiers

If the api.football-data.org/v1/competitions holds this data: { "_links": { "teams": { "href": "http://api.football-data.org/v1/competitions/444/teams" } }, "id": 444, "caption": "Campeonato Brasileiro da Série A", ...

When I changed the encoding of a live texture to sRGB in Three.js, the frame rate dropped by fifty percent

I am currently working on a threejs application that requires updating a texture in each frame. The output encoding of the THREE.WebGLRenderer is set to sRGB. Upon setting the texture encoding to sRGB, I observed that the rendering result is accurate. How ...