Slidy Slidy

slidy/core

Simple, configurable, nested & reusable sliding action script.

Сompletely mimics the behavior of a native scroll with mouse drag, index navigation, acceleration, gravity, easings, custom animations & infinite loop mode.

Try the DEMO.

Getting started

The package is available via NPM:

npm i -D @slidy/core

or via CDN:

<script src="https://unpkg.com/@slidy/core"></script>

Playground is available in svelte REPL.

Usage Examples

There are several options how to get Slidy into your project.

ES Module Import

<head>
   <script type="module">
        import { slidy } from "https://unpkg.com/@slidy/core/dist/index.mjs";

        slidy(document.querySelector("#node"))
    </script>
</head>

<section>
    <ul id="node">
        <!-- slide items -->
    </ul>
</section>

CommonJS Require

<head>
   <script type="module">
        import { slidy } from "https://unpkg.com/@slidy/core/dist/index.cjs";

        slidy(document.querySelector("#node"))
    </script>
</head>

<section>
    <ul id="node">
        <!-- slidy items -->
    </ul>
</section>

IIFE as Window Object

<head>
    <script src="https://unpkg.com/@slidy/core"></script>
</head>

<script>
    let slidy = null;
    let node = document.querySelector("#slidy"),

    window.onload = () => {
        slidy = Slidy.core(node);
    }
</script>

<section>
    <ul id="slidy">
        <!-- slidy items -->
    </ul>
</section>

SvelteJS

To use slidy with SvelteJS Framework, pass slidy as action into the parent DOM node:

<script>
    import { slidy } from "@slidy/core";
</script>

<section>
    <ul use:slidy>
        <!-- slidy items -->
    </ul>
</section>

API

slidy have two arguments: required node and optional options.

Options

Key Default Type Description
index 0 number The starting index on render.
clamp 0 number Defines the step in number of slides to slide on.
indent 1 number Creates an indent at the edges. The value is calculated from the gap: gap * indent
sensity 5 number Defines sensite as the pixels required to drag in order to start move, 0 when sliding.
gravity 1.2 number Controls the gravity value 0 (space) : 1 (earth) : 2 (underground).
duration 375 number Sliding duration in ms.
animation undefined AnimationFunc Custom sliding animation. Predefined sets are available via @slidy/animation.
easing undefined (t: number) => number Sliding easing behaviour. Predefined sets are available vie @slidy/easing.
snap undefined string Defines an area to “snap” the slide: "start", "center", "end", "deck", undefined. By default the behaviour is clamp sliding by edges.
axis undefined "x" or "y" Defines the flow direction.
loop false boolean Activated the infinite sliding mode.

Readonly properties:

Key Type Description
position number Current position
direction number Children move direction
vertical number Children axis flow: 0 or any Number as true
reverse number Children reverse flow: -1 or 1

Flow

To control the flow direction use one of the options on the parent node:

To use deck flow use the snap: "deck" option, it may be required for some animations.

Usage example

<head>
    <script type="module">
        import { slidy } from '@slidy/core';;
        import { linear } from '@slidy/easing';
        import { fade } from '@slidy/animation';

        const options = {
            index: 0,
            clamp: 0,
            indent: 1,
            sensity: 5,
            gravity: 1.2,
            duration: 375,
            animation: fade,
            easing: linear,
            snap: 'center',
            axis: 'x',
            loop: false,
        };
        
        const node = document.querySelector("#node");
        slidy(node, options);
    </script>
</head>

<section>
    <ul id="node">
        <!-- items -->
    </ul>
</section>

Events

Name Detail Description
mount {options} node.children.length and node.children are connected.
resize {ROE} The target node changed it’s size: ROE: ResizeObserverEntry[]
move {index,position} The sliding occured.
index {index} The index was changed.
keys {e.key} The key is pressed during the target node focus. Arrow keys behaviour is predefined for navigation with preventDefault used on them. To get the focus use tabIndex=0 attribute on the target node.
update {updated.options} Options were changed.
destroy {node} Targed node is unmounted from the DOM or destroy() method was used.

Events Usage Example

<head>
    <script type="module">
        import { slidy } from "@slidy/core";

        const node = document.querySelector("#node");
        slidy(document.querySelector("#node"));

        node.addEventListener("mount", e => console.log(e));
        node.onupdate = e => console.log(e.detail);

        function onMove(e) {
            const { index, position } = e.detail
            console.log(index, position)
        }
    </script>
</head>

<section>
    <ul id="node" onmove="onMove" tabindex="0">
        <!-- items -->
    </ul>
</section>

Methods

Name Description
to(index: number) Scrolls to index.
update({ option: value }) Update property in options.
destroy() Remove event listners, observers and default properties on slidy instance.

Methods Usage Example

<head>
    <script type="module">
        import { slidy } from "https://unpkg.com/@slidy/core/dist/index.mjs";

        const node = document.querySelector("#node");
        const prev = document.querySelector("#prev");
        const next = document.querySelector("#next");

        slidy(node);
        slidy.update({ snap: "center" });

        prev.onclick = () => slidy.to(index - 1);
        next.onclick = () => slidy.to(index + 1);
    </script>
</head>

<section>
    <ul id="node">
        <!-- items -->
    </ul>
</section>

<nav>
    <button id="prev"></button>
    <button id="next"></button>
</nav>

<!--
    If `slidy()` is defined in global scope, use global event handlers as attribures:

    <nav>
        <button onclick="slidy.to(index - 1)">←</button>
        <button onclick="slidy.to(index + 1)">→</button>
    </nav>
-->

Easing

Easing is an animation function, also known as timing function. It allows to create smooth sliding transitions.

/** Both input and output are numbers in range [0, 1].
type Easing = (t: number) => number;

By default an easing function is linear. Examples of easing function can be seen here.

The set of predefined easing functions is available as [@slidy/easing][separate package].

Animation

animation function allows to define a custom sliding animation.

interface AnimationArgs {
	node: HTMLElement;
  child: Child;
  options: Options;
  translate: string;
}

type AnimationFunc = (args: AnimationArgs) => CSSStyleDeclaration;

Functions receives 4 arguments:

Name Type Description
node HTMLElement slidy instance root DOM node.
child Child Extended childNode object.
options Options @slidy/core options subset.
translate string Basic translate required for any function { transform: translate }

child

Name Type Description
i number Child index in array.
index number Child index in core script.
active number Calculated as options.loop ? cix : options.index.
size number Calculated as size + gap by options.vertical.
dist number Snap position distance.
track number Move by slide size from its snap point in specified direction.
turn number Calculated as -1 <- child.track / child.size -> 1
exp number Interpolated child.track as 0 <- exp -> 1.

options

Name Type Description
index number Active slide index.
position number Current position value.
vertical number Children flow, calculated as 0 or any Number as true.
reverse number The children reverse flow state: `-1
snap Snap The snap position value: "start", "center", "end", "deck", undefined.

Animation presets

Predefined custom animations are available as separate package.

Limitations

  1. Children (slide items) absolute positioning should not be applied. While the script uses coordinates from the childNode, it breaks the script functionality completely;

  2. @slidy/core does not style the DOM, but keep in mind that there are some inline properties applied to the target node to provide functionality:

{
    outline: none;
    overflow: hidden;
    user-select: none;
}