0.8.1 • Published 5 years ago

@cdoublev/use-definition v0.8.1

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

CircleCI

use-definition

  1. About
  2. Installation
  3. Example
  4. API

About

use-definition abstracts animating the definition attribute of an SVG <path> in a React component.

It receives a collection of definitions, and returns a normalized value to render and a function to trigger a transition to another (normalized) definition from the given collection.

Demo: CodePen.

Features:

  • support of any command type in the definition
  • transformation between <path>s with different numbers of points
  • individual transformation of each point's parameters over time
  • chaining animations
  • render in a <canvas>
  • render in a static CSS file as CSS keyframes

This package has been created to overcome the limitations of the existing SVG animation libraries for this specific need. It's an alternative to GreenSock morphSVG plugin, which is not free and "React plug and play ready", but has some extra features, probably a better browsers support, and higher performances.

This package has no external dependency like SVGO to parse or normalize definitions, in order to avoid extra iterations on each definition and to optimize performances.

Installation

  npm i @cdoublev/use-definition

This package doesn't include a polyfill of requestAnimationFrame, which is required for IE < 10. You should include it yourself.

Example

    import useDefinition from '@cdoublev/use-definition'
    import React from 'react'
    import ReactDOM from 'react-dom'

    const MorphingPath = ({ definitions }) => {

        const [definition, animateTo] = useDefinition(definitions)
        const handleClick = () => animateTo('next')

        return (
            <a onClick={handleClick}>
                <svg viewBox='0 0 100 150'>
                    <path d={definition} fill='#0e1539' />
                </svg>
            </a>
        )
    }
    // "blob" shapes
    const definitions = [
        'M84 19c8 25-27 7-27 51s39 29 23 54S32 138 22 128S3 89 3 68s7-37 18-46S75-6 84 19z',
        'M82 22c11 25-33 36-11 71s-3 57-33 46S5 74 13 40 71-2 82 22z',
    ]

    ReactDOM.render(<MorphingPath definitions={definitions} />, document.getElementById('app'))

API

    import useDefinition, { timing } from '@cdoublev/use-definition`

useDefinition is the default export of this package. It's a React hook which has the following signature:

useDefinition :: ([Definition], Options?) -> [Definition, Function, State]

Arguments

Definition

[Definition] should be a collection of definition attributes of multiple SVG <path>s.

All command types are supported – m, l, h, v, s, c, q, t, a, z – either relative (lowercase) or absolute (uppercase). The only rule is that each <path> should not include a moving command (m or M) that is not the first command of the definition.

options (optional)

OptionsDescriptionDefault
delayDelay (in ms) to wait before animating all points.undefined
durationDuration (in ms) to animate all points.undefined
minDelayRandom minimum delay (not processed if delay is set).0
maxDelayRandom maximum delay (not processed if delay is set).1000
minDurationRandom minimum duration (not processed if duration is set).2000
maxDurationRandom maximum duration (not processed if duration is set).4000
precisionRounding precision.2
startIndexIndex of the first definition to render.0
timingTiming function used to animate the definition.easeOutCubic

Note: delay, duration and timing can also be defined per animation when calling animateTo.

When using a minimum and/or a maximum delay or duration, each point will be animated using a random value for the corresponding option. Points which have the same position will receive the same value.

When using delay and/or duration, random values will not be used for the corresponding option.

timing is a short word for timing function. It should be either a timing function or an alias to an available timing function (see the note below):

  • TimingFunction => String
  • TimingFunction :: Number -> Number
  • TimingFunction :: (Number, [Group, Group]) -> Group

It will be called at each frame, and will receive:

  • a relative time value (between 0 and 1)
  • a collection of two groups of parameters: the first group belongs to the initial definition, and the second belongs to the corresponding group in the definition to transition to

The behavior of this timing function should vary depending on its parameters length:

  • if it uses time as its sole argument, it should return a value relative to the intermediate value (between 0 and 1) that a parameter should have at the corresponding relative time
  • otherwise, it should directly return the intermediate group of parameters

For example, when time is 0.75, a linear timing function that receive a group of parameters going from { x: 0, y: 0 } to { x: 100, y: 100 } should return 0.75 if it uses time as its sole argument, otherwise { x: 75, y: 75 }.

Note on available timing functions:

This package has a timing named export that is a collection of popular timing functions such as ease, ease-in, ease-out, etc... Medium to heavy motion design projects usually involve custom timing functions. This collection exists for demonstration purpose. Replacing CSS with JavaScript to transition between paths using a linear or a cubic bezier function is non-sense (do it directly with CSS).

Visualisations: Codepen.

Return values

definition

The String returned by useDefinition can be named definition and should be used as the definition attribute value of the SVG <path> to render.

This component prop will be updated while transitionning to another definition.

animateTo

The Function returned by useDefinition can be named animateTo and has the following signature:

animateTo :: (Number|String, Options?) -> Future

The first argument should be the index of the definition to transition to, or a convenient 'next' or 'prev' alias that will be resolved to the next or prev index after the current index, starting over at the first or last when required.

The second argument can be used to override some of the global options defined when calling useDefinition.

animateTo returns an object which is a Folktale's Future that can be used to .map() a callback and .chain() animation(s).

Executing a callback after the end of an animation:

    animateTo(2).map(() => console.log('transition to index 2: done'))

Chaining animations:

    const log = state => console.log(`${state} transition`)
    animateTo(2)
        .map(() => log('Start'))
        .chain(() => animateTo(3))
        .map(() => log('End'))

A convenient (but experimental) .to() interface can be used as an alias of .chain(() => animateTo()), ie.:

    animateTo(2).to(3)

Experimental: in addition to .to(), pause() and resume() can be used to pause and resume an animation.

state

The State returned by useDefinition can be named state and has the following type:

State => { currentIndex: Number, isAnimated: Boolean, nextIndex: Number }}

Those component props can be used eg. to prevent starting a new animation if the previous one is not over, or to set a CSS class name to an HTML/SVG element.

currentIndex will be updated after the last animation frame, with the nextIndex defined in state. nextIndex will be updated before the first animation frame, to the index of the definition to transition to. isAnimated will be updated to true before the first animation frame, and to false after the last one.

TODO

  • Performances: measure performances of each processing task
  • Performances: use a transducer to parse, normalize, and configure each command/point of a path
  • Performances: consider rendering in a canvas
  • Feature: implement pause(), resume(), restart(), stop() interfaces
  • Feature: export definitions as static CSS keyframes
0.8.1

5 years ago

0.8.0

5 years ago

0.7.3

5 years ago

0.7.2

5 years ago

0.7.1

5 years ago

0.7.0

5 years ago

0.6.1

5 years ago

0.6.0

5 years ago

0.5.0

5 years ago

0.4.0

5 years ago

0.3.0

5 years ago

0.2.1

5 years ago

0.2.0

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago

0.1.0

5 years ago