ArcGIS Time Animation
Use this skill for temporal data, time-aware layers, time filtering, and animation controls.
Import Patterns
ESM (npm)
import TimeExtent from "@arcgis/core/TimeExtent.js";
import TimeInterval from "@arcgis/core/TimeInterval.js";
import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
CDN (dynamic import)
const TimeExtent = await $arcgis.import("@arcgis/core/TimeExtent.js");
const TimeInterval = await $arcgis.import("@arcgis/core/TimeInterval.js");
const TimeSlider = await $arcgis.import("@arcgis/core/widgets/TimeSlider.js");
TimeExtent
Represents a time range with start and end dates.
const timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-12-31")
});
// Apply to view (filters all time-aware layers)
view.timeExtent = timeExtent;
// Instant in time (start === end)
const instant = new TimeExtent({
start: new Date("2024-06-15"),
end: new Date("2024-06-15")
});
// No time filter (show all data)
view.timeExtent = null;
TimeInterval
Represents a duration of time.
const interval = new TimeInterval({
value: 1,
unit: "months"
});
Supported units: milliseconds, seconds, minutes, hours, days, weeks, months, years, decades, centuries
TimeSlider Component
arcgis-time-slider (Map Component)
<arcgis-map item-id="YOUR_WEBMAP_ID">
<arcgis-time-slider
slot="bottom-left"
mode="time-window"
loop
time-visible>
</arcgis-time-slider>
</arcgis-map>
<script type="module">
const map = document.querySelector("arcgis-map");
const timeSlider = document.querySelector("arcgis-time-slider");
await map.viewOnReady();
// Configure from layer
const layer = map.view.map.layers.find(l => l.timeInfo);
if (layer) {
await layer.load();
timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
timeSlider.stops = { interval: layer.timeInfo.interval };
}
</script>
TimeSlider Widget (Core API)
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: {
start: new Date("2020-01-01"),
end: new Date("2024-12-31")
},
timeExtent: {
start: new Date("2024-01-01"),
end: new Date("2024-03-31")
},
mode: "time-window",
playRate: 1000,
loop: true,
stops: {
interval: {
value: 1,
unit: "months"
}
}
});
TimeSlider Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| fullTimeExtent | TimeExtent | — | Complete time range the widget can display |
| timeExtent | TimeExtent | — | Currently selected time range |
| mode | string | "instant" | Animation mode |
| stops | object | — | Snap points for slider |
| playRate | number | 1000 | Milliseconds between steps |
| loop | boolean | false | Restart at end |
| layout | string | "auto" | "auto", "compact", "wide" |
| disabled | boolean | false | Disable interaction |
TimeSlider Modes
// Instant - single point in time
mode: "instant"
// Time Window - range with start and end
mode: "time-window"
// Cumulative from Start - everything from start to current
mode: "cumulative-from-start"
// Cumulative from End - everything from current to end
mode: "cumulative-from-end"
Custom Stops
// Interval-based stops
stops: {
interval: { value: 1, unit: "weeks" }
}
// Specific dates
stops: {
dates: [
new Date("2024-01-01"),
new Date("2024-04-01"),
new Date("2024-07-01"),
new Date("2024-10-01")
]
}
// Number of evenly distributed stops
stops: {
count: 12
}
Playback Control
timeSlider.play();
timeSlider.stop();
Watch Time Changes
timeSlider.watch("timeExtent", (timeExtent) => {
console.log("New time range:", timeExtent.start, "to", timeExtent.end);
});
timeSlider.watch("viewModel.state", (state) => {
console.log("State:", state); // "ready", "playing", "disabled"
});
Custom Labels
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: { start, end },
tickConfigs: [{
mode: "position",
values: [
new Date("2021-01-01"),
new Date("2022-01-01"),
new Date("2023-01-01")
],
labelsVisible: true,
labelFormatFunction: (value) => value.getFullYear().toString()
}],
labelFormatFunction: (value, type, element, layout) => {
const date = new Date(value);
if (type === "min" || type === "max") {
return date.toLocaleDateString();
}
return date.toLocaleDateString("en-US", { month: "short", year: "numeric" });
}
});
Time-Aware Layers
FeatureLayer with Time
const featureLayer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0",
timeInfo: {
startField: "event_date",
endField: "end_date",
interval: {
value: 1,
unit: "days"
}
}
});
// Check if layer supports time
await featureLayer.load();
if (featureLayer.timeInfo) {
console.log("Time field:", featureLayer.timeInfo.startField);
console.log("Full extent:", featureLayer.timeInfo.fullTimeExtent);
}
TimeInfo Properties
| Property | Type | Description |
|----------|------|-------------|
| startField | string | Start date field name (required) |
| endField | string | End date field name (optional) |
| fullTimeExtent | TimeExtent | Complete time range of data |
| interval | TimeInterval | Suggested animation interval |
| trackIdField | string | Track identifier field (for StreamLayer) |
Other Time-Aware Layers
// ImageryLayer
const imageryLayer = new ImageryLayer({
url: "...",
timeInfo: { startField: "acquisition_date" }
});
// MapImageLayer
const mapImageLayer = new MapImageLayer({
url: "...",
timeInfo: { startField: "date_field" }
});
// StreamLayer
const streamLayer = new StreamLayer({
url: "wss://services.arcgis.com/.../StreamServer",
timeInfo: {
trackIdField: "vehicle_id",
startField: "timestamp"
},
purgeOptions: {
displayCount: 1000,
age: 5
}
});
Initializing TimeSlider from Layer
async function setupTimeSlider(view, layer) {
await layer.load();
if (!layer.timeInfo) {
console.warn("Layer is not time-aware");
return null;
}
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: layer.timeInfo.fullTimeExtent,
mode: "time-window",
playRate: 1000,
loop: true,
stops: {
interval: layer.timeInfo.interval || { value: 1, unit: "months" }
}
});
timeSlider.watch("timeExtent", (extent) => {
document.getElementById("currentTime").textContent =
`${extent.start.toLocaleDateString()} - ${extent.end.toLocaleDateString()}`;
});
return timeSlider;
}
Filtering by Time
Client-Side Filter
// Filter all time-aware layers via view
view.timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-06-30")
});
// Filter specific layer via layerView
const layerView = await view.whenLayerView(featureLayer);
layerView.filter = {
timeExtent: new TimeExtent({
start: new Date("2024-03-01"),
end: new Date("2024-03-31")
})
};
Query with Time
const query = featureLayer.createQuery();
query.timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-12-31")
});
query.where = "status = 'active'";
query.returnGeometry = true;
const results = await featureLayer.queryFeatures(query);
Animation Patterns
TimeSlider with Statistics
timeSlider.watch("timeExtent", async (timeExtent) => {
const query = featureLayer.createQuery();
query.timeExtent = timeExtent;
query.outStatistics = [{
statisticType: "count",
onStatisticField: "OBJECTID",
outStatisticFieldName: "count"
}, {
statisticType: "sum",
onStatisticField: "value",
outStatisticFieldName: "total"
}];
const result = await featureLayer.queryFeatures(query);
const stats = result.features[0].attributes;
document.getElementById("count").textContent = stats.count;
document.getElementById("total").textContent = stats.total;
});
Time-Based Highlighting
let highlightHandle;
timeSlider.watch("timeExtent", async (timeExtent) => {
if (highlightHandle) highlightHandle.remove();
const query = featureLayer.createQuery();
query.timeExtent = timeExtent;
const layerView = await view.whenLayerView(featureLayer);
const results = await featureLayer.queryFeatures(query);
highlightHandle = layerView.highlight(results.features);
});
Manual Animation Loop
async function animateOverTime(layer, startDate, endDate, intervalDays) {
const current = new Date(startDate);
while (current <= endDate) {
const nextDate = new Date(current);
nextDate.setDate(nextDate.getDate() + intervalDays);
view.timeExtent = new TimeExtent({
start: current,
end: nextDate
});
await new Promise(resolve => setTimeout(resolve, 500));
current.setDate(current.getDate() + intervalDays);
}
}
TimeZoneLabel Component
<arcgis-map>
<arcgis-time-zone-label slot="bottom-left"></arcgis-time-zone-label>
</arcgis-map>
Graphics Visibility with Time
// Set time visibility on individual graphics
graphic.visibilityTimeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-06-30")
});
Common Pitfalls
-
Time zone issues: Dates are affected by JavaScript's local timezone — use UTC dates for consistency.
const date = new Date("2024-06-15T00:00:00Z"); -
Layer must be loaded:
timeInfois only available afterawait layer.load(). -
TimeExtent not applied: The view's
timeExtentmust be set to filter all time-aware layers. -
Null removes filter: Use
view.timeExtent = nullto show all data (no time filter). -
Performance: Large time ranges can return many features — use server-side queries when possible.
-
Stops required: TimeSlider needs
stopsconfigured to define slider positions.
Reference Samples
timeslider— Basic TimeSlider widgettimeslider-filter— Filtering data with TimeSlidertimeslider-component-filter— TimeSlider component with filteringtime-layer— Working with time-aware layerswidgets-timeslider— TimeSlider widget exampleswidgets-timeslider-offset— TimeSlider with timezone offsetlayers-scenelayer-time— Time-aware SceneLayerlayers-voxel-time— Time-aware VoxelLayerlayers-graphics-visibilitytimeextent— Graphics visibility with time
Related Skills
arcgis-imagery— Multidimensional imagery with timearcgis-layers— Layer configurationarcgis-widgets-ui— Widget placement and slots