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 dp = new DayPilot.Scheduler("dp", {
startDate: "2026-09-01",
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"
});
dp.init();
const app = {
init() {
this.loadData();
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();
},
loadData() {
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(dp.startDate);
const resources = [];
const events = [];
const links = [];
for (const [index, team] of teams.entries()) {
const children = [];
for (let row = 0; row < team.count; row++) {
const id = team.id + "-" + row;
const offset = (row % 4) * 2;
children.push({id, name: team.name + " " + (row + 1)});
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"});
}
}
resources.push({
id: team.id,
name: team.name + " (" + team.count + (team.count === 1 ? " resource)" : " resources)"),
expanded: index < 2,
children
});
}
dp.update({resources, events, links});
},
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();