HTML5画布和图像使用内存泄漏/极端内存

时间:2018-03-11 14:46:59

标签: javascript canvas three.js

我参与了一个项目,可以在Potree中将地图投影到点云上。

我们正在从OpenStreetMap的tile API加载地图,并在用户放大点云时加载更多的图块,以便为用户提供更详细的体验。我们使用Three.js纹理将瓷砖数据从瓷砖投影到点云,我们从画布元素中提取瓷砖。

我们已经完成所有代码以从OSM获取切片,将它们切割成正确的大小并将它们放在画布中。对于较小的点云,该解决方案非常有效,但随着区域的增长,需要更多的图块/图像,并且画布元素变得更大(我们正在缩放画布以跟随缩放级别以提供更好的分辨率)我们使用大量内存会遇到麻烦(在任务管理器中大约10 GB,但后来我调查了chrome dev工具中的内存使用情况,内存使用率要低很多)。大内存使用将导致浏览器无响应并最终死亡。

我的问题是,在这段代码中是否存在某种内存泄漏,或者在使用图像和画布时必须非常小心?

代码:

Potree.MapTextureManager = class MapTextureManager {
constructor(projection, bbMin, bbMax) {
    this.projection = projection;
    this.bbMin = bbMin;
    this.bbMax = bbMax;
    this._mapCanvas = document.getElementById("texture");
    let ratio = (bbMax[0] - bbMin[0]) / (bbMax[1] - bbMin[1]);
    let minHeight = 256;
    this._mapCanvas.width = minHeight * ratio;
    this._mapCanvas.height = minHeight;
    this._minWeb = proj4(swiss, WGS84, [this.bbMin[0], this.bbMin[1]]);
    this._maxWeb = proj4(swiss, WGS84, [this.bbMax[0], this.bbMax[1]]);
    this.updateTexture(this._minWeb, this._maxWeb);
    this._cachedTexture = null;
    this._drawnImages = [];
    this.geometryNodeIds = new Set();
    this._cachedTileImages = [];
    this._currentMaxZoom = this.getTiles(this._minWeb, this._maxWeb)[0].zoom;
}

updateTextureFor(visibleNodes, matrixWorld) {
    visibleNodes.forEach(visibleNode => {
        if (!this.geometryNodeIds.has(visibleNode.geometryNode.id)) {
            this.geometryNodeIds.add(visibleNode.geometryNode.id);
            var swiss = proj4.defs("test");
            var WGS84 = proj4.defs("WGS84");
            let nodeBox = Potree.utils.computeTransformedBoundingBox(visibleNode.geometryNode.boundingBox, matrixWorld);
            let minWeb = proj4(swiss, WGS84, [nodeBox.min.x, nodeBox.min.y]);
            let maxWeb = proj4(swiss, WGS84, [nodeBox.max.x, nodeBox.max.y]);
            this.updateTexture(minWeb, maxWeb);
        }
    });
}

updateTexture(minWeb, maxWeb) {
    let canvasEl = this._mapCanvas;
    let tiles = this.getTiles(minWeb, maxWeb);
    let tilePromises = this.tilePromisesFor(tiles);
    tilePromises.forEach(tilePromise => {
        tilePromise.then(tileImage => {
            if (tileImage.tile.zoom > this._currentMaxZoom) {
                this.resizeCanvasTo(tileImage.tile.zoom);
            }
            this._cachedTileImages.push(tileImage);
            this._cachedTileImages.sort((tileImage1, tileImage2) => {
                if (tileImage1.tile.zoom >= tileImage2.tile.zoom) {
                    return 1;
                } else {
                    return -1;
                }
            });
            let myArray = this._cachedTileImages.filter((el) => !this._drawnImages.includes(el));
            myArray.forEach(tileImage => {
                // if (this._drawnImages.indexOf(tileImage) === -1) {
                this.drawTileOnCanvas(canvasEl, tileImage.image, tileImage.tile);
                this._drawnImages.push(tileImage);
                // }
            });
            if (this._cachedTexture) {
                this._cachedTexture.dispose();
                this._cachedTexture = null;
            }
        });
    });
}

get mapTexture() {
    if (this._cachedTexture) {
        return this._cachedTexture;
    }
    let texture = new THREE.CanvasTexture(this._mapCanvas);
    texture.minFilter = THREE.LinearFilter;
    texture.needsUpdate = true;
    this._cachedTexture = texture;
    return texture;
}

getTiles(minCoord, maxCoord, zoom = 1) {
    let maxZoom = 18;
    let minNumberOfTiles = 4;
    let minX = this.long2tile(minCoord[0], zoom);
    let minY = this.lat2tile(minCoord[1], zoom);
    let maxX = this.long2tile(maxCoord[0], zoom);
    let maxY = this.lat2tile(maxCoord[1], zoom);
    let arrayX = [minX, maxX].sort();
    let arrayY = [minY, maxY].sort();
    let tiles = [];
    for (var x = arrayX[0]; x <= arrayX[1]; x++) {
        for (var y = arrayY[0]; y <= arrayY[1]; y++) {
            tiles.push({ X: x, Y: y, zoom: zoom });
        }
    }

    // We want at least minNumberOfTiles tiles per pointcloud node
    if (tiles.length >= minNumberOfTiles || zoom === maxZoom) {
        return tiles;
    } else {
        return this.getTiles(minCoord, maxCoord, zoom + 1);
    }
}

tilePromisesFor(Tiles) {
    return Tiles.map(function (tile, i) {
        return new Promise((resolve, reject) => {
            let image = new Image(256, 256);
            image.crossOrigin = "Anonymous";
            image.onload = function () {
                let data = { tile: tile, image: image };
                resolve(data);
            }
            image.src = "https://tile.openstreetmap.org" + "/" + tile.zoom + "/" + tile.X + "/" + tile.Y + ".png";
        })
    });

}


drawTileOnCanvas(canvas, image, tile) {
    let ctx = canvas.getContext("2d");
    ctx.drawImage(image, sX, sY, imageWidthToBeDrawn, imageHeightToBeDrawn, dX, dY, drawingWidth, drawingHeight);
    image.src = "";
    image = null;
}

resizeCanvasTo(zoom) {
    let canvas = this._mapCanvas;
    let multiplier = Math.pow(2, zoom - this._currentMaxZoom);

    let ctx = canvas.getContext("2d");
    // create a temporary canvas obj to cache the pixel data //
    var temp_cnvs = document.createElement('canvas');
    var temp_cntx = temp_cnvs.getContext('2d');
    // set it to the new width & height and draw the current canvas data into it // 
    temp_cnvs.width = canvas.width * multiplier;;
    temp_cnvs.height = canvas.height * multiplier;;
    temp_cntx.drawImage(canvas, 0, 0);

    // resize & clear the original canvas and copy back in the cached pixel data //
    canvas.width = canvas.width * multiplier;
    canvas.height = canvas.height * multiplier;
    ctx.scale(multiplier, multiplier);
    ctx.drawImage(temp_cnvs, 0, 0);
    this._currentMaxZoom = zoom;
    temp_cnvs = null;
    temp_cntx = null;
        }
};

我试图删除一些不必要的代码。如果你遗失了什么,请说出来。

0 个答案:

没有答案