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>
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 withdata-startwhose 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 firstlength.data-clip-exit="kind [direction] length [ease]"applies the outgoing styles over the clip's lastlength, ending at the clip's end. A clip with nodata-durationordata-endends 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-toporfrom-bottomfor the kinds that slide or wipe and forhalftone, andclockwiseoranticlockwiseforclock. - The map kinds take tokens of their own (Luma transitions).
lumatakes its map asurl(path), andinvertandsoft. halftonetakes a cell in px (24px), an angle in degrees (45deg) andsoft, anddissolvetakes a grain in px (3px) andsoft.- A length is seconds as a number (
0.6), seconds withs(0.6s), or frames withf(18f, divided by the composition's fps).data-clip-transitiontakes none, since its length is the overlap. - An ease is any name
ease()accepts,cubic-bezier(…)included. The default ispower2.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'sz-index, and at p = 0 the outgoing clip none exceptreveal's. - A clip can have
data-clip-transitionanddata-clip-exit, but notdata-clip-transitionanddata-clip-enter. - When two transitions cover one clip at the same time, such as a
data-clip-exitand the outgoing side of the next clip'sdata-clip-transition, the clip gets the properties of both. Where both writeopacity, the two multiply. - Kinetic applies the transitions clip by clip in markup order, and on each clip its
data-clip-transition, then itsdata-clip-enter, then itsdata-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 butluma. lumawith nourl(), or two of one token.- A length on
data-clip-transition. - A
data-clip-enterordata-clip-exitwith no length, or one longer than the clip. - A clip with both
data-clip-transitionanddata-clip-enter. - A
data-clip-enterordata-clip-exiton an element with nodata-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.
| Kind | Incoming | Outgoing | Default direction |
|---|---|---|---|
fade | opacity 0 to 1 | opacity 1 to 0 | none |
push | translate from 100% on the side to 0 | translate from 0 to 100% on the opposite side | from-right |
cover | slides in as push, over the outgoing clip, with z-index: 1 | stays | from-right |
reveal | stays, under the outgoing clip | slides out as push, with z-index: 1 | from-right |
wipe | clip-path: inset() opens from the side | stays | from-left |
iris | clip-path: circle() grows from the center to the corners | stays | none |
clock | clip-path: polygon() sweeps around the center from 12 o'clock | stays | clockwise |
zoom | scale 1.15 to 1, opacity 0 to 1 | scale 1 to 0.9, opacity 1 to 0 | none |
blur | filter: blur() 16px to 0, opacity 0 to 1 | filter: blur() 0 to 16px, opacity 1 to 0 | none |
flip | rotate: y from 90deg to 0 in the second half | rotate: y from 0 to -90deg in the first half | none |
luma | a filter shows the clip where an image's brightness is under a threshold that rises from black to white | stays; in data-clip-exit, the clip goes dark parts first | none |
halftone | the same, with a map of dots on a grid, which open from their centers from one side or all at once | stays; in data-clip-exit, the dots close | none: every dot at once |
dissolve | the same, with seeded noise, so the clip comes in grain by grain | stays; in data-clip-exit, the clip goes grain by grain | none |
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
.layerclass in the patterns gives. - The
clockhand reaches each corner at a multiple of 45 degrees whatever the box's shape, since its angles are taken in the box's percentages. fliplooks three-dimensional only underperspectiveon 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>
| Kind | Map | Tokens |
|---|---|---|
luma | The 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. |
halftone | A 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. |
dissolve | Value 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/kat p = 0 to1 + 1/kat 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 withsoft, an edge about an eighth of the map's tones wide. - In
data-clip-exitthe mask is 1 minus that, so the clip goes in the map's order. Indata-clip-transitionthe mask is on the incoming clip only, and the outgoing clip stays under it, as withwipe. - Kinetic adds one hidden
<svg>to the composition root, with a<filter>for each clip and attribute that uses one of these kinds. It writesfilter: 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
halftoneanddissolvemaps 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, socheck()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
dissolveclip anid. Without one its seed is its place among the composition's[data-start]elements, so a clip added before it changes its noise. - A
lumamap is fetched at readiness, and bycheck()andrender(). For a clip added after readiness, the map arrives with the nextready(),check()orrender(), 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 needsAccess-Control-Allow-Originfrom its server, since a filter draws nothing from another origin's image. blurand these three all writefilter. 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,scaleandrotate, the individual transform properties, and nevertransform. The browser applies those three beforetransform, so a transition composes with atransformfrom the clip's author or akeyframestrack. - It writes
opacity,clip-path,filterand, forcoverandreveal,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 andrender()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.
| Option | Default | Sets |
|---|---|---|
direction | the kind's (The kinds) | The side or the turn, for a kind that takes one. |
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; },
},
});
inis{}at p = 1 andoutis{}at p = 0, apart fromreveal'szIndex: 1.luma,halftoneanddissolvethrow 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.