Transitions

This page covers the data-clip-transition, data-clip-enter and data-clip-exit attributes, each transition kind, the luma, halftone and dissolve masks, the styles Kinetic writes on a clip, and the transition() helper for elements that are not clips.

data-clip-transition goes on the incoming clip and turns its overlap with the clip it replaces into a transition of the same length. Kinetic writes the transition's styles inline on the clip's own tag, so a binding or a root: block that writes the same property there overwrites it. Development builds warn about a bad attribute (KN-115), and Kinetic leaves the clip as it is.

Transitions

A clip that starts before the clip it replaces ends overlaps it, and data-clip-transition turns the overlap into a transition of the same length. data-clip-enter and data-clip-exit give a clip an entrance or exit of its own length, with nothing on the other side. Clips can be scenes, plain HTML or anything with data-start.

<div id="intro" class="layer" data-start="0" data-duration="3">…</div>
<div class="layer" data-start="intro - 0.5" data-duration="4" data-clip-transition="push from-right power3.inOut">…</div>

<h1 data-start="1" data-duration="4" data-clip-enter="wipe from-left 0.6" data-clip-exit="fade 12f">Launch</h1>
Clip B carries the attribute shown, and as an exit the kinds whose outgoing side stays (cover, wipe, iris, clock) leave it as it is; click the stage to pause.

The three attributes carry data-clip- so they never meet the framework's own data-transition.

The attributes

  • data-clip-transition="kind [direction] [ease]" goes on the incoming clip. It replaces the one sibling with data-start whose window holds this clip's start and ends before this clip ends, so a backdrop that runs past this clip is never the one. The transition runs from this clip's start to the end of the other's window, with the kind's incoming styles on this clip and its outgoing styles on the other.
  • data-clip-enter="kind [direction] length [ease]" applies the incoming styles over the clip's first length.
  • data-clip-exit="kind [direction] length [ease]" applies the outgoing styles over the clip's last length, ending at the clip's end. A clip with no data-duration or data-end ends with the composition.

The attributes are the default way to fade a clip, with no scene code; see Which to use for when edges and presence fit instead.

The kind comes first, and the other tokens follow in any order.

  • A direction is from-left, from-right, from-top or from-bottom for the kinds that slide or wipe and for halftone, and clockwise or anticlockwise for clock.
  • The map kinds take tokens of their own (Luma transitions). luma takes its map as url(path), and invert and soft.
  • halftone takes a cell in px (24px), an angle in degrees (45deg) and soft, and dissolve takes a grain in px (3px) and soft.
  • A length is seconds as a number (0.6), seconds with s (0.6s), or frames with f (18f, divided by the composition's fps). data-clip-transition takes none, since its length is the overlap.
  • An ease is any name ease() accepts, cubic-bezier(…) included. The default is power2.inOut.

Progress, overlaps and warnings

Progress runs from 0 on the window's first frame to 1 on its last captured frame, 1/fps before the window ends, as a scene's progress does. There the outgoing clip is at its final state, and on the next frame its window hides it.

  • At p = 1 the incoming clip has no transition style except cover's z-index, and at p = 0 the outgoing clip none except reveal's.
  • A clip can have data-clip-transition and data-clip-exit, but not data-clip-transition and data-clip-enter.
  • When two transitions cover one clip at the same time, such as a data-clip-exit and the outgoing side of the next clip's data-clip-transition, the clip gets the properties of both. Where both write opacity, the two multiply.
  • Kinetic applies the transitions clip by clip in markup order, and on each clip its data-clip-transition, then its data-clip-enter, then its data-clip-exit. For any other property two of them write, the one applied last is used.

Development builds warn once per clip (KN-115), and Kinetic leaves the clip as it is, for each of these.

  • An unknown kind or token, or a token the kind does not take, such as a url() on any kind but luma.
  • luma with no url(), or two of one token.
  • A length on data-clip-transition.
  • A data-clip-enter or data-clip-exit with no length, or one longer than the clip.
  • A clip with both data-clip-transition and data-clip-enter.
  • A data-clip-enter or data-clip-exit on an element with no data-start.

A data-clip-transition that overlaps no sibling, or more than one, is KN-116.

The kinds

Positions are percentages of the clip's own box, and p is the eased progress.

KindIncomingOutgoingDefault direction
fadeopacity 0 to 1opacity 1 to 0none
pushtranslate from 100% on the side to 0translate from 0 to 100% on the opposite sidefrom-right
coverslides in as push, over the outgoing clip, with z-index: 1staysfrom-right
revealstays, under the outgoing clipslides out as push, with z-index: 1from-right
wipeclip-path: inset() opens from the sidestaysfrom-left
irisclip-path: circle() grows from the center to the cornersstaysnone
clockclip-path: polygon() sweeps around the center from 12 o'clockstaysclockwise
zoomscale 1.15 to 1, opacity 0 to 1scale 1 to 0.9, opacity 1 to 0none
blurfilter: blur() 16px to 0, opacity 0 to 1filter: blur() 0 to 16px, opacity 1 to 0none
fliprotate: y from 90deg to 0 in the second halfrotate: y from 0 to -90deg in the first halfnone
lumaa filter shows the clip where an image's brightness is under a threshold that rises from black to whitestays; in data-clip-exit, the clip goes dark parts firstnone
halftonethe same, with a map of dots on a grid, which open from their centers from one side or all at oncestays; in data-clip-exit, the dots closenone: every dot at once
dissolvethe same, with seeded noise, so the clip comes in grain by grainstays; in data-clip-exit, the clip goes grain by grainnone

cover sets z-index: 1 on the incoming clip for the whole overlap, p = 1 included, so it comes in over the outgoing one wherever each sits in the markup. reveal sets it on the outgoing clip for the whole overlap, p = 0 included.

  • Both remove it after, and both need positioned clips, as the .layer class in the patterns gives.
  • The clock hand reaches each corner at a multiple of 45 degrees whatever the box's shape, since its angles are taken in the box's percentages.
  • flip looks three-dimensional only under perspective on the clips' common parent, and without it the clips narrow and widen flat.
#stage { perspective: 1600px; }   /* the clips' common parent, for flip */

See Which to use for the blur kind beside the other ways to blur.

Luma transitions

luma, halftone and dissolve mask the clip with a grayscale map. The clip shows where the map is darker than a threshold that sweeps through the map's tones as the eased progress runs from 0 to 1, so the map's dark parts come in first.

<div class="layer" data-start="intro - 0.8" data-duration="5" data-clip-transition="halftone from-left 32px power2.inOut">…</div>
<div class="layer" data-start="8" data-duration="4" data-clip-enter="luma url(maps/burn.png) soft 1s">…</div>
<div class="layer" data-start="12" data-duration="4" data-clip-exit="dissolve 20f">…</div>
Clip B comes in over clip A through the halftone or dissolve map shown, with luma left out since it needs a map image; click the stage to pause.
KindMapTokens
lumaThe brightness of the image in url(path), stretched over the clip's box.url(path), which it needs; invert, which reads the map's tones the other way round, light parts first; soft.
halftoneA cone at each dot of a grid turned by the angle, 0 at its center and 1 at its cell's corners, so each dot opens from its center. With a direction, the map is 0.55 of that plus 0.45 of a ramp from 0 on the given side to 1 on the other, so the dots open from that side.A direction; the cell in px, 28px by default; the angle in degrees, 45deg by default; soft.
dissolveValue noise from Kinetic's seeded hash at the grain's size, seeded from the clip's id, else its place among the clips.The grain in px, 2px by default; soft.

The mask, the filter and the maps

  • The mask is clamp(k × (threshold − map)), with the threshold running from −1/k at p = 0 to 1 + 1/k at p = 1, so the clip is hidden on the window's first frame and shown on its last. k is 30, a sharp edge, or 8 with soft, an edge about an eighth of the map's tones wide.
  • In data-clip-exit the mask is 1 minus that, so the clip goes in the map's order. In data-clip-transition the mask is on the incoming clip only, and the outgoing clip stays under it, as with wipe.
  • Kinetic adds one hidden <svg> to the composition root, with a <filter> for each clip and attribute that uses one of these kinds. It writes filter: url(#kinetic-map-N) on the clip inside the window only, and on each seek it changes only the filter's map and threshold.
  • The filter's region is the clip's own box, so anything the clip draws outside it, such as overflowing children, a shadow or an outline, is cut off during the transition. A clip with an empty box, such as a wrapper with no height around positioned children, shows nothing during its transition, and development builds warn (KN-115).
  • The halftone and dissolve maps are drawn as PNGs at the clip's size, at readiness and once for each new size the clip takes during its transition. Each is the same PNG on every page load, so check() and every render agree.
  • Each size's PNG is kept for the life of the page, since check() compares the filter's URL from seek to seek, so a clip whose size animates keeps one PNG per size it has taken.
  • Give a dissolve clip an id. Without one its seed is its place among the composition's [data-start] elements, so a clip added before it changes its noise.
  • A luma map is fetched at readiness, and by check() and render(). For a clip added after readiness, the map arrives with the next ready(), check() or render(), and until then the clip stays hidden through its transition.
  • A map that fails to load or decode is a [KN-117] error on the console in every build, and readiness holds (Failure). A map from another origin needs Access-Control-Allow-Origin from its server, since a filter draws nothing from another origin's image.
  • blur and these three all write filter. For two of them on one clip at the same time, the order of the attributes above decides which is used.

What Kinetic writes

  • Kinetic writes translate, scale and rotate, the individual transform properties, and never transform. The browser applies those three before transform, so a transition composes with a transform from the clip's author or a keyframes track.
  • It writes opacity, clip-path, filter and, for cover and reveal, z-index, inline, only inside the window. Outside the window each property comes off, and the clip's inline value from before Kinetic first wrote it comes back, as does a value written on the clip between two seeks inside the window.
  • Values have at most four decimals.
  • It applies them on every seek, with or without a runtime on the page, and once before the page is shown, so check() compares them and render() captures them.

A binding on the clip's own tag, or a scene's root: block, that writes one of these properties on the same clip overwrites the transition, and the transition overwrites it on the next seek. Put the scene's own styling on an element inside the scene and leave the clip's tag to the transition. A slow zoom on an incoming iris, for one, goes on a layer inside the clip, so it scales the picture and the circle on the clip keeps the clip's size.

  • Development builds warn once per clip when a property a transition wrote was written again during the same seek, or between two seeks inside the window (KN-156).

transition(kind, p, options)

transition(kind, p, { direction }) returns { in, out }, the two style objects of a kind at p, the progress the caller has already eased.

OptionDefaultSets
directionthe kind's (The kinds)The side or the turn, for a kind that takes one.
Clip B comes in over clip A by the call shown, with p eased by ramp() from one second in, and holds to the end of the loop. Click the stage to pause.

An unknown kind, a direction the kind does not take, or a p that is not a finite number throws KN-100.

The values are the ones the attributes write, with camelCase keys (clipPath, zIndex), for data-bind-style on anything that is no clip, such as a layer inside a scene or a card in a list.

const { scene, transition, ramp } = wildflower.kinetic;

scene('quiz', {
    computed: {
        turn() { return transition('flip', ramp(this.local, 1.6, 2.2, 'power2.inOut')); },
        question() { return this.turn.out; },
        answer() { return this.turn.in; },
    },
});
  • in is {} at p = 1 and out is {} at p = 0, apart from reveal's zIndex: 1.
  • luma, halftone and dissolve throw KN-100 too, since their mask is a filter that needs a clip.

A clip fades by its attributes, with no helper; see Which to use.