1.1.5 • Published 7 months ago

@madsbrydegaard/timelines v1.1.5

Weekly downloads
-
License
ISC
Repository
github
Last release
7 months ago

@madsbrydegaard/timelines

Timeline engine for creating robust and flexible timelines with zoom and pan.

The project is designed to act as the main engine for web projects that can visualize historic events.

Code re-calculates points in time on every frame using minutes as timescale. The engine does not depent on expanding divs or any other HTML elements which is why it can zoom past browser limits and span 14B years of history.

How to use:

Module supports both ESM and UMD - please check demos.

Install ESM Module:

npm i @madsbrydegaard/timelines

ESM Setup:

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

	const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "100ad"
    });
</script>

Vanilla setup:

<div id="timeline"></div>
<script src="path/to/timelines.umd.js"></script>
<script>
    const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "100ad"
    });
</script>

Add timeline events

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

	const timeline = TimelineContainer("#timeline", {
        start: "100bc",
        end: "now"
    });

    const timelineEvents = [
        {
            start: [1974],
            end: 'now',
            title: "My Life",
            description:
                "I was born in the last century and spend most of my life programming computers",
        },
    ];
    timeline.add({
        events: timelineEvents.map((item) => ({
            ...item,
            type: "timeline",
        })),
    });
</script>

Capturing events

<div id="timeline"></div>
<script type="module">
    import { TimelineContainer } from "@madsbrydegaard/timelines";

    const timelineContainer = document.querySelector("#timeline");
	const timeline = TimelineContainer(timelineContainer, {
        start: "100bc",
        end: "now"
    });

    const timelineEvents = [
        "pinch.tl.container",
        "wheel.tl.container",
        "drag.tl.container",
        "click.tl.event"
    ];

    timelineEvents.forEach((eventName) => {
        timelineContainer.addEventListener(eventName, (timelineEvent) => {
            console.log(eventname, timelineEvent);
        });
    });

    const timelineEvents = [
        {
            start: [1974],
            end: 'now',
            title: "My Life",
            description:
                "I was born in the last century and spend most of my life programming computers",
        },
    ];
    timeline.add({
        events: timelineEvents.map((item) => ({
            ...item,
            type: "timeline",
        })),
    });
</script>

React & Typescript setup:

import { TimelineContainer, ITimelineContainer, ITimelineOptions, ITimelineEvent } from "@madsbrydegaard/timelines"
import { useEffect, useRef, createRef } from 'react';

function App() {
    const container = createRef<HTMLDivElement>();
    const timeline = useRef<ITimelineContainer>();
    const events = [
        "pinch.tl.container",
        "wheel.tl.container",
        "drag.tl.container",
        "click.tl.event"
    ];

    useEffect(()=>{
        if (container.current) {
            // Initialize a timeline
			timeline.current = TimelineContainer(container.current, {
				start: "100bc",
				end: "2030ac",
			} as ITimelineOptions) as ITimelineContainer;

            // Listening for interaction event(s)
            events.forEach((eventName) => {
                container.current.addEventListener(eventName, (evt: CustomEvent) => {
                    if (evt.detail) {
                        // See details on event that has been clicked
                        console.log(evt.detail)
                    }
                });
            });

            // Load timeline events into timeline
            timeline.current.add(async () => {
				return await gofetch("path/to/some.json") as ITimelineEvent;
            }
        }
    }, [])

    return (
        <div className="App">
            <div ref={myRef}></div>
        </div>
    );
}

export default App;

Documentation:

// Constructor
TimelineContainer(element: HTMLElement | string, options: object)

First argument is the page container for the timeline. Eg. '#timelineContainer' | document.querySelector("#timelineContainer");

Zooming and panning is automatically activated on the whole container so that large page areas can be part of timeline.

The timeline works with 2 different date sets.

  • Overall date range of allowed zooming and panning. Zooming and Panning is not allowed outside these dates.
  • Specific date range for initialization. These acts as the boundaries when the timeline initalizes and creates a 'view' on the overall timeline

Second argument is a configuration object. Default configuration looks like this:

{
    // Defines number of 'time' labels in the timeline
    labelCount: 5,
    // How fast zooming happens
    zoomSpeed: 0.04,
    // How fast panning happens
    dragSpeed: 0.001,
    // When timeline should start on init. (See below for valid input)
    start: "-100y",
    // When timeline should end on init. (See below for valid input)
    end: "now",
    // When overall timeline boundary should start. (See below for valid input)
    timelineStart: "-1B",
    // When overall timeline boundary should end. (See below for valid input)
    timelineEnd: "1M",
    // How far out zoom is allowed
    minZoom: 1,
    // How far in zoom is allowed
    maxZoom: 1e11,
    // Where to vertically place the timeline in the container. 'bottom' | 'top'
    position: "bottom",
    // Height of timeline events
    eventHeight: 5,
    // Spacing between timeline events
    eventSpacing: 3,
    // Whether zoom and pan is automatically initialized when clicking a timeline event
    autoZoom: false,
    // Margin on each side when using auto zoom
    zoomMargin: 0.1,
    // Whether events are automatically selected on mouse click
    autoSelect: false,
    // Default color for each timeline event
    defaultColor: "#aaa",
    // Default color for each timeline event when selected
    defaultHighlightedColor: "#444",
    // Default color for each timeline background event
    defaultBackgroundColor: "#0000",
    // Default color for each timeline background event when selected
    defaultBackgroundHightligtedColor: "#eee7",
    // Duration in ms when auto zoom is enabled
    zoomDuration: 200,
    // Auto zoom easing. "easeOutCubic" | "easeOutExpo" | "easeLinear"
	easing: "easeOutCubic",
    // How many previews to display
	numberOfHighscorePreviews: 5,
    // Delay befort displaying previews in ms
	highscorePreviewDelay: 500,
    // Preview width in px
	highscorePreviewWidth: 100,
    // Default classnames for generated HTML Elements
    classNames: {
        timeline: "tl",
        timelineEvent: "tl__event",
        timelinePreview: "tl__preview",
        timelineEventTitle: "tl__event__title",
        timelineLabels: "tl__labels",
        timelineDividers: "tl__dividers",
        timelineEvents: "tl__events",
        timelinePreviews: "tl__previews",
        timelineIo: "tl__io",
        timelineLabel: "tl__label",
        timelineDivider: "tl__divider",
    }
}

Allowed values for dates:
Engine has its own Date string parser which supports the following specials:

  • XXX -> any positive or negative number translates to now plus/minus number of minutes.
  • 'YYYY-MM-DDTHH:mm:ss.sssZ' -> ISO 8601 string
  • 'now' -> Translates to current time = new Date()
  • 'XXXy' -> any positive or negative number with a trailing 'y'. Translates relative to now. Eg. '-1000y' = Minus 1000 years from now.
  • 'XXXbc' -> any positive or negative number with a trailing 'bc'. Translates into that specific year - BC. Eg. '100bc' = Year -100
  • 'XXXad' -> any positive or negative number with a trailing 'ad'. Translates into that specific year - AD. Eg. '100ad' = Year 100
  • 'XXXK' -> any positive or negative number with a trailing 'K'. Translates into year * 1000. Eg. '100K' = Year 100.000
  • 'XXXM' -> any positive or negative number with a trailing 'M'. Translates into year * 1.000.000. Eg. '100M' = Year 100.000.000
  • 'XXXB' -> any positive or negative number with a trailing 'B'. Translates into year * 1.000.000.000. Eg. '100B' = Year 100.000.000.000

Events Engine dispatches events on interactions. Use addEventListener to capture them

timelineContainer.addEventListener(eventName, (timelineEvent) => {
    // Handle timelineEvent...
});

Timeline Event object

{
    // Name of event. See list below.
    name: string;
    // Configurations object. See structure above.
    options: ITimelineOptions;
    // Current timeline event if IO event is related to any timeline event. See structure below.
    timelineEvent: ITimelineEventWithDetails;
    // Where is viewStart now. formatted.
    viewStartDate: string;
    // Where is viewEnd now. formatted.
    viewEndDate: string;
    // View duration now. In minutes.
    viewDuration: number;
    // Current timeline / view ratio (z-axix offset)
    ratio: number;
    // Current timeline / view pivot (x-axix offset)
    pivot: number;
},

Complete IO event list:

  • update.tl.container -> When engine updates (re-draws)
  • pinch.tl.container
  • wheel.tl.container
  • drag.tl.container
  • touchstart.tl.container
  • touchend.tl.container
  • touchmove.tl.container
  • mousemove.tl.container
  • mousedown.tl.container
  • mouseup.tl.container
  • resize.tl.container
  • click.tl.event
  • hover.tl.event
  • selected.tl.event

TimelineEvent

{
    // Title of event
	title: string;
    // Optional Render function for event HTML in timeline. Must return HTML Div Element.
	renderEventNode?: (timelineEvent: ITimelineEventWithDetails) => HTMLDivElement;
    // Optional Render function for event preview HTML in timeline. Must return HTML Div Element.
	renderPreviewNode?: (timelineEvent: ITimelineEventWithDetails) => HTMLDivElement;
    // Type of event. Supports "timeline" | "background"
    type?: string;
    // Color of event element. array of numbers stranslates to #XXXXXXXX hex code
	color?: number[];
    // Color of event element when selected. array of numbers stranslates to #XXXXXXXX hex code
	highlightedColor?: number[];
    // Start of timeline event.
    start?: number[] | string | number | Date;
    // End of timeline event.
	end?: number[] | string | number | Date;
    // Optional duration of timeline event. Used when 'end' is not defined.
	duration?: number | string;
    // Child events
	events?: ITimelineEvent[];
    // Prevents preview rendering on next frame
    preventNextPreviewRender?: boolean;
}

TimelineContainer

{
    // Add events to container
	add: (...timelineEvents: ITimelineEvent[]) => void;
    // auto zoom specific event
	zoom: (timelineEvent: ITimelineEvent, useAnimation?: boolean, onzoomend?: (timelineEvent: ITimelineEvent) => void) => void;
    // focus specific event
	focus: (timelineEvent: ITimelineEvent, useAnimation?: boolean, onfocused?: (timelineEvent: ITimelineEvent) => void) => void;
    // reset container to initial config
	reset: () => void;
    // select specific event
	select: (timelineEventIdentifier?: string) => void;
    // Prevents global preview rendering on next frame
	preventNextPreviewRender: () => void;
}
1.1.5

7 months ago

1.1.4

7 months ago

1.1.3

7 months ago

1.1.2

7 months ago

1.1.1

7 months ago

1.1.0

7 months ago

1.0.4

9 months ago

1.0.3

9 months ago

1.0.2

9 months ago

1.0.1

9 months ago

1.0.0

9 months ago