Auto respects your system's reduced-motion preference. Enabled animates regardless of that preference. Choose Disabled to disable the transition. Duration covers the motion; rendering takes additional time.
const teams = [
{id: "planning", name: "Planning", count: 1},
{id: "design", name: "Design", count: 3},
{id: "development", name: "Development", count: 10},
{id: "operations", name: "Operations", count: 30},
{id: "production", name: "Production", count: 100}
];
const start = new DayPilot.Date("2026-09-01");
const events = [];
const links = [];
const resources = teams.map((team, index) => ({
id: team.id,
name: team.name + " (" + team.count + (team.count === 1 ? " resource)" : " resources)"),
expanded: index < 2,
children: Array.from({length: team.count}, (_, row) => {
const id = team.id + "-" + row;
const offset = (row % 4) * 2;
events.push(
{id: id + "-plan", resource: id, text: "Preparation", start: start.addDays(offset + 1), end: start.addDays(offset + 3)},
{id: id + "-work", resource: id, text: "Delivery", start: start.addDays(offset + 4), end: start.addDays(offset + 7)}
);
links.push({id: id + "-link", from: id + "-plan", to: id + "-work"});
if (row > 0 && row % 3 === 0) {
links.push({id: id + "-handoff", from: team.id + "-" + (row - 1) + "-work", to: id + "-work"});
}
return {id, name: team.name + " " + (row + 1)};
})
}));
const dp = new DayPilot.Scheduler("dp", {
startDate: start, days: 21, scale: "Day", cellWidthSpec: "Auto", cellWidthMin: 30,
floatingTimeHeaders: false,
timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
heightSpec: "Max", height: 450,
rowMarginTop: 2, rowMarginBottom: 2,
rowHeaderWidth: 230,
treeEnabled: true,
treeAnimation: true,
treeAnimationDuration: "Auto",
treeAnimationEasing: "standard",
eventMoveHandling: "Disabled", eventResizeHandling: "Disabled", timeRangeSelectedHandling: "Disabled",
resources, events, links
});
dp.init();
const app = {
init() {
document.getElementById("animation-settings").addEventListener("change", () => {
this.apply();
});
this.preference = window.matchMedia("(prefers-reduced-motion: reduce)");
this.preference.addEventListener("change", () => this.showPreference());
this.showPreference();
this.showConfiguration();
},
showPreference() {
document.getElementById("motion-preference").hidden =
!(dp.treeAnimation === "Auto" && dp.treeAnimationDuration !== 0 && this.preference.matches);
},
apply() {
if (!document.getElementById("animation-settings").reportValidity()) {
this.message("Enter valid settings. The previous configuration remains active.", true);
return;
}
const animation = document.getElementById("animation").value;
const duration = document.getElementById("duration").value === "Auto" ? "Auto" :
Number(document.getElementById("duration").value);
const easing = document.getElementById("easing").value;
try {
// Animation settings are read at the next tree toggle; no redraw is needed.
dp.treeAnimation = animation === "Auto" ? "Auto" : animation === "true";
dp.treeAnimationDuration = duration;
dp.treeAnimationEasing = easing;
this.showPreference();
this.showConfiguration();
this.message("");
}
catch (error) {
this.message(error.message, true);
}
},
message(text, error = false) {
const message = document.getElementById("animation-message");
message.textContent = text;
message.hidden = !text;
message.classList.toggle("error", error);
},
showConfiguration() {
document.getElementById("configuration").textContent = "heightSpec: \"Max\",\n" +
"treeEnabled: true,\n" +
"treeAnimation: " + JSON.stringify(dp.treeAnimation) + ",\n" +
"treeAnimationDuration: " + JSON.stringify(dp.treeAnimationDuration) + ",\n" +
"treeAnimationEasing: " + JSON.stringify(dp.treeAnimationEasing);
}
};
app.init();