Why my animation slows down when I use Leaflet and pixi-overlay?

Viewed 134

I'm using WebGL pixi-overlay plugin for leaflet https://github.com/manubb/Leaflet.PixiOverlay. There are markers which have animation effect when leaflet map is moving and they rendered with WebGL renderer. I've optimized WebGL animation but still have dropped frames sometimes. I suppose it's because leaflet animation rendering intersects WebGL rendering. I've found out that Leaflet uses CSS transitions. I'd like to know how to improve performance to avoid dropped frames.

Performance tab

animation.js:

  container,
  flags,
  timestamp,
  delta,
  targetScale,
  currentScale
) {
  var time = flags.scalingTime;
  var progress = timestamp - time;
  var lambda = progress / delta;
  if (lambda > 1) lambda = 1;
  lambda = lambda * (0.4 + lambda * (2.2 + lambda * -1.6));
  for (var markerSprite of container.children) {
    markerSprite.scale.set(
      currentScale + lambda * (targetScale - currentScale)
    );
  }
  if (progress >= delta) {
    flags.scaling = false;
  }
}

function transformFading(container, flags, timestamp, delta, coef = 1) {
  var fadeMarkers, time;
  if (coef > 0) {
    fadeMarkers = flags.fadeInMarkers;
    time = flags.fadingInTime;
  } else {
    fadeMarkers = flags.fadeOutMarkers;
    time = flags.fadingOutTime;
  }
  var progress = timestamp - time;
  var lambda = progress / delta;
  if (lambda > 1) lambda = 1;
  lambda = lambda * (0.4 + lambda * (2.2 + lambda * -1.6));

  for (var markerSprite of fadeMarkers) {
    markerSprite.alpha = coef > 0 ? lambda : 1 - lambda;
  }
  if (progress >= delta) {
    if (coef < 0) {
      flags.fadingOut = false;
      container.removeChild(...fadeMarkers);
    } else flags.fadingIn = false;
  }
}

export function animate(
  flags,
  timestamp,
  container,
  renderer,
  delta,
  targetScale = null,
  currentScale = null
) {
  if (flags.scaling)
    transformScaling(
      container,
      flags,
      timestamp,
      delta,
      targetScale,
      currentScale
    );
  if (flags.fadingIn) transformFading(container, flags, timestamp, delta, 1);
  if (flags.fadingOut) transformFading(container, flags, timestamp, delta, -1);
  renderer.render(container);
  if (flags.scaling || flags.fadingIn || flags.fadingOut) {
    var frame = requestAnimationFrame(() =>
      animate(
        flags,
        Date.now(),
        container,
        renderer,
        delta,
        targetScale,
        currentScale
      )
    );
  } else {
    cancelAnimationFrame(frame);
    flags.animateRunning = false;
  }
}

main loop: (some code was removed for simplicity)

export function preloadApp(map) {
  var loader = new PIXI.Loader();
  loadTextures(loader);
  loader.load(function (loader, resources) {
    var container = new PIXI.Container();
    var prevZoom
    var renderer, project;
    var pointToCoords = {};
    var sentCount = 0;
    var firstDraw = true;
    var flags = {
      scaling: false,
      fadingIn: false,
      fadingOut: false,
      animateRunning: false,
    };
    var delta = 200;
    var pixiOverlay = L.pixiOverlay(function (utils) { // calls each time when map position updates
      if (typeof renderer === "undefined") {
        renderer = utils.getRenderer();
      }
      var zoom = map.getZoom();
      //...

      sentCount += 1; // count moving

      sendRequest("GET", `http://localhost:5000/api/markers/${requestString}`)
        .then((mapObjects) => {
          setTimeout(() => { // Imitation timeout
            // if position was changed multiiple times we should work with the last one
            if (sentCount > 1) {
              sentCount -= 1;
              return;
            }

            var scale = utils.getScale();
            project = utils.latLngToLayerPoint; // projection
            pointToCoords.cur = utils.layerPointToLatLng;

            var fadeOutMarkers = [
              ...container.children.filter(
                //[rule]
              ),
            ];

            flags.fadingOut = true;
            flags.fadingOutTime = Date.now();
            flags.fadeOutMarkers = fadeOutMarkers;

            if (!flags.animateRunning) {
              animate(
                flags,
                timestamp,
                container,
                renderer,
                delta,
                targetScale,
                currentScale
              );
              flags.animateRunning = true;
            }

            // оставляем в контейнере только те элементы, которые совпадают с теми, что пришли с сервера

            var clusterPoints = mapObjects;
            var fadeInMarkers = [];
            for (let /*!*/ coordinate in clusterPoints) {
              let objectCoords = clusterPoints[coordinate].coords; // координаты в виде массива
              var options = {
                objectCoords,
                project,
                scale,
                map,
                container,
                renderer,
                clusterPoints,
                mapObjects,
                pointToCoords,
              };
              var newMarker = createMarker(options);
              newMarker.alpha = 0;
              fadeInMarkers.push(newMarker);
              container.addChild(newMarker);
            }
            prevZoom = zoom;
            flags.fadingIn = true;
            flags.fadeInMarkers = fadeInMarkers;
            flags.fadingInTime = Date.now();
            if (!flags.animateRunning) {
              animate(
                flags,
                timestamp,
                container,
                renderer,
                delta,
                targetScale,
                currentScale
              );
              flags.animateRunning = true;
            }
            sentCount -= 1;
          }, 0);
        })
        .catch((err) => {
          alert(err);
        });

      // Rescaling
      if (zoom !== prevZoom && !firstDraw) {
        var scale = utils.getScale();
        var targetScale = 1 / scale / 4;
        var currentScale = container.children[0].transform.scale.x;

        var timestamp = Date.now();
        flags.scaling = true;
        flags.scalingTime = timestamp;

        if (!flags.animateRunning) {
          animate(
            flags, 
            timestamp,
            container,
            renderer,
            delta,
            targetScale,
            currentScale
          );
          flags.animateRunning = true;
        }
      }
      prevZoom = zoom;

      firstDraw = false;
      // if(!flags.animateRunning) ???
      renderer.render(container);
    }, pixiContainer);
    pixiOverlay.addTo(map);
  });
}
0 Answers
Related