Mapbox GL JS: Export map to PNG or PDF?

There are two main questions:

1. How do I get the map canvas as an image?

Actually, you are doing the right thing, but just too early. Give that map some time to load and fetch the image data when the load event is triggered:

map.on('load', () => console.log(map.getCanvas().toDataURL()));

2. How do I get that image in high-res?

By changing window.devicePixelRatio according to your destination dpi, you can trick your browser into generating high-res output. I found that solution in an implementation created by Matthew Petroff, see his code on https://github.com/mpetroff/print-maps. This is the trick he's using for generating high-res output:

Object.defineProperty(window, 'devicePixelRatio', {
    get: function() {return dpi / 96}
});

Source


I created a simple working example for anybody stumbling upon this thread:

(Thanks @Vic for pointing out the preserveDrawingBuffer-option in Mapbox GL JS)

<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8' />
    <title>Display a map</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css' rel='stylesheet' />
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <style>
    #map {
        margin: auto;
        width: 400px;
        height: 400px;
    }
    </style>
</head>

<body>
    <div id='map'></div>
    <a id="downloadLink" href="" download="map.png">Download ↓</a>
    <div id="image"></div>
    <script>
    mapboxgl.accessToken = 'your-token-here';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/streets-v9',
        center: [-74.50, 40],
        zoom: 9,
        preserveDrawingBuffer: true
    });

    $('#downloadLink').click(function() {
        var img = map.getCanvas().toDataURL('image/png')
        this.href = img
    })
    </script>
</body>

</html>