Mapbox 自定义图层 WebGL

Mapbox Custom Layer WebGL

我想知道如何在 Mapbox GL JS 中切换 webgl 层。
当我在下面的例子中调用 console.log(map.getStyle()) 时。新的自定义层(突出显示)没有显示,所以我无法在调用 map.removeLayer('highlight') 时删除该层。然而,该图层确实出现在 console.log(map.style.sourceCaches)

我不确定如何删除图层以创建切换按钮。我查看了 Mapbox 文档。这可能很简单,但我就是不明白...

此外,有没有办法控制自定义纹理层的不透明度?

<html>
<head>
<meta charset="utf-8" />
<title>Add a custom style layer</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel="stylesheet" />
<style>
    body { margin: 0; padding: 0; }
    #map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
    mapboxgl.accessToken = 'pk.eyJ1Ijoia29ycmlhIiwiYSI6ImNpeHdpeHR5aDAwMjQycXFxcDZ2amQ2cjIifQ.mMba8iFMCiuoL7hOmfKjwQ';
var map = (window.map = new mapboxgl.Map({
container: 'map',
zoom: 3,
center: [7.5, 58],
style: 'mapbox://styles/mapbox/light-v10',
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
}));
 
// create a custom style layer to implement the WebGL content
var highlightLayer = {
id: 'highlight',
type: 'custom',
 
// method called when the layer is added to the map
// https://docs.mapbox.com/mapbox-gl-js/api/#styleimageinterface#onadd
onAdd: function (map, gl) {
// create GLSL source for vertex shader
var vertexSource =
'' +
'uniform mat4 u_matrix;' +
'attribute vec2 a_pos;' +
'void main() {' +
'    gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);' +
'}';
 
// create GLSL source for fragment shader
var fragmentSource =
'' +
'void main() {' +
'    gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);' +
'}';
 
// create a vertex shader
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
 
// create a fragment shader
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
 
// link the two shaders into a WebGL program
this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program);
 
this.aPos = gl.getAttribLocation(this.program, 'a_pos');
 
// define vertices of the triangle to be rendered in the custom style layer
var helsinki = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 25.004,
lat: 60.239
});
var berlin = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 13.403,
lat: 52.562
});
var kyiv = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 30.498,
lat: 50.541
});
 
// create and initialize a WebGLBuffer to store vertex and color data
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
helsinki.x,
helsinki.y,
berlin.x,
berlin.y,
kyiv.x,
kyiv.y
]),
gl.STATIC_DRAW
);
},
 
// method fired on each animation frame
// https://docs.mapbox.com/mapbox-gl-js/api/#map.event:render
render: function (gl, matrix) {
gl.useProgram(this.program);
gl.uniformMatrix4fv(
gl.getUniformLocation(this.program, 'u_matrix'),
false,
matrix
);
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.enableVertexAttribArray(this.aPos);
gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
}
};
 
// add the custom style layer to the map
map.on('load', function () {
map.addLayer(highlightLayer, 'building');
});
</script>
 
</body>
</html>

基本上你需要将图层可见性设置为none,所以图层将停止渲染:

map.setLayoutProperty('highlight', 'visibility', 'none');

自定义层(基于 CustomLayerInterface)有一个警告,即尽管它们是可见的,但默认情况下它们没有可见性预设,因此您必须将其明确设置为可见首先,在 map.addLayer 创建之后。然后您可以使用相同的方法将图层从 visible 切换到 nonesetLayoutProperty

在您的代码中添加一个 link 按钮和方法将像 this fiddle I have drafted with your sample 一样工作。

代码如下...

<html>
<head>
    <meta charset="utf-8" />
    <title>Add a custom style layer</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <script src="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js"></script>
    <link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel="stylesheet" />
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
        #menu {
            background: #fff;
            position: absolute;
            z-index: 1;
            top: 10px;
            right: 10px;
            border-radius: 3px;
            width: 120px;
            border: 1px solid rgba(0, 0, 0, 0.4);
            font-family: 'Open Sans', sans-serif;
        }

            #menu a {
                font-size: 13px;
                color: #404040;
                display: block;
                margin: 0;
                padding: 0;
                padding: 10px;
                text-decoration: none;
                border-bottom: 1px solid rgba(0, 0, 0, 0.25);
                text-align: center;
            }

                #menu a:last-child {
                    border: none;
                }

                #menu a:hover {
                    background-color: #f8f8f8;
                    color: #404040;
                }

                #menu a.active {
                    background-color: #3887be;
                    color: #ffffff;
                }

                    #menu a.active:hover {
                        background: #3074a4;
                    }

    </style>
</head>
<body>
    <div id="map"></div>
    <nav id="menu">
        <a href="#" class="active" onclick="toggleLayer(this)">highlight</a>
    </nav>
    <script>
        mapboxgl.accessToken = 'pk.eyJ1Ijoia29ycmlhIiwiYSI6ImNpeHdpeHR5aDAwMjQycXFxcDZ2amQ2cjIifQ.mMba8iFMCiuoL7hOmfKjwQ';
        var map = (window.map = new mapboxgl.Map({
            container: 'map',
            zoom: 3,
            center: [7.5, 58],
            style: 'mapbox://styles/mapbox/light-v10',
            antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
        }));

        // create a custom style layer to implement the WebGL content
        var highlightLayer = {
            id: 'highlight',
            type: 'custom',

            // method called when the layer is added to the map
            // https://docs.mapbox.com/mapbox-gl-js/api/#styleimageinterface#onadd
            onAdd: function (map, gl) {
                // create GLSL source for vertex shader
                var vertexSource =
                    '' +
                    'uniform mat4 u_matrix;' +
                    'attribute vec2 a_pos;' +
                    'void main() {' +
                    '    gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);' +
                    '}';

                // create GLSL source for fragment shader
                var fragmentSource =
                    '' +
                    'void main() {' +
                    '    gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);' +
                    '}';

                // create a vertex shader
                var vertexShader = gl.createShader(gl.VERTEX_SHADER);
                gl.shaderSource(vertexShader, vertexSource);
                gl.compileShader(vertexShader);

                // create a fragment shader
                var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
                gl.shaderSource(fragmentShader, fragmentSource);
                gl.compileShader(fragmentShader);

                // link the two shaders into a WebGL program
                this.program = gl.createProgram();
                gl.attachShader(this.program, vertexShader);
                gl.attachShader(this.program, fragmentShader);
                gl.linkProgram(this.program);

                this.aPos = gl.getAttribLocation(this.program, 'a_pos');

                // define vertices of the triangle to be rendered in the custom style layer
                var helsinki = mapboxgl.MercatorCoordinate.fromLngLat({
                    lng: 25.004,
                    lat: 60.239
                });
                var berlin = mapboxgl.MercatorCoordinate.fromLngLat({
                    lng: 13.403,
                    lat: 52.562
                });
                var kyiv = mapboxgl.MercatorCoordinate.fromLngLat({
                    lng: 30.498,
                    lat: 50.541
                });

                // create and initialize a WebGLBuffer to store vertex and color data
                this.buffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
                gl.bufferData(
                    gl.ARRAY_BUFFER,
                    new Float32Array([
                        helsinki.x,
                        helsinki.y,
                        berlin.x,
                        berlin.y,
                        kyiv.x,
                        kyiv.y
                    ]),
                    gl.STATIC_DRAW
                );
            },

            // method fired on each animation frame
            // https://docs.mapbox.com/mapbox-gl-js/api/#map.event:render
            render: function (gl, matrix) {
                gl.useProgram(this.program);
                gl.uniformMatrix4fv(
                    gl.getUniformLocation(this.program, 'u_matrix'),
                    false,
                    matrix
                );
                gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
                gl.enableVertexAttribArray(this.aPos);
                gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, 0, 0);
                gl.enable(gl.BLEND);
                gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
            }
        };


        // add the custom style layer to the map
        map.on('load', function () {
            map.addLayer(highlightLayer, 'building');
            map.setLayoutProperty('highlight', 'visibility', 'visible');
        });

        function toggleLayer(e) {
            var clickedLayer = e.textContent;

            var visibility = map.getLayoutProperty(clickedLayer, 'visibility');

            // toggle layer visibility by changing the layout object's visibility property
            if (visibility === 'visible') {
                map.setLayoutProperty(clickedLayer, 'visibility', 'none');
                this.className = '';

            } else {
                this.className = 'active';
                map.setLayoutProperty(clickedLayer, 'visibility', 'visible');
            }
        }
    </script>

</body>
</html>

如果这个答案解决了您的问题,请将其标记为已接受答案,这样它也会帮助其他用户知道这是正确的解决方案。