简体   繁体   English

我的JavaScript画布地图脚本和性能不佳

[英]My javascript canvas map script and poor performance

Basically below is my script for a prototype which uses 128x128 tiles to draw a map on a canvas which user can drag to move around. 基本上,以下是我的原型脚本,该脚本使用128x128瓦片在画布上绘制地图,用户可以拖动该地图来移动。

Script does work. 脚本确实起作用。 However I have a few problems to be solved: 1. Poor performance and I can't figure out why. 但是,我有一些问题需要解决:1.性能不佳,我不知道为什么。 2. I am missing a method to buffer the tiles before the actual drawing. 2.我缺少在实际绘制之前缓冲图块的方法。 3. If you notice any other issues also that could help me to make things run more smoothly it would be fantastic. 3.如果您发现任何其他问题也可以帮助我使事情更顺利进行,那就太好了。

Some explanations for the script: 脚本的一些解释:

variables 变量

coordinates - Defines the actual images to be displayed. 坐标 -定义要显示的实际图像。 Image file names are type of '0_1.jpg', where 0 is Y and 1 is X. 图像文件名的类型为“ 0_1.jpg”,其中0为Y,1为X。

mouse_position - As name says, is keeping record of mouse position. mouse_position-顾名思义 ,正在记录鼠标位置。

position - This is a poorly named variable. 位置 -这是一个名称不正确的变量。 It defines the position of the context drawn on canvas. 它定义了在画布上绘制的上下文的位置。 This changes when user drags the view. 当用户拖动视图时,此更改。


Any assistance would be appreciated greatly. 任何帮助将不胜感激。 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();

To get better performance you should avoid changing the src of img nodes and move them around instead. 为了获得更好的性能,您应该避免更改img节点的src ,而应将它们移动。

A simple way to minimize the number of img nodes handled and modified (except for screen positioning) is to use an LRU (Least Recently Used) cache. 最小化处理和修改的img节点数量(屏幕定位除外)的简单方法是使用LRU(最近最少使用)缓存。

Basically you keep a cache of last say 100 image nodes (they must be enough to cover at least one screen) by using a dictionary mapping the src url to a node object and also keeping them all in a doubly-linked list. 基本上,通过使用将src url映射到节点对象的字典,并将它们全部保存在双向链接列表中,可以保留最近说的100个图像节点(它们必须足以覆盖至少一个屏幕)的缓存。

When a tile is required you first check in the cache, and if it's already there just move it to the front of LRU list and move the img coordinates, otherwise create a new node and set the source or, if you already hit the cache limit, reuse the last node in the doubly-linked list instead. 当需要切片时,首先检查缓存,如果已经存在,则将其移至LRU列表的最前面并移动img坐标,否则创建一个新节点并设置源,或者如果您已经达到缓存限制,而是重用双向链接列表中的最后一个节点。 In code: 在代码中:

function setTile(x, y, src) {
    var t = cache[src];
    if (!t) {
        if (cache_count == MAXCACHE) {
            t = lru_last;
            t.prev.next = null;
            lru_last = t.prev;
            t.prev = t.next = null;
            delete cache[t.src]
            t.src = src;
            t.img.src = src;
            cache[t.src] = t;
        } else {
            t = { prev: null,
                  next: null,
                  img: document.createElement("img") };
            t.src = src;
            t.img.src = src;
            t.img.className = "tile";
            scr.appendChild(t.img);
            cache[t.src] = t;
            cache_count += 1;
        }
    } else {
        if (t.prev) t.prev.next = t.next; else lru_first = t.next;
        if (t.next) t.next.prev = t.prev; else lru_last = t.prev;
    }
    t.prev = null; t.next = lru_first;
    if (t.next) t.next.prev = t; else lru_last = t;
    lru_first = t;

    t.img.style.left = x + "px";
    t.img.style.top = y + "px";
    scr.appendChild(t.img);
}

I'm also always appending the requested tile to the container so that it goes in front of all other existing tiles; 我还总是将请求的图块附加到容器中,以便它位于所有其他现有图块的前面; this way I don't need to remove old tiles and they're simply left behind. 这样,我就不需要删除旧的图块,而将它们简单地留下了。

To update the screen I just iterate over all the tiles I need and request them: 要更新屏幕,我只需遍历我需要的所有图块并请求它们:

function setView(x0, y0) {
    var w = scr.offsetWidth;
    var h = scr.offsetHeight;
    var iy0 = y0 >> 7;
    var ix0 = x0 >> 7;
    for (var y=iy0; y*128 < y0+h; y++) {
        for (var x=ix0; x*128 < x0+w; x++) {
            setTile(x*128-x0, y*128-y0, "tile_" + y + "_" + x + ".jpg");
        }
    }
}

most of the time the setTile request will just update the x and y coordinates of an existing img tag, without changing anything else. 在大多数情况下, setTile请求将仅更新现有img标签的xy坐标,而不更改其他任何内容。 At the same time no more than MAXCACHE image nodes will be present on the screen. 同时,屏幕上最多MAXCACHE图像节点。

You can see a full working example in 您可以在中看到完整的工作示例

http://raksy.dyndns.org/tiles/tiles.html http://raksy.dyndns.org/tiles/tiles.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM