Feature Layer with Time Slider - Fade in individual features with 4.17

1202
1
Jump to solution
11-27-2020 12:03 PM
SethLutske
New Contributor III

I have a feature of layer of points which is time enabled. I also have a time slider, with some code that runs a filter on the layer to only show points that are within the time slider range.  The time slider uses cumulative-from-start.  The feature layer is using a picture-marker renderer with a firefly image:

 

 

const renderer = {
        symbol: {
          angle: 0,
          contentType: 'image/png',
          height: 18.75,
          width: 18.75,
          type: 'picture-marker',
          url: 'https://static.arcgis.com/images/Symbols/Firefly/FireflyC20.png',
          xoffset: 0,
          yoffset: 0,
        },
        type: 'simple',
      };

 

 

Here's what my filter looks like:

 

 

timeSlider.watch('timeExtent', function(value) {
  const dateString = value.end.getTime();
  layerView.filter = {
    where: `Time <= '${dateString}'`,
  };
});

 

 

 Here's a codepen showing what I'm working with. Its all working nicely.

 

What I would like to do is implement a fade in effect, such that as each point is included in the filter, it fades in, rather than just appears.  I know this has been done, as this code is basically an attempt to recreate this application. That app seems to be using version 3 of the arcgis api.  What's nice about that is that the markers are rendered as actual <img /> elements, so the css classes can be harnessed to create a very nice fade in effect.  However, with v4 of the API, I don't see a way to have such simple access to the elements as they're drawn into the canvas of the map.

How can I apply a fade-in effect to the points as the timeslider includes them in the layer's filter?

1 Solution

Accepted Solutions
SethLutske
New Contributor III

After some digging, I figured out a way to make this happen.  Its not perfect, but I saw a few similar questions that had gone unanswered, so I figured I'd post it here for any future passers-by.

Getting each individual feature can be done by calling layer.queryFeatures() on the layer.  This returns a FeatureSet , which is essentially a collection of Graphics .  There is no way to control the opacity of a Graphic.  However, you can create an array of new layers from the FeatureSet, each with a singular graphic representing one point:

 

 

 

 

  let layerView;
  let flFromResults;
  view.whenLayerView(torchRelay).then((lv) => {
    torchRelay.queryFeatures().then(function (results) {
      // create a new feature layer for each feature in the original layer
      flFromResults = results.features.map((feature) => {
        const layer = new FeatureLayer({
          objectIdField: "OBJECTID",
          source: [feature],
          fields: results.fields,
          popupTemplate: feature.popupTemplate,
          renderer
        });

        // listen for layer to be created, then call animation function
        layer.on("layerview-create", () => {
          fadeVisibilityOn(layer);
        });

        return {
          time: feature.attributes.Time,
          onmap: false,
          shouldBeAdded: false,
          layer
        };
      });

    });

    layerView = lv;
    layerView.filter = {
      where: `Time <= 'nope'`
    };

  });

 

 

 

 

 

timeSlider.watch("timeExtent", function (value) {
    const dateString = value.end.getTime();

    flFromResults && flFromResults.forEach((p) => {
      if (p.time <= dateString) {
        p.shouldBeAdded = true;
      } else {
        p.shouldBeAdded = false;
      }

      if (!p.onmap && p.shouldBeAdded) {
        map.add(p.layer);
        p.onmap = true;
      }

      if (p.onmap && !p.shouldBeAdded) {
        map.remove(p.layer);
        p.onmap = false;
      }
    });

  });

 

 

 

 

These if else statements will check the date of the timeslider against the date of each layer, and add and remove layers accordingly.  And voila, a fade in effect:

Working codepen 

The codepen has a bit of a wierd flash effect going on, but that is not occuring on my local machine. 

This method is probably not optimal, as it requires the creation of a new layer for every feature, but I can't find a way to set opacity on individual features.  Perhaps using a new GraphicsLayer instead of new FeatureLayer would be more optimal.  But it does work.  Hopefully this helps someone else in the future.

View solution in original post

0 Kudos
1 Reply
SethLutske
New Contributor III

After some digging, I figured out a way to make this happen.  Its not perfect, but I saw a few similar questions that had gone unanswered, so I figured I'd post it here for any future passers-by.

Getting each individual feature can be done by calling layer.queryFeatures() on the layer.  This returns a FeatureSet , which is essentially a collection of Graphics .  There is no way to control the opacity of a Graphic.  However, you can create an array of new layers from the FeatureSet, each with a singular graphic representing one point:

 

 

 

 

  let layerView;
  let flFromResults;
  view.whenLayerView(torchRelay).then((lv) => {
    torchRelay.queryFeatures().then(function (results) {
      // create a new feature layer for each feature in the original layer
      flFromResults = results.features.map((feature) => {
        const layer = new FeatureLayer({
          objectIdField: "OBJECTID",
          source: [feature],
          fields: results.fields,
          popupTemplate: feature.popupTemplate,
          renderer
        });

        // listen for layer to be created, then call animation function
        layer.on("layerview-create", () => {
          fadeVisibilityOn(layer);
        });

        return {
          time: feature.attributes.Time,
          onmap: false,
          shouldBeAdded: false,
          layer
        };
      });

    });

    layerView = lv;
    layerView.filter = {
      where: `Time <= 'nope'`
    };

  });

 

 

 

 

 

timeSlider.watch("timeExtent", function (value) {
    const dateString = value.end.getTime();

    flFromResults && flFromResults.forEach((p) => {
      if (p.time <= dateString) {
        p.shouldBeAdded = true;
      } else {
        p.shouldBeAdded = false;
      }

      if (!p.onmap && p.shouldBeAdded) {
        map.add(p.layer);
        p.onmap = true;
      }

      if (p.onmap && !p.shouldBeAdded) {
        map.remove(p.layer);
        p.onmap = false;
      }
    });

  });

 

 

 

 

These if else statements will check the date of the timeslider against the date of each layer, and add and remove layers accordingly.  And voila, a fade in effect:

Working codepen 

The codepen has a bit of a wierd flash effect going on, but that is not occuring on my local machine. 

This method is probably not optimal, as it requires the creation of a new layer for every feature, but I can't find a way to set opacity on individual features.  Perhaps using a new GraphicsLayer instead of new FeatureLayer would be more optimal.  But it does work.  Hopefully this helps someone else in the future.

0 Kudos