Tuesday, 17 August 2021

Why is animated MapBox map screaming on the CPU?

I have this map code for MapBox here: How to get click handler on animated canvas dot on MapBox map?

import { useEffect } from 'react'
import * as mapboxgl from 'mapbox-gl'
import locations from 'seeds/sample-location-list.json'
import styles from './Map.module.css'

export default function Map({ scrollable, id, style, onClick }) {
  useEffect(() => {
    const map = new mapboxgl.Map({
      accessToken: '<token>',
      container: id ?? 'map',
      center: [16.572149246748594, 19.06111670348622],
      zoom: 1.5,
      style: style ?? 'mapbox://styles/<co>/<style>',
      attributionControl: false,
    })
    // .addControl(new mapboxgl.AttributionControl({
    //   compact: true
    // }), 'top-left')

    var size = 120;
    // This implements `StyleImageInterface`
    // to draw a pulsing dot icon on the map.
    var pulsingDot = {
      width: size,
      height: size,
      data: new Uint8ClampedArray(size * size * 4),
      context: null,

      // When the layer is added to the map,
      // get the rendering context for the map canvas.
      onAdd: function () {
        var canvas = document.createElement('canvas');
        canvas.width = this.width;
        canvas.height = this.height;
        this.context = canvas.getContext('2d');
      },

      // Call once before every frame where the icon will be used.
      render: function () {
        var duration = 1000;
        var t = (performance.now() % duration) / duration;

        var radius = (size / 2) * 0.3;
        var outerRadius = (size / 2) * 0.7 * t + radius;
        var context = this.context;

        // Draw the outer circle.
        context.clearRect(0, 0, this.width, this.height);
        context.beginPath();
        context.arc(
          this.width / 2,
          this.height / 2,
          outerRadius,
          0,
          Math.PI * 2
        );
        context.fillStyle = 'rgba(253, 253, 150,' + (1 - t) + ')';
        context.fill();

        // Draw the inner circle.
        context.beginPath();
        context.arc(
          this.width / 2,
          this.height / 2,
          radius,
          0,
          Math.PI * 2
        );
        context.fillStyle = 'rgba(253, 253, 150, 1)';
        context.strokeStyle = 'rgb(119, 221, 119)';
        context.lineWidth = 2 + 4 * (1 - t);
        context.fill();
        context.stroke();

        // Update this image's data with data from the canvas.
        this.data = context.getImageData(
          0,
          0,
          this.width,
          this.height
        ).data;

        // Continuously repaint the map, resulting
        // in the smooth animation of the dot.
        map.triggerRepaint();

        // Return `true` to let the map know that the image was updated.
        return true;
      }
    };

    if (!scrollable) map.scrollZoom.disable()

    map.on('load', function () {
      map.addImage('pulsing-dot', pulsingDot, { pixelRatio: 2 });

      locations.forEach(location => {
        map.addSource(location.id.seed, {
          type: 'geojson',
          data: {
            type: 'FeatureCollection',
            features: [
              {
                type: 'Feature',
                geometry: {
                  type: 'Point',
                  coordinates: [location.lng, location.lat] // icon position [lng, lat]
                },
                properties: {},
              }
            ]
          }
        })

        map.addLayer({
          'id': location.id.seed,
          'type': 'symbol',
          'source': location.id.seed,
          'layout': {
            'icon-image': 'pulsing-dot'
          }
        });
      })

      map.on('mousemove', 'earthquakes-viz', (e) => {
        // console.log
      })
    });

    if (onClick) map.on('click', onClick)

    return () => map.remove()
  }, [])

  return (
    <div id={id ?? "map"} className={styles.map}></div>
  )
}

It animates the pulsing of around 20 dots on a map canvas layer. I checked it after some time and it was at 80MB memory usage (my whole React app, of which the map is a small part part), yet my CPU was screaming, fans going off like crazy. How do I fix it so it's not so processor intensive?



from Why is animated MapBox map screaming on the CPU?

No comments:

Post a Comment