@infinityfx/lively 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/animatable.js ADDED
@@ -0,0 +1,300 @@
1
+ import { Children, cloneElement, Component, isValidElement } from 'react';
2
+ import Animation from './animation';
3
+
4
+ // on window resize reset initial elements sizes
5
+ // implement keyframe position (not all evenly spaced)
6
+ // mabye split whileViewport up into onEnter and onLeave
7
+ // animate things like background color
8
+ // implement repeat argument (and maybe repeat delay)
9
+
10
+ export default class Animatable extends Component {
11
+
12
+ constructor(props) {
13
+ super(props);
14
+
15
+ this.hover = false;
16
+ this.hasFocus = false;
17
+ this.inView = false;
18
+ this.scrollDelta = 0;
19
+ this.viewportMargin = props.viewportMargin;
20
+
21
+ this.elements = [];
22
+ this.animations = {
23
+ animate: this.getAnimation(),
24
+ onMount: this.getAnimation('onMount'),
25
+ onUnmount: this.getAnimation('onUnmount'),
26
+ onClick: this.getAnimation('onClick'),
27
+ whileHover: this.getAnimation('whileHover'),
28
+ whileFocus: this.getAnimation('whileFocus'),
29
+ whileViewport: this.getAnimation('whileViewport')
30
+ };
31
+
32
+ this.level = 0;
33
+ this.children = [];
34
+ }
35
+
36
+ getAnimation(name = 'animate') {
37
+ if (name === 'animate' && this.props.animation && (typeof this.props.animation === 'object' || typeof this.props.animation === 'function') && 'use' in this.props.animation) {
38
+ return this.props.animation.use();
39
+ }
40
+
41
+ return Animation.from(this.props[name], this.props.initial, this.props.scaleCorrection);
42
+ }
43
+
44
+ countNestedLevels(children) {
45
+ if (!children) return 0;
46
+
47
+ let count = 0, nested = 0;
48
+ Children.forEach(children, (child) => {
49
+ if (!isValidElement(child)) return;
50
+ if (child.type === Animatable) count = 1;
51
+
52
+ const n = this.countNestedLevels(child.props?.children);
53
+ nested = nested < n ? n : nested;
54
+ });
55
+
56
+ return nested + count;
57
+ }
58
+
59
+ addEvent(event, callback) {
60
+ if (!(callback instanceof Function)) return;
61
+
62
+ if (!window.UITools?.Events) window.UITools = { Events: {} };
63
+ if (!(event in window.UITools.Events)) {
64
+ window.UITools.Events[event] = { unique: 0 };
65
+ window.addEventListener(event, e => {
66
+ Object.values(window.UITools.Events[event]).forEach(cb => {
67
+ if (cb instanceof Function) cb(e);
68
+ })
69
+ });
70
+ }
71
+
72
+ callback.UITools = { ListenerID: window.UITools.Events[event].unique };
73
+ window.UITools.Events[event][window.UITools.Events[event].unique] = callback;
74
+ window.UITools.Events[event].unique++;
75
+ }
76
+
77
+ removeEvent(event, callback) {
78
+ if (!(event in window.UITools?.Events)) return;
79
+ if (!('ListenerID' in callback.UITools)) return;
80
+
81
+ delete window.UITools.Events[event][callback.UITools.ListenerID];
82
+ }
83
+
84
+ componentDidMount() {
85
+ this.elements.forEach(el => {
86
+ this.animations.animate?.setInitialStyles(el);
87
+ });
88
+
89
+
90
+ if (this.props.parentLevel < 1 || this.props.noCascade) {
91
+ this.scrollEventListener = this.onScroll.bind(this);
92
+ this.addEvent('scroll', this.scrollEventListener);
93
+
94
+ if (this.props.onMount) this.play('onMount', { staggerDelay: 0.001 });
95
+ if (this.props.whileViewport) this.onScroll();
96
+ }
97
+ }
98
+
99
+ componentWillUnmount() {
100
+ this.removeEvent('scroll', this.scrollEventListener);
101
+
102
+ if (this.props.onUnmount && (this.props.parentLevel < 1 || this.props.noCascade)) this.play('onUnmount', { reverse: true, immediate: true });
103
+ }
104
+
105
+ inViewport() {
106
+ let entered = true, left = true;
107
+
108
+ this.elements.forEach(el => {
109
+ const { y } = el.getBoundingClientRect();
110
+ entered = entered && y + el.clientHeight * (1 - this.viewportMargin) < window.innerHeight;
111
+ left = left && y > window.innerHeight + el.clientHeight * this.viewportMargin;
112
+ });
113
+
114
+ if (!this.elements.length) {
115
+ this.children.forEach(({ animatable }) => {
116
+ const [nestedEntered, nestedLeft] = animatable.inViewport();
117
+ entered = entered && nestedEntered;
118
+ left = left && nestedLeft;
119
+ });
120
+ }
121
+
122
+ return [entered, left];
123
+ }
124
+
125
+ async onScroll() {
126
+ if (Date.now() - this.scrollDelta < 350) return;
127
+ this.scrollDelta = Date.now();
128
+
129
+ let [entered, left] = this.inViewport();
130
+
131
+ if (!this.inView && entered) {
132
+ this.inView = true;
133
+ if (this.props.whileViewport) this.play('whileViewport');
134
+ }
135
+
136
+ if (this.inView && left) {
137
+ this.inView = false;
138
+ if (this.props.whileViewport) this.play('whileViewport', { reverse: true, immediate: true });
139
+ }
140
+ }
141
+
142
+ async onEnter(e, callback = false) {
143
+ if (!this.hover) {
144
+ if (this.props.whileHover) this.play('whileHover');
145
+ this.hover = true;
146
+ }
147
+
148
+ if (callback) callback(e);
149
+ }
150
+
151
+ async onLeave(e, callback = false) {
152
+ if (this.hover) {
153
+ if (this.props.whileHover) this.play('whileHover', { reverse: true });
154
+ this.hover = false;
155
+ }
156
+
157
+ if (callback) callback(e);
158
+ }
159
+
160
+ async onFocus(e, callback = false) {
161
+ if (!this.hasFocus) {
162
+ if (this.props.whileFocus) this.play('whileFocus');
163
+ this.hasFocus = true;
164
+ }
165
+
166
+ if (callback) callback(e);
167
+ }
168
+
169
+ async onBlur(e, callback = false) {
170
+ if (this.hasFocus) {
171
+ if (this.props.whileFocus) this.play('whileFocus', { reverse: true });
172
+ this.hasFocus = false;
173
+ }
174
+
175
+ if (callback) callback(e);
176
+ }
177
+
178
+ async onClick(e, callback = false) {
179
+ if (this.props.onClick) this.play('onClick');
180
+
181
+ if (callback) callback(e);
182
+ }
183
+
184
+ async play(animationName, { reverse = false, immediate = false, cascade = false, groupAdjust = 0, cascadeDelay = 0, staggerDelay = 0 } = {}) {
185
+ if (this.props.parentLevel > 0 && !cascade) return;
186
+
187
+ let animation = this.animations[animationName];
188
+ if (!animation) animation = this.animations.animate;
189
+ if (!animation) return;
190
+
191
+ this.elements.forEach((el, i) => {
192
+ let offset = 'group' in this.props ? this.props.parentLevel - this.props.group : this.level + groupAdjust;
193
+ cascadeDelay = reverse ? animation.duration : cascadeDelay; // NOT FULLY CORRECT (also take into account reverse staggering)
194
+ const delay = reverse ? offset * cascadeDelay : (this.props.parentLevel - offset) * cascadeDelay;
195
+
196
+ animation.play(el, {
197
+ delay: this.props.stagger * i + delay + staggerDelay,
198
+ reverse,
199
+ immediate
200
+ });
201
+ });
202
+
203
+ this.children.forEach(({ animatable, staggerIndex = -1 }) => {
204
+ animatable.play(animationName, {
205
+ reverse,
206
+ immediate,
207
+ cascade: true,
208
+ staggerDelay: staggerIndex < 0 ? 0 : this.props.stagger * staggerIndex,
209
+ cascadeDelay: animation.duration,
210
+ groupAdjust: staggerIndex < 0 ? 0 : 1
211
+ });
212
+ });
213
+ }
214
+
215
+ style(inherited = {}) {
216
+ const styles = {
217
+ ...inherited,
218
+ transitionProperty: `transform, opacity, clip-path, border-radius${this.props.scaleCorrection ? ', width, height, left, top' : ''}`,
219
+ willChange: 'transform'
220
+ };
221
+
222
+ return styles;
223
+ }
224
+
225
+ mergeProperties(own = {}, passed = {}) {
226
+ const merged = {};
227
+ merged.initial = this.mergeProperty(passed.initial, own.initial);
228
+ merged.animate = this.mergeProperty(passed.animate, own.animate);
229
+
230
+ merged.onMount = this.mergeProperty(passed.onMount, own.onMount);
231
+ merged.onUnmount = this.mergeProperty(passed.onUnmount, own.onUnmount);
232
+ merged.onClick = this.mergeProperty(passed.onClick, own.onClick);
233
+ merged.whileHover = this.mergeProperty(passed.whileHover, own.whileHover);
234
+ merged.whileFocus = this.mergeProperty(passed.whileFocus, own.whileFocus);
235
+ merged.whileViewport = this.mergeProperty(passed.whileViewport, own.whileViewport);
236
+
237
+ merged.viewportMargin = this.mergeProperty(passed.viewportMargin, own.viewportMargin);
238
+ merged.stagger = this.mergeProperty(passed.stagger, own.stagger);
239
+
240
+ return merged;
241
+ }
242
+
243
+ mergeProperty(a, b) {
244
+ if (!a) return b;
245
+ if (!b) return a;
246
+
247
+ if (typeof a === 'object' || typeof b === 'object') return { ...a, ...b };
248
+
249
+ return b;
250
+ }
251
+
252
+ deepClone(component, { index = 0, useElements = false, useEvents = false } = {}) {
253
+ if (!isValidElement(component)) return component;
254
+
255
+ let props = {};
256
+ if (component.type !== Animatable) {
257
+ if (useElements) props = { style: this.style(component.props?.style), ref: el => this.elements[index] = el };
258
+
259
+ if (useEvents && (this.props.parentLevel < 1 || this.props.noCascade)) {
260
+ props = {
261
+ ...props,
262
+ onMouseEnter: e => this.onEnter(e, component.props?.onMouseEnter),
263
+ onMouseLeave: e => this.onLeave(e, component.props?.onMouseLeave),
264
+ onFocus: e => this.onFocus(e, component.props?.onFocus),
265
+ onBlur: e => this.onBlur(e, component.props?.onBlur),
266
+ onClick: e => this.onClick(e, component.props?.onClick),
267
+ };
268
+ useEvents = false;
269
+ }
270
+ }
271
+
272
+ if (component.type === Animatable && !component.props?.noCascade) {
273
+ props = {
274
+ ...props,
275
+ ...this.mergeProperties(component.props, this.props),
276
+ parentLevel: this.parentLevel > 0 ? this.parentLevel : this.level,
277
+ ref: el => this.children[this.children.length] = { animatable: el, staggerIndex: useElements ? index : -1 }
278
+ };
279
+ }
280
+
281
+ const children = Children.map(component.props.children, (child, i) => this.deepClone(child, { index: i, useEvents }));
282
+
283
+ return Object.values(props).length ? cloneElement(component, props, children) : component; // CHECK IF CORRECT
284
+ }
285
+
286
+ render() {
287
+ this.level = this.countNestedLevels(this.props.children);
288
+
289
+ return Children.map(this.props.children, (child, i) => this.deepClone(child, { index: i, useElements: true, useEvents: true }));
290
+ }
291
+
292
+ static defaultProps = {
293
+ scaleCorrection: false,
294
+ parentLevel: 0,
295
+ stagger: 0.1,
296
+ viewportMargin: 0.25,
297
+ animate: {}
298
+ }
299
+
300
+ }
package/animate.js ADDED
@@ -0,0 +1,43 @@
1
+ import { Children, cloneElement, Component, isValidElement } from 'react';
2
+ import Animatable from './animatable';
3
+ import Move from './animations/move';
4
+ import Pop from './animations/pop';
5
+
6
+ export default class Animate extends Component {
7
+
8
+ constructor(props) {
9
+ super(props);
10
+
11
+ this.levels = this.props.levels;
12
+ this.animations = new Array(this.levels).fill(0).map((_, i) => {
13
+ return i < this.props.animations.length ? this.props.animations[i] : this.props.animations[this.props.animations.length - 1];
14
+ });
15
+ }
16
+
17
+ makeAnimatable(children, level = 1) {
18
+ if (level < 1 || Children.count(children) < 1) return children;
19
+
20
+ const { levels, animations, ...props } = this.props;
21
+ const animation = this.animations[this.levels - level];
22
+
23
+ return <Animatable animation={animation} scaleCorrection={animation.scaleCorrection} {...props}>
24
+ {Children.map(children, child => {
25
+ if (!isValidElement(child)) return child;
26
+
27
+ return cloneElement(child, {}, this.makeAnimatable(child.props.children, level - 1));
28
+ })}
29
+ </Animatable>;
30
+ }
31
+
32
+ render() {
33
+ return this.makeAnimatable(this.props.children, this.levels);
34
+ }
35
+
36
+ static defaultProps = {
37
+ levels: 1,
38
+ stagger: 0.1,
39
+ viewportMargin: 0.25,
40
+ animations: [Move, Pop]
41
+ }
42
+
43
+ }
package/animation.js ADDED
@@ -0,0 +1,333 @@
1
+ import AnimationQueue from './queue';
2
+
3
+ export default class Animation {
4
+
5
+ static initials = {
6
+ opacity: 1,
7
+ scale: { x: 1, y: 1 },
8
+ position: { x: 0, y: 0 },
9
+ clip: { left: 0, top: 0, right: 0, bottom: 0 },
10
+ borderRadius: 0,
11
+ active: { value: true, at: 0 }
12
+ }
13
+
14
+ constructor({ delay = 0, duration = 1, loop = false, interpolate = 'ease', origin = { x: 0.5, y: 0.5 }, scaleCorrection = false, ...properties } = {}, initial = {}) {
15
+ this.scaleCorrection = scaleCorrection;
16
+ this.keyframes = this.getKeyframes(properties, initial);
17
+
18
+ this.delay = delay;
19
+ this.duration = duration;
20
+ this.delta = duration / (this.keyframes.length - 1);
21
+ this.interpolation = interpolate;
22
+ this.origin = this.originToStyle(origin);
23
+
24
+ this.loop = loop;
25
+ }
26
+
27
+ static from(options, initial = {}, scaleCorrection = false) {
28
+ if (!options || typeof options === 'boolean') return null;
29
+
30
+ return new Animation({ ...options, scaleCorrection }, initial);
31
+ }
32
+
33
+ originToStyle(origin) {
34
+ let x = 0.5, y = 0.5;
35
+
36
+ if (typeof origin === 'object') {
37
+ x = origin.x;
38
+ y = origin.y;
39
+ } else
40
+ if (typeof origin === 'string') {
41
+ switch (origin) {
42
+ case 'left':
43
+ x = 0;
44
+ break;
45
+ case 'right':
46
+ x = 1;
47
+ break;
48
+ case 'top':
49
+ y = 0;
50
+ break;
51
+ case 'bottom':
52
+ y = 1;
53
+ case 'center':
54
+ break;
55
+ default:
56
+ x = y = parseFloat(origin);
57
+ }
58
+ } else {
59
+ x = y = origin;
60
+ }
61
+
62
+ return `${x * 100}% ${y * 100}%`;
63
+ }
64
+
65
+ getKeyframes(properties, initial = {}) {
66
+ for (const key in properties) {
67
+ let first = key in initial ? initial[key] : Animation.initials[key];
68
+
69
+ if (Array.isArray(properties[key])) {
70
+ properties[key] = properties[key].length > 1 ? properties[key] : [first, ...properties[key]];
71
+ } else {
72
+ properties[key] = [first, properties[key]];
73
+ }
74
+ }
75
+
76
+ const len = Object.values(properties).reduce((len, arr) => arr.length > len ? arr.length : len, 0);
77
+ let keyframes = new Array(len).fill(0);
78
+
79
+ keyframes = keyframes.map((_, i) => {
80
+ const keyframe = {};
81
+
82
+ for (const key in properties) {
83
+ keyframe[key] = this.interpolateKeyframe(properties[key], i, len);
84
+ }
85
+
86
+ return this.keyframeToStyle(keyframe);
87
+ });
88
+
89
+ return keyframes;
90
+ }
91
+
92
+ interpolate(from, to, n) { // when interpolating pure strings fix!!! (also bools)
93
+ let unit = typeof from === 'string' ? from.match(/[^0-9\.]*/i) : null;
94
+ if (typeof to === 'string' && !unit) unit = to.match(/[^0-9\.]*/i);
95
+
96
+ from = parseFloat(from);
97
+ to = parseFloat(to);
98
+
99
+ const res = from * (1 - n) + to * n;
100
+ return unit ? res + unit : res;
101
+ }
102
+
103
+ interpolateKeyframe(property, i, len) {
104
+ if (!property) return null;
105
+ if (property.length === len) return property[i];
106
+
107
+ const idx = i * ((property.length - 1) / (len - 1));
108
+ const absIdx = Math.floor(idx);
109
+
110
+ let from = property[absIdx];
111
+ let to = absIdx === property.length - 1 ? null : property[absIdx + 1];
112
+ if (!to) return from;
113
+
114
+ if (typeof from === 'object') {
115
+ const obj = {};
116
+ Object.keys(from).forEach(key => {
117
+ obj[key] = this.interpolate(from[key], to[key], idx - absIdx);
118
+ });
119
+
120
+ return obj;
121
+ }
122
+
123
+ return this.interpolate(from, to, idx - absIdx);
124
+ }
125
+
126
+ propertyToString(property, value, key, unit) {
127
+ value = value[key];
128
+ if (typeof value === 'string') return value;
129
+
130
+ value = isNaN(value) ? Animation.initials[property][key] : value;
131
+ return `${value * (unit === '%' ? 100 : 1)}${unit}`;
132
+ }
133
+
134
+ propertyToNumber(property, value, key) {
135
+ value = value[key];
136
+ if (typeof value === 'string') {
137
+ return value.match(/[^0-9\.]*/i) === '%' ? parseFloat(value) / 100 : value;
138
+ }
139
+
140
+ return isNaN(value) ? Animation.initials[property][key] : value;
141
+ }
142
+
143
+ keyframeToStyle(keyframe) {
144
+ let properties = {
145
+ transform: ''
146
+ };
147
+
148
+ Object.entries(keyframe).forEach(([key, val]) => {
149
+ if (val === null || val === undefined) return;
150
+
151
+ switch (key) {
152
+ case 'position':
153
+ properties.transform += `translate(${this.propertyToString(key, val, 'x', 'px')}, ${this.propertyToString(key, val, 'y', 'px')}) `;
154
+ break;
155
+ case 'scale':
156
+ val = typeof val !== 'object' ? { x: val, y: val } : val;
157
+
158
+ if (this.scaleCorrection) {
159
+ properties.width = this.propertyToNumber(key, val, 'x');
160
+ properties.height = this.propertyToNumber(key, val, 'y');
161
+ break;
162
+ }
163
+
164
+ properties.transform += `scale(${this.propertyToString(key, val, 'x', '%')}, ${this.propertyToString(key, val, 'y', '%')}) `;
165
+ break;
166
+ case 'rotation':
167
+ properties.transform += `rotate(${parseFloat(val)}deg) `;
168
+ break;
169
+ case 'clip':
170
+ const top = this.propertyToString(key, val, 'top', '%'), right = this.propertyToString(key, val, 'right', '%'),
171
+ bottom = this.propertyToString(key, val, 'bottom', '%'), left = this.propertyToString(key, val, 'left', '%');
172
+
173
+ properties.clipPath = `inset(${top} ${right} ${bottom} ${left})`;
174
+ break;
175
+ case 'borderRadius':
176
+ properties[key] = typeof val === 'string' ? val : val + 'px';
177
+ break;
178
+ case 'opacity':
179
+ case 'active':
180
+ properties[key] = val;
181
+ }
182
+ });
183
+
184
+ if (!properties.transform.length) delete properties.transform;
185
+ return properties;
186
+ }
187
+
188
+ static setInitial(element) {
189
+ const {
190
+ width,
191
+ height,
192
+ paddingLeft,
193
+ paddingRight,
194
+ paddingTop,
195
+ paddingBottom,
196
+ borderRadius,
197
+ boxSizing
198
+ } = getComputedStyle(element);
199
+ const { x, y } = element.getBoundingClientRect();
200
+
201
+ if (!('UITools' in element)) element.UITools = {};
202
+ if (!('queue' in element.UITools)) element.UITools.queue = [];
203
+ if (!('initialStyles' in element.UITools)) element.UITools.initialStyles = {
204
+ x,
205
+ y,
206
+ includePadding: boxSizing === 'border-box',
207
+ clientWidth: element.clientWidth,
208
+ clientHeight: element.clientHeight,
209
+ width: parseInt(width),
210
+ height: parseInt(height),
211
+ paddingLeft: parseInt(paddingLeft),
212
+ paddingRight: parseInt(paddingRight),
213
+ paddingTop: parseInt(paddingTop),
214
+ paddingBottom: parseInt(paddingBottom),
215
+ borderRadius: parseInt(borderRadius.split(' ')[0])
216
+ };
217
+ }
218
+
219
+ setInitialStyles(element) {
220
+ Animation.setInitial(element);
221
+
222
+ const keyframe = this.keyframes[0];
223
+ element.style.transitionDuration = '0s';
224
+ this.apply(element, keyframe, true);
225
+ }
226
+
227
+ test(scale, total, size, padStart, padEnd, includePadding) {
228
+ scale = typeof scale === 'string' ? parseInt(scale) / total : scale;
229
+ size = size - (total - scale * total);
230
+
231
+ const pad = padStart + padEnd + (size < 0 ? size : 0);
232
+ const ratio = padStart / (padStart + padEnd);
233
+ padStart = includePadding ? scale * padStart : pad * ratio;
234
+ padEnd = includePadding ? scale * padEnd : pad * (1 - ratio);
235
+
236
+ return {
237
+ size: (size < 0 ? 0 : size) + 'px',
238
+ padStart: padStart + 'px',
239
+ padEnd: padEnd + 'px'
240
+ };
241
+ }
242
+
243
+ apply(element, keyframe, initial = false) {
244
+ const applyStyles = () => {
245
+ Object.entries(keyframe).forEach(([key, val]) => {
246
+ if (key === 'width') {
247
+ const { clientWidth, width, paddingLeft, paddingRight, includePadding } = element.UITools.initialStyles;
248
+ const { size, padStart, padEnd } = this.test(val, clientWidth, width, paddingLeft, paddingRight, includePadding);
249
+
250
+ element.style.width = size;
251
+ element.style.paddingLeft = padStart;
252
+ element.style.paddingRight = padEnd;
253
+ return;
254
+ }
255
+
256
+ if (key === 'height') {
257
+ const { clientHeight, height, paddingTop, paddingBottom, includePadding } = element.UITools.initialStyles;
258
+ const { size, padStart, padEnd } = this.test(val, clientHeight, height, paddingTop, paddingBottom, includePadding);
259
+
260
+ element.style.height = size;
261
+ element.style.paddingTop = padStart;
262
+ element.style.paddingBottom = padEnd;
263
+ return;
264
+ }
265
+
266
+ if (key === 'active') return;
267
+
268
+ element.style[key] = val;
269
+ });
270
+ };
271
+
272
+ if ('active' in keyframe) {
273
+ let when = Object.keys(keyframe.active)[0];
274
+
275
+ if (when === 'start' || initial) {
276
+ element.style.display = keyframe.active[when] ? '' : 'none';
277
+ initial ? applyStyles() : AnimationQueue.delay(applyStyles, 0.01);
278
+ } else {
279
+ AnimationQueue.delay(() => {
280
+ element.style.display = keyframe.active[when] ? '' : 'none';
281
+ }, this.delta);
282
+ applyStyles();
283
+ }
284
+ } else {
285
+ applyStyles();
286
+ }
287
+ }
288
+
289
+ start(element, { immediate = false, reverse = false } = {}) {
290
+ if (element.UITools.animating && !immediate) {
291
+ element.UITools.queue.push([this, { reverse }]);
292
+ return;
293
+ }
294
+
295
+ element.style.transitionDuration = `${this.delta}s`;
296
+ element.style.transitionTimingFunction = this.interpolation;
297
+ element.style.transformOrigin = this.origin;
298
+ element.UITools.animating = true;
299
+ element.UITools.index = 1;
300
+
301
+ this.getNext(element, reverse);
302
+ }
303
+
304
+ play(element, { delay = 0, immediate = false, reverse = false } = {}) {
305
+ if (!element.style) return;
306
+
307
+ this.delay || delay ? AnimationQueue.delay(() => this.start(element, { immediate, reverse }), this.delay + delay) : this.start(element, { immediate, reverse });
308
+ }
309
+
310
+ getNext(element, reverse = false) {
311
+ if (element.UITools.index === this.keyframes.length) {
312
+ element.UITools.animating = false;
313
+
314
+ const [next, options] = element.UITools.queue.shift() || [];
315
+ if (next) return next.start(element, options);
316
+
317
+ if (this.loop) this.start(element, options);
318
+ return;
319
+ }
320
+
321
+ let idx = element.UITools.index;
322
+ if (reverse) idx = this.keyframes.length - 1 - idx;
323
+
324
+
325
+ requestAnimationFrame(() => {
326
+ this.apply(element, this.keyframes[idx]);
327
+ });
328
+ element.UITools.index++;
329
+
330
+ AnimationQueue.delay(() => this.getNext(element, reverse), this.delta); // cancel this when using immediate
331
+ }
332
+
333
+ }
@@ -0,0 +1,12 @@
1
+ import Animation from '../animation';
2
+
3
+ export default function Fade(options = {}) {
4
+ return {
5
+ scaleCorrection: options.scaleCorrection,
6
+ use: Fade.use.bind(this, options)
7
+ };
8
+ }
9
+
10
+ Fade.use = ({ scaleCorrection = false } = {}) => {
11
+ return new Animation({ opacity: 1, scaleCorrection, duration: 0.65 }, { opacity: 0 });
12
+ }