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?
Solved! Go to Solution.
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:
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.
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:
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.