<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Animate color visual variable | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
background: var(--calcite-color-foreground-1);
--calcite-color-brand: #9e9e9e;
--calcite-color-brand-hover: #ededed;
box-shadow: 0 0 4px rgba(255, 255, 255, 0.75);
background-color: var(--calcite-color-foreground-1);
<body class="calcite-mode-dark">
<calcite-shell id="appShell">
<arcgis-map center="-73.967569, 40.727724" zoom="12" resize-align="top-left">
<div slot="top-left" id="titleDiv">New York Construction</div>
<arcgis-home slot="top-left"></arcgis-home>
<arcgis-fullscreen slot="top-right"></arcgis-fullscreen>
<arcgis-expand slot="bottom-left" expand-icon="list-bullet">
<arcgis-legend legend-style="classic"></arcgis-legend>
<div id="footer" slot="footer">
<calcite-block id="years-block" expanded label="current year">
<span id="sliderValue">1880</span>
<calcite-block id="slider-block" label="slider block" expanded>
<div id="sliderInnerContainer">
min="1880"></calcite-slider>
<calcite-block label="play button block" id="play-block" expanded>
<calcite-button id="playButton" appearance="transparent" icon-start="play"
// Consolidate imports up front and freeze constants
const [Map, FeatureLayer, promiseUtils] = await $arcgis.import([
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/core/promiseUtils.js",
const sliderValue = document.getElementById("sliderValue");
const playButton = document.getElementById("playButton");
const slider = document.getElementById("yearsSlider");
const viewElement = document.querySelector("arcgis-map");
const layer = new FeatureLayer({
portalItem: { id: "dfe2d606134546f5a712e689d76540ac" },
definitionExpression: "CNSTRCT_YR > 0",
title: "Building Footprints",
effect: "bloom(2.5 0 0.5)",
viewElement.map = new Map({
id: "4f2e99ba65e34bb8af49733d9778fb8e",
viewElement.constraints = {
const fullsceenElement = document.querySelector("arcgis-fullscreen");
fullsceenElement.element = document.getElementById("appShell");
// When user drags the slider:
// - set the visualized year to the slider one.
slider.addEventListener("calciteSliderInput", (e) => {
// Toggle animation on/off when user
// clicks on the play button
playButton.addEventListener("click", () => {
playButton.iconStart === "pause" ? stopAnimation() : startAnimation();
// When the layerview is available, setup hovering interactivity
viewElement.whenLayerView(layer).then(setupHoverTooltip);
// Starts the application by visualizing year 1984
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
* Sets the current visualized construction year.
function setYear(value) {
const year = Math.floor(value);
sliderValue.textContent = year;
layer.renderer = createRenderer(year);
* Returns a renderer with a color visual variable driven by the input
* year. The selected year will always render buildings built in that year
* with a light blue color. Buildings built 20+ years before the indicated
* year are visualized with a pink color. Buildings built within that
* 20-year time frame are assigned a color interpolated between blue and pink.
function createRenderer(year) {
symbol: { type: "simple-fill", color: "rgb(0, 0, 0)", outline: null },
{ opacity: 1, value: year },
{ opacity: 0, value: year + 1 },
legendOptions: { showLegend: false },
legendOptions: { title: "Built:" },
{ value: year, color: "#0ff", label: `in ${year}` },
{ value: year - 10, color: "#f0f", label: `in ${year - 20}` },
{ value: year - 50, color: "#404", label: `before ${year - 50}` },
* Starts the animation that cycle
* through the construction years.
function startAnimation() {
animation = animate(Number(slider.value));
playButton.iconStart = "pause";
playButton.textContent = "Pause";
function stopAnimation() {
playButton.iconStart = "play";
playButton.textContent = "Play";
* Animates the color visual variable continously
function animate(startValue) {
if (value > 2017) value = 1880;
requestAnimationFrame(update);
requestAnimationFrame(update);
* Sets up a moving tooltip that displays
* the construction year of the hovered building.
function setupHoverTooltip(layerView) {
const tooltip = createTooltip();
const hitTest = promiseUtils.debounce((event) => {
return viewElement.hitTest(event).then((hit) => {
const result = hit.results.find((r) => r.graphic.layer === layer);
return result ? { graphic: result.graphic, screenPoint: hit.screenPoint } : null;
viewElement.addEventListener("arcgisViewPointerMove", async (event) => {
const hit = await hitTest(event.detail);
highlight = layerView.highlight(hit.graphic);
tooltip.show(hit.screenPoint, `Built in ${hit.graphic.getAttribute("CNSTRCT_YR")}`);
if (error.name !== "AbortError") {
console.error("Unexpected hitTest error:", error);
// Ignore AbortError silently
* Creates a tooltip to display a the construction year of a building.
function createTooltip() {
const tooltip = document.createElement("div");
tooltip.className = "tooltip";
tooltip.setAttribute("role", "tooltip");
const text = document.createElement("div");
tooltip.appendChild(text);
viewElement.appendChild(tooltip);
x += (targetX - x) * 0.1;
y += (targetY - y) * 0.1;
if (Math.abs(targetX - x) < 1 && Math.abs(targetY - y) < 1) {
requestAnimationFrame(move);
tooltip.style.transform = `translate3d(${Math.round(x)}px,${Math.round(y)}px,0)`;
if (!visible) [x, y] = [pt.x, pt.y];
[targetX, targetY] = [pt.x, pt.y];
tooltip.style.opacity = 1;
tooltip.style.opacity = 0;