motion complete

pull/4742/head
pushkine 5 years ago
parent 0d9d3c53d1
commit a75fc80f4c

@ -72,7 +72,7 @@
"c8": "^7.1.1",
"code-red": "0.1.1",
"codecov": "^3.6.5",
"css-tree": "1.0.0-alpha39",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-svelte3": "^2.7.3",

@ -116,7 +116,7 @@ export function init(
if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = val))) {
if (i in $$.bound) $$.bound[i](val);
if (ready) {
if (!~$$.dirty) {
if (!~$$.dirty[0]) {
schedule_update(component);
$$.dirty.fill(0);
}

@ -1,9 +1,9 @@
import { noop } from './utils';
export const resolved_promise = Promise.resolve();
export const is_client = typeof window !== 'undefined';
export const is_iframe = is_client && window.location !== window.parent.location;
export let now = is_client ?()=> performance.now() :()=> Date.now();
export let now = is_client ? performance.now.bind(performance) : Date.now.bind(Date);
export let raf = is_client ? window.requestAnimationFrame : noop;

@ -6,103 +6,106 @@ type TaskCanceller = () => void;
let next_frame: Array<TaskCallback> = [];
let running_frame: Array<TaskCallback> = [];
function run(t: number) {
let next_frame_length = 0;
const run = (t: number) => {
t = now();
[running_frame, next_frame] = [next_frame, running_frame];
let next_frame_tasks = 0;
for (let i = 0, j = running_frame.length, v; i < j; i++) {
if ((v = running_frame[i])(t)) next_frame[next_frame_tasks++] = v;
for (let i = (next_frame_length = 0), j = running_frame.length, v; i < j; i++) {
if ((v = running_frame[i])(t)) {
next_frame[next_frame_length++] = v;
}
}
running_frame.length = 0;
if (next_frame_tasks) raf(run);
}
if (next_frame_length) raf(run);
else console.log('ended loop');
};
type TimeoutTask = { t: number; c: (now: number) => void };
type TimeoutTask = { timestamp: number; callback: (now: number) => void };
const timed_tasks: Array<TimeoutTask> = [];
const pending_insert_timed: Array<TimeoutTask> = [];
let running_timed = false;
let pending_inserts = false;
function run_timed(now: number) {
const run_timed = (now: number) => {
/* Runs every timed out task */
let last_index = timed_tasks.length - 1;
while (last_index >= 0 && now >= timed_tasks[last_index].t) {
timed_tasks[last_index--].c(now);
}
while (last_index >= 0 && now >= timed_tasks[last_index].timestamp) timed_tasks[last_index--].callback(now);
if (pending_inserts) {
for (
let i = 0, j = last_index, this_task: TimeoutTask, that_task: TimeoutTask;
i < pending_insert_timed.length;
i++
) {
if (now >= (this_task = pending_insert_timed[i]).t) {
this_task.c(now);
} else {
for (j = last_index; j > 0 && this_task.t > (that_task = timed_tasks[j]).t; j--) {
/* move each task up until this_task.timestamp > task.timestamp */
)
if (now >= (this_task = pending_insert_timed[i]).timestamp) this_task.callback(now);
else {
/* moves each task up until this_task.timestamp > task.timestamp */
for (j = last_index; j > 0 && this_task.timestamp > (that_task = timed_tasks[j]).timestamp; j--)
timed_tasks[j + 1] = that_task;
}
timed_tasks[j] = this_task;
last_index++;
}
}
pending_inserts = !!(pending_insert_timed.length = 0);
}
return (running_timed = !!(timed_tasks.length = last_index + 1));
}
export function setAnimationTimeout(callback: () => void, timestamp: number): TaskCanceller {
const task: TimeoutTask = { c: callback, t: timestamp };
return (running_timed = !!(timed_tasks.length = last_index));
};
const unsafe_loop = (fn) => {
if (!next_frame_length) raf(run);
next_frame[next_frame_length++] = fn;
};
export const loop = (fn) => {
let running = true;
if (!next_frame_length) raf(run);
next_frame[next_frame_length++] = (t) => !running || fn(t);
return () => void (running = false);
};
export const setAnimationTimeout = (callback: () => void, timestamp: number): TaskCanceller => {
const task: TimeoutTask = { callback, timestamp };
if (running_timed) {
pending_inserts = !!pending_insert_timed.push(task);
} else {
unsafe_loop(run_timed);
running_timed = true;
timed_tasks.push(task);
if (1 === next_frame.push(run_timed)) raf(run);
}
return () => void (task.c = noop);
}
return () => void (task.callback = noop);
};
/**
* Calls function every frame with a value going from 0 to 1
*/
export function useTween(
export const useTween = (
run: (now: number) => void,
stop: () => void,
end_time: number,
duration = end_time - now()
): TaskCanceller {
): TaskCanceller => {
let running = true;
if (
1 ===
next_frame.push((t) => {
unsafe_loop((t) => {
if (!running) return false;
t = (end_time - t) / duration;
if (t >= 1) return run(1), stop(), (running = false);
t = 1 - (end_time - t) / duration;
if (t >= 1) return run(1), stop(), false;
if (t >= 0) run(t);
return running;
})
)
raf(run);
});
return () => void (running = false);
}
};
/**
* Calls function every frame with the amount of elapsed frames
*/
export function onEachFrame(
export const onEachFrame = (
each_frame: (seconds_elapsed: number) => boolean,
on_stop?,
max_skipped_frames = 4
): TaskCanceller {
): TaskCanceller => {
max_skipped_frames *= FRAME_RATE || calc_framerate();
let lastTime = now();
let running = true;
if (
1 ===
next_frame.push((t: number) => {
if (!running) return on_stop && on_stop(t), false;
const cancel = (t) => (on_stop && on_stop(t), false);
unsafe_loop((t: number) => {
if (!running) return cancel(t);
if (t > lastTime + max_skipped_frames) t = lastTime + max_skipped_frames;
return each_frame((-lastTime + (lastTime = t)) / 1000);
})
)
raf(run);
return each_frame((-lastTime + (lastTime = t)) / 1000) ? true : cancel(t);
});
return () => void (running = false);
}
};
// tests
export const clear_loops = () =>
void (next_frame.length = running_frame.length = timed_tasks.length = pending_insert_timed.length = 0);
void (next_frame.length = running_frame.length = timed_tasks.length = pending_insert_timed.length = next_frame_length = +(running_timed = pending_inserts = false));

@ -1,9 +1,8 @@
import { set_current_component } from './lifecycle';
import { resolved_promise } from './environment';
const dirty_components = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
export function schedule_update(component) {
dirty_components.push(component);

@ -1,22 +1,33 @@
import { safe_not_equal, noop } from './utils';
import { safe_not_equal, noop, subscribe } from './utils';
import { onEachFrame, useTween, loop } from './loop';
import { now } from './environment';
type Setter<T> = (value: T) => void;
type StopCallback = () => void;
export type StartStopNotifier<T> = (set: Setter<T>) => StopCallback | void;
type Subscriber<T> = (value: T) => void;
type Unsubscriber = () => void;
interface Observable<T> {
subscribe(callback: Subscriber<T>): Unsubscriber;
}
type Observable_s = Observable<unknown>[] | Observable<unknown>;
type Deriver<T> = (values: any, setter?: Setter<T>) => void | (() => void) | T;
/**
* Internal Svelte Observable
*/
export class Store<T> {
static update_queue: Store<any>[] = [];
static value_queue: Store<any>['value'][] = [];
static update_queue: Store<any>['subscribers'][] = [];
static is_flushing = false;
static flush(store: Store<any>) {
this.update_queue.push(store);
this.value_queue.push(store.value);
this.update_queue.push([...store.subscribers]);
if (this.is_flushing) return;
this.is_flushing = true;
for (let i = 0, j = 0, subscribers, value; i < this.update_queue.length; i++) {
for (j = 0, { subscribers, value } = this.update_queue[i]; j < subscribers.length; j++) {
subscribers[j].r(value);
}
}
this.update_queue.length = +(this.is_flushing = false);
for (let i = 0, j = 0, subscribers, value; i < this.update_queue.length; i++)
for (j = 0, subscribers = this.update_queue[i], value = this.value_queue[i]; j < subscribers.length; j++)
subscribers[j].run(value);
this.update_queue.length = this.value_queue.length = +(this.is_flushing = false);
}
value: T;
has_subscribers = false;
@ -27,21 +38,32 @@ export class Store<T> {
set(next_value: T) {
this.value = next_value;
if (!this.has_subscribers) return;
for (let i = 0; i < this.subscribers.length; i++) this.subscribers[i].i();
for (let i = 0; i < this.subscribers.length; i++) this.subscribers[i].invalidate();
Store.flush(this);
}
subscribe(run: Subscriber<T>, invalidate = noop) {
const subscriber = { r: run, i: invalidate };
const subscriber = { run, invalidate };
this.subscribers.push(subscriber);
run(this.value), (this.has_subscribers = true);
return this.unsubscribe.bind(this, subscriber) as Unsubscriber;
}
unsubscribe(subscriber) {
this.subscribers.splice(this.subscribers.indexOf(subscriber), 1);
const index = this.subscribers.indexOf(subscriber);
if (~index) {
if (Store.is_flushing) subscriber.run = subscriber.invalidate = noop;
this.subscribers.splice(index, 1);
this.has_subscribers = !!this.subscribers.length;
return true;
}
return false;
}
}
export class Writable<T> extends Store<T> {
/**
* like Store but
* + StartStopNotifier
* + update function
*/
class StartStopWritable<T> extends Store<T> {
start: StartStopNotifier<T>;
stop = noop;
constructor(initial: T, startStopNotifier: StartStopNotifier<T>) {
@ -49,17 +71,178 @@ export class Writable<T> extends Store<T> {
this.start = startStopNotifier || noop;
}
subscribe(run, invalidate) {
// *must* run *after* first subscription ?
if (!super.has_subscribers) this.stop = this.start(this.set.bind(this)) || noop;
return super.subscribe(run, invalidate);
}
set(next_value: T) {
if (this.stop && safe_not_equal(super.value, next_value)) super.set(next_value);
if (this.stop) super.set(next_value);
}
update(fn) {
this.set(fn(this.value));
}
unsubscribe(subscriber) {
super.unsubscribe(subscriber);
if (!super.has_subscribers && this.stop) this.stop(), (this.stop = null);
if (super.unsubscribe(subscriber)) {
if (!this.has_subscribers) this.stop();
return true;
}
return false;
}
}
/**
* StartStopWritable but
* + safe_not_equal
*/
export class Writable<T> extends StartStopWritable<T> {
set(next_value: T) {
if (safe_not_equal(this.value, next_value)) super.set(next_value);
}
}
export class Derived<S extends Observable_s, D extends Deriver<T>, T> extends StartStopWritable<T> {
cleanup = noop;
target;
deriver;
set: (value_s: unknown | unknown[]) => void;
constructor(stores: S, deriver: D, initial_value?: T) {
super(
initial_value,
Array.isArray(stores)
? (_set) => {
let l = stores.length;
let pending = 1 << l;
const values = new Array(l);
const unsubs = stores.map((store, i) =>
subscribe(
store,
(v) => void ((values[i] = v), !(pending &= ~(1 << i)) && this.set(values)),
() => void (pending |= 1 << i)
)
);
if (!(pending &= ~(1 << l))) this.set(values);
return () => {
while (l--) unsubs[l]();
this.cleanup();
};
}
: (_set) => ((unsub) => void (unsub(), this.cleanup())).bind(this, subscribe(stores, this.set))
);
this.target = stores;
this.set =
// deriver defines < 2 arguments ?
deriver.length < 2
? // return value is store value
(v) => void super.set(deriver(v) as T)
: // return value is cleanup | void, store value is set manually
(v) =>
void (this.cleanup(),
typeof (this.cleanup = deriver(v, super.set.bind(this)) as () => void) !== 'function' &&
(this.cleanup = noop));
}
}
export type initCreateMotionTick<T> = (set: (value: T) => void) => createMotionTick<T>;
export type createMotionTick<T> = (prev_value: T, next_value: T) => SpringTick<T>;
export type SpringTick<T> = (current_value: T, elapsed: number, dt: number) => boolean;
export type TweenTick = (t: number) => boolean;
/** applies motion fn to every leaf of any object */
function parseStructure<T>(obj: T, schema: initCreateMotionTick<T>): initCreateMotionTick<T> {
const isArray = Array.isArray(obj);
if (typeof obj === 'object' && obj !== null && (isArray || Object.prototype === Object.getPrototypeOf(obj))) {
const keys = Object.keys(obj);
let i = 0,
l = keys.length,
k = '',
createTickers = keys.map((key) => parseStructure(obj[key], schema)((next_value) => (obj[key] = next_value))),
tickers = new Array(l),
pending = 0;
const target = { ...obj };
//@ts-ignore
obj = isArray ? [...obj] : { ...obj };
return (set) => (_from_value, to_value) => {
for (k in to_value) if (to_value[k] !== obj[k]) target[k] = to_value[k];
for (i = 0; i < l; i++) (pending |= 1 << i), (tickers[i] = createTickers[i](obj[keys[i]], target[keys[i]]));
return (_current, elapsed, dt) => {
for (i = 0; i < l; i++) if (pending & (1 << i) && !tickers[i](obj[keys[i]], elapsed, dt)) pending &= ~(1 << i);
//@ts-ignore
set(isArray ? [...obj] : { ...obj });
return !!pending;
};
};
}
return schema;
}
abstract class MotionStore<T> extends Store<T> {
running = false;
cancel = noop;
initCreateTicker: initCreateMotionTick<T>;
createTicker: createMotionTick<T>;
tick;
constructor(value: T, startSetTick: initCreateMotionTick<T>) {
super(value);
this.createTicker = parseStructure(value, (this.initCreateTicker = startSetTick))(super.set.bind(this));
}
set(next_value: T) {
const this_id = ++this.uidRunning;
this.clearStateSubscribers(false);
//@ts-ignore
if (!this.value && this.value !== 0) {
this.setImmediate(next_value);
} else {
this.tick = this.createTicker(this.value, next_value);
this.loop(() => this.clearStateSubscribers(true));
this.running = true;
}
return {
then: (resolve, reject) => {
const stop = (has_ended) => (this.uidRunning === this_id ? resolve : reject)(has_ended);
if (!this.running || this_id !== this.uidRunning) stop(true);
else this.onCompletionSubscribers.push(stop);
},
};
}
abstract loop(stop): void;
setImmediate(value) {
this.createTicker = parseStructure(value, this.initCreateTicker)(super.set.bind(this));
super.set((this.value = value));
if (this.running) this.cancel();
this.running = false;
}
onCompletionSubscribers = [];
onRestSubscribers = [];
uidRunning = 0;
onRest(callback: Subscriber<void>) {
this.onRestSubscribers.push(callback);
return () => {
const index = this.onRestSubscribers.indexOf(callback);
if (~index) this.onRestSubscribers[index] = noop;
};
}
clearStateSubscribers(has_ended: boolean) {
let i = 0,
l = this.onRestSubscribers.length;
if (has_ended) {
this.running = false;
if (l)
for (; i < this.onRestSubscribers.length; i++) {
this.onRestSubscribers[i]();
if (this.onRestSubscribers[i] === noop) this.onRestSubscribers.splice(i--, 1);
}
}
for (i = 0, l = this.onCompletionSubscribers.length; i < l; i++) this.onCompletionSubscribers[i](has_ended);
this.onCompletionSubscribers.length = 0;
}
}
export class SpringMotion<T> extends MotionStore<T> {
elapsed = 0.0;
tick: SpringTick<T>;
loop(stop) {
this.elapsed = 0.0;
if (!this.running) this.cancel = onEachFrame((dt) => this.tick(this.value, (this.elapsed += dt), dt), stop);
}
}
export class TweenMotion<T> extends MotionStore<T> {
tick: TweenTick;
loop(stop) {
if (this.running) this.cancel();
this.cancel = loop((t) => this.tick(t) || (stop(), false));
}
}

@ -26,7 +26,7 @@ interface ExtendedDoc extends Document {
const active_documents = new Set<ExtendedDoc>();
let running_animations = 0;
function add_rule(node, name, rule): [CSSStyleSheet, Set<string>] {
function add_rule(node): [CSSStyleSheet, Set<string>] {
const { ownerDocument } = node;
if (!active_documents.has(ownerDocument)) {
active_documents.add(ownerDocument);
@ -52,7 +52,7 @@ const gen = (t, step, css) => {
};
export function animate_css(css: (t: number) => string, node: HTMLElement, duration: number, t = 0) {
const [name, rule] = gen(t, duration / (FRAME_RATE || calc_framerate()), css);
const [stylesheet, rules] = add_rule(node, rule);
const [stylesheet, rules] = add_rule(node);
if (!rules.has(name)) {
rules.add(name);
stylesheet.insertRule(rule, stylesheet.cssRules.length);

@ -1,133 +0,0 @@
// import { Readable, writable } from 'svelte/store';
// import { assign, loop, now, Task } from 'svelte/internal';
// import { linear } from 'svelte/easing';
// import { is_date } from './utils';
// function get_interpolator(a, b) {
// if (a === b || a !== a) return () => a;
// const type = typeof a;
// if (type !== typeof b || Array.isArray(a) !== Array.isArray(b)) {
// throw new Error('Cannot interpolate values of different type');
// }
// if (Array.isArray(a)) {
// const arr = b.map((bi, i) => {
// return get_interpolator(a[i], bi);
// });
// return t => arr.map(fn => fn(t));
// }
// if (type === 'object') {
// if (!a || !b) throw new Error('Object cannot be null');
// if (is_date(a) && is_date(b)) {
// a = a.getTime();
// b = b.getTime();
// const delta = b - a;
// return t => new Date(a + t * delta);
// }
// const keys = Object.keys(b);
// const interpolators = {};
// keys.forEach(key => {
// interpolators[key] = get_interpolator(a[key], b[key]);
// });
// return t => {
// const result = {};
// keys.forEach(key => {
// result[key] = interpolators[key](t);
// });
// return result;
// };
// }
// if (type === 'number') {
// const delta = b - a;
// return t => a + t * delta;
// }
// throw new Error(`Cannot interpolate ${type} values`);
// }
// interface Options<T> {
// delay?: number;
// duration?: number | ((from: T, to: T) => number);
// easing?: (t: number) => number;
// interpolate?: (a: T, b: T) => (t: number) => T;
// }
// type Updater<T> = (target_value: T, value: T) => T;
// interface Tweened<T> extends Readable<T> {
// set(value: T, opts: Options<T>): Promise<void>;
// update(updater: Updater<T>, opts: Options<T>): Promise<void>;
// }
// export function tweened<T>(value?: T, defaults: Options<T> = {}): Tweened<T> {
// const store = writable(value);
// let task: Task;
// let target_value = value;
// function set(new_value: T, opts: Options<T>) {
// if (value == null) {
// store.set(value = new_value);
// return Promise.resolve();
// }
// target_value = new_value;
// let previous_task = task;
// let started = false;
// let {
// delay = 0,
// duration = 400,
// easing = linear,
// interpolate = get_interpolator
// } = assign(assign({}, defaults), opts);
// const start = now() + delay;
// let fn;
// task = loop(now => {
// if (now < start) return true;
// if (!started) {
// fn = interpolate(value, new_value);
// if (typeof duration === 'function') duration = duration(value, new_value);
// started = true;
// }
// if (previous_task) {
// previous_task.abort();
// previous_task = null;
// }
// const elapsed = now - start;
// if (elapsed > duration) {
// store.set(value = new_value);
// return false;
// }
// // @ts-ignore
// store.set(value = fn(easing(elapsed / duration)));
// return true;
// });
// return task.promise;
// }
// return {
// set,
// update: (fn, opts: Options<T>) => set(fn(target_value, value), opts),
// subscribe: store.subscribe
// };
// }

@ -1,4 +1,5 @@
import { Store, onEachFrame } from 'svelte/internal';
import { SpringMotion, TweenMotion, now } from 'svelte/internal';
import { is_date } from './utils';
function solve_spring(
prev_value: number,
@ -20,62 +21,91 @@ function solve_spring(
return (t: number) => target_value - (Math.cos((f = t * dfm)) * delta + Math.sin(f) * leftover) * Math.exp(t * dm);
}
}
class MotionStore<T> extends Store<T> {
elapsed = 0.0;
running = false;
setTick: (prev_value: T, next_value: T) => (current_value: T, elapsed: number, dt: number) => boolean;
tick: (current_value: T, elapsed: number, dt: number) => boolean;
constructor(value, startSetTick) {
super(value);
this.setTick = startSetTick(super.set.bind(this));
}
set(next_value: T) {
this.elapsed = 0.0;
this.tick = this.setTick(this.value, next_value);
if (this.running) return;
else this.running = true;
onEachFrame((dt) => (this.running = this.tick(this.value, (this.elapsed += dt), dt)));
}
}
export function spring(value, params) {
const { mass = 1.0, damping = 10.0, stiffness = 100.0, precision = 0.001, soft = false } = params || {};
return new MotionStore(
value,
parseStructure(value, (set) => {
export function spring(value, { mass = 1.0, damping = 10.0, stiffness = 100.0, precision = 0.001, soft = false } = {}) {
const store = new SpringMotion(value, (set) => {
let velocity = 0.0,
calc;
return (from_value, to_value) => {
calc = solve_spring(from_value, velocity, to_value, { mass, damping, stiffness, soft });
calc = solve_spring(from_value, velocity, to_value, obj);
return (current, elapsed, dt) =>
precision > Math.abs((velocity = (-current + (current = calc(elapsed))) / dt)) &&
precision > Math.abs(to_value - current)
? (set(to_value), !!(velocity = 0.0))
: (set(current), true);
};
})
);
});
const obj = {
mass,
damping,
stiffness,
precision,
soft,
set(next_value, params) {
if (params) {
if ('mass' in params) obj.mass = params.mass;
if ('damping' in params) obj.damping = params.damping;
if ('stiffness' in params) obj.stiffness = params.stiffness;
if ('precision' in params) obj.precision = params.precision;
if ('soft' in params) obj.soft = params.soft;
}
return store.set(next_value);
},
setImmediate: store.setImmediate.bind(store),
subscribe: store.subscribe.bind(store),
onRest: store.onRest.bind(store),
};
return obj;
}
function tween_between(a, b) {
if (a === b || a !== a) return () => a;
else if (typeof a === 'number') {
return (t) => a + t * (b-a);
} else if (is_date(a) && is_date(b)) {
a = a.getTime();
b = b.getTime();
const delta = b - a;
return (t) => new Date(a + t * delta);
} else throw new Error(`Cannot interpolate ${typeof a} values`);
}
function parseStructure(obj, schema) {
if (typeof obj === 'object' && obj !== null) {
const keys = Object.keys(obj);
let i = 0,
k = '',
setTickers = keys.map((key) => parseStructure(obj[key], schema)((next_value) => (obj[key] = next_value))),
tickers,
pending = 0;
const target = { ...obj };
const isArray = Array.isArray(obj);
obj = isArray ? [...obj] : { ...obj };
return (set) => (_from_value, to_value) => {
for (k in to_value) if (to_value[k] !== obj[k]) target[k] = to_value[k];
tickers = setTickers.map((setTicker, i) => ((pending |= 1 << i), setTicker(obj[keys[i]], target[keys[i]])));
return (_current, elapsed, dt) => {
for (i = 0; i < tickers.length; i++)
if (pending & (1 << i) && !tickers[i](obj[keys[i]], elapsed, dt)) pending &= ~(1 << i);
set(isArray ? [...obj] : { ...obj });
return !!pending;
export function tween(
value,
{
delay: default_delay = 0,
duration: default_duration = 400,
easing: default_easing = (v) => v,
interpolate: default_interpolate = tween_between,
}
) {
let delay = default_delay,
duration = default_duration,
easing = default_easing,
interpolate = default_interpolate;
const store = new TweenMotion(value, (set) => {
let end_time = 0,
calc;
return (from_value, to_value) => {
end_time = now() + delay + duration;
calc = interpolate(from_value, to_value);
return (t) => {
t = 1 - (end_time - t) / duration;
if (t >= 1) return set(calc(easing(1))), false;
if (t >= 0) set(calc(easing(t)));
return true;
};
};
});
function set(next_value, params) {
delay = (params && params.delay) || default_delay;
duration = (params && 'duration' in params && params.duration) || default_duration;
easing = (params && params.easing) || default_easing;
interpolate = (params && params.interpolate) || default_interpolate;
return store.set(next_value);
}
return schema;
return {
set,
// update: (fn, params) => set(fn(target_value, value), params),
setImmediate: store.setImmediate.bind(store),
subscribe: store.subscribe.bind(store),
onRest: store.onRest.bind(store),
};
}

@ -1,4 +1,4 @@
import { subscribe, noop, get_store_value, Writable, StartStopNotifier } from 'svelte/internal';
import { get_store_value, Writable, StartStopNotifier, Derived } from 'svelte/internal';
export { get_store_value as get };
export function readable<T>(value: T, start: StartStopNotifier<T>) {
const store = new Writable(value, start);
@ -8,38 +8,7 @@ export function writable<T>(value: T, start: StartStopNotifier<T>) {
const store = new Writable(value, start);
return { set: store.set.bind(store), update: store.update.bind(store), subscribe: store.subscribe.bind(store) };
}
export function derived<T, S>(stores: S, deriver, initial_value?: T) {
let cleanup = noop;
const dispatcher =
deriver.length < 2
? (set, v) => void set(deriver(v))
: (set, v) => void (cleanup(), typeof (cleanup = deriver(v, set)) !== 'function' && (cleanup = noop));
return readable(
initial_value,
Array.isArray(stores)
? (set) => {
set = dispatcher.bind(null, set);
let l = stores.length;
let pending = 1 << l;
const values = new Array(l);
const unsubs = stores.map((store, i) =>
subscribe(
store,
// @ts-ignore
(v) => void ((values[i] = v), !(pending &= ~(1 << i)) && set(values)),
() => void (pending |= 1 << i)
)
);
// @ts-ignore
if (!(pending &= ~(1 << l))) set(values);
return () => {
while (l--) unsubs[l]();
cleanup();
};
}
: (set) => {
const unsub = subscribe(stores, dispatcher.bind(null, set));
return () => void (unsub(), cleanup());
}
);
export function derived(stores, deriver, initial_value?) {
const store = new Derived(stores, deriver, initial_value);
return { subscribe: store.subscribe.bind(store) };
}

@ -1,19 +1,20 @@
import { cubicOut, cubicInOut, linear } from 'svelte/easing';
type EasingFunction = (t: number) => number;
export interface TransitionConfig {
interface BasicConfig {
delay?: number;
duration?: number;
easing?: EasingFunction;
}
interface TimeableConfig extends Omit<BasicConfig, 'duration'> {
duration?: number | ((len: number) => number);
}
export interface TransitionConfig extends BasicConfig {
css?: (t: number, u?: number) => string;
tick?: (t: number, u?: number) => void;
}
interface BlurParams {
delay: number;
duration: number;
easing?: EasingFunction;
interface BlurParams extends BasicConfig {
amount: number;
opacity: number;
}
@ -34,13 +35,7 @@ export function blur(
};
}
interface FadeParams {
delay: number;
duration: number;
easing: EasingFunction;
}
export function fade(node: Element, { delay = 0, duration = 400, easing = linear }: FadeParams): TransitionConfig {
export function fade(node: Element, { delay = 0, duration = 400, easing = linear }: BasicConfig): TransitionConfig {
const o = +getComputedStyle(node).opacity;
return {
delay,
@ -50,10 +45,7 @@ export function fade(node: Element, { delay = 0, duration = 400, easing = linear
};
}
interface FlyParams {
delay: number;
duration: number;
easing: EasingFunction;
interface FlyParams extends BasicConfig {
x: number;
y: number;
opacity: number;
@ -79,13 +71,7 @@ export function fly(
};
}
interface SlideParams {
delay: number;
duration: number;
easing: EasingFunction;
}
export function slide(node: Element, { delay = 0, duration = 400, easing = cubicOut }: SlideParams): TransitionConfig {
export function slide(node: Element, { delay = 0, duration = 400, easing = cubicOut }: BasicConfig): TransitionConfig {
const style = getComputedStyle(node);
const opacity = +style.opacity;
const height = parseFloat(style.height);
@ -112,10 +98,7 @@ export function slide(node: Element, { delay = 0, duration = 400, easing = cubic
};
}
interface ScaleParams {
delay: number;
duration: number;
easing: EasingFunction;
interface ScaleParams extends BasicConfig {
start: number;
opacity: number;
}
@ -140,11 +123,8 @@ export function scale(
};
}
interface DrawParams {
delay: number;
interface DrawParams extends TimeableConfig {
speed: number;
duration: number | ((len: number) => number);
easing: EasingFunction;
}
export function draw(
@ -156,18 +136,10 @@ export function draw(
else if (typeof duration === 'function') duration = duration(len);
return { delay, duration, easing, css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};` };
}
interface CrossfadeParams {
delay: number;
duration: number | ((len: number) => number);
easing: EasingFunction;
}
interface CrossFadeConfig extends CrossfadeParams {
fallback: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig;
}
interface MarkedCrossFadeConfig extends CrossfadeParams {
key: any;
interface CrossFadeConfig extends TimeableConfig {
fallback: (node: Element, params: TimeableConfig, intro: boolean) => TransitionConfig;
}
type MarkedCrossFadeConfig = TimeableConfig & { key: any };
type ElementMap = Map<string, Element>;
export function crossfade({
@ -182,7 +154,7 @@ export function crossfade({
function crossfade(
from_node: Element,
to_node: Element,
{ delay = default_delay, easing = default_easing, duration = default_duration }: CrossfadeParams
{ delay = default_delay, easing = default_easing, duration = default_duration }: TimeableConfig
) {
const from = from_node.getBoundingClientRect();
const to = to_node.getBoundingClientRect();

Loading…
Cancel
Save