bidi complete

pull/4742/head
pushkine 5 years ago
parent 0ee27c4bc1
commit c225731d06

@ -0,0 +1 @@
export * from '../types/runtime/interpolate/index';

@ -0,0 +1,16 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function dates(a, b) {
const difference = (b = new Date(b).getTime()) - (a = new Date(a).getTime());
const d = new Date(a);
return (t) => (d.setTime(a + difference * t), d);
}
function numbers(a, b) {
const d = (b = +b) - (a = +a);
return (t) => a + d * t;
}
exports.dates = dates;
exports.numbers = numbers;

@ -0,0 +1,11 @@
function dates(a, b) {
const difference = (b = new Date(b).getTime()) - (a = new Date(a).getTime());
const d = new Date(a);
return (t) => (d.setTime(a + difference * t), d);
}
function numbers(a, b) {
const d = (b = +b) - (a = +a);
return (t) => a + d * t;
}
export { dates, numbers };

@ -0,0 +1,5 @@
{
"main": "./index",
"module": "./index.mjs",
"types": "./index.d.ts"
}

@ -29,7 +29,7 @@ export class ElseBlockWrapper extends Wrapper {
this.block = block.child({ this.block = block.child({
comment: create_debugging_comment(node, this.renderer.component), comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.get_unique_name(`create_else_block`), name: this.renderer.component.get_unique_name(`create_else_block`),
type: 'else' type: 'else',
}); });
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(
@ -58,7 +58,7 @@ export default class EachBlockWrapper extends Wrapper {
fixed_length: number; fixed_length: number;
data_length: string; data_length: string;
view_length: string; view_length: string;
} };
context_props: Array<Node | Node[]>; context_props: Array<Node | Node[]>;
index_name: Identifier; index_name: Identifier;
@ -82,7 +82,7 @@ export default class EachBlockWrapper extends Wrapper {
const { dependencies } = node.expression; const { dependencies } = node.expression;
block.add_dependencies(dependencies); block.add_dependencies(dependencies);
this.node.contexts.forEach(context => { this.node.contexts.forEach((context) => {
renderer.add_to_context(context.key.name, true); renderer.add_to_context(context.key.name, true);
}); });
@ -93,7 +93,7 @@ export default class EachBlockWrapper extends Wrapper {
// @ts-ignore todo: probably error // @ts-ignore todo: probably error
key: node.key as string, key: node.key as string,
bindings: new Map(block.bindings) bindings: new Map(block.bindings),
}); });
// TODO this seems messy // TODO this seems messy
@ -105,7 +105,7 @@ export default class EachBlockWrapper extends Wrapper {
const fixed_length = const fixed_length =
node.expression.node.type === 'ArrayExpression' && node.expression.node.type === 'ArrayExpression' &&
node.expression.node.elements.every(element => element.type !== 'SpreadElement') node.expression.node.elements.every((element) => element.type !== 'SpreadElement')
? node.expression.node.elements.length ? node.expression.node.elements.length
: null; : null;
@ -118,7 +118,7 @@ export default class EachBlockWrapper extends Wrapper {
const length = { const length = {
type: 'Identifier', type: 'Identifier',
name: 'length', name: 'length',
loc: { start, end } loc: { start, end },
}; };
const each_block_value = renderer.component.get_unique_name(`${this.var.name}_value`); const each_block_value = renderer.component.get_unique_name(`${this.var.name}_value`);
@ -136,23 +136,22 @@ export default class EachBlockWrapper extends Wrapper {
// optimisation for array literal // optimisation for array literal
fixed_length, fixed_length,
data_length: fixed_length === null ? x`${each_block_value}.${length}` : fixed_length, data_length: fixed_length === null ? x`${each_block_value}.${length}` : fixed_length,
view_length: fixed_length === null ? x`${iterations}.length` : fixed_length view_length: fixed_length === null ? x`${iterations}.length` : fixed_length,
}; };
const store = const store =
node.expression.node.type === 'Identifier' && node.expression.node.type === 'Identifier' && node.expression.node.name[0] === '$'
node.expression.node.name[0] === '$'
? node.expression.node.name.slice(1) ? node.expression.node.name.slice(1)
: null; : null;
node.contexts.forEach(prop => { node.contexts.forEach((prop) => {
this.block.bindings.set(prop.key.name, { this.block.bindings.set(prop.key.name, {
object: this.vars.each_block_value, object: this.vars.each_block_value,
property: this.index_name, property: this.index_name,
modifier: prop.modifier, modifier: prop.modifier,
snippet: prop.modifier(x`${this.vars.each_block_value}[${this.index_name}]` as Node), snippet: prop.modifier(x`${this.vars.each_block_value}[${this.index_name}]` as Node),
store, store,
tail: prop.modifier(x`[${this.index_name}]` as Node) tail: prop.modifier(x`[${this.index_name}]` as Node),
}); });
}); });
@ -165,14 +164,7 @@ export default class EachBlockWrapper extends Wrapper {
this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, strip_whitespace, next_sibling); this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, strip_whitespace, next_sibling);
if (this.node.else) { if (this.node.else) {
this.else = new ElseBlockWrapper( this.else = new ElseBlockWrapper(renderer, block, this, this.node.else, strip_whitespace, next_sibling);
renderer,
block,
this,
this.node.else,
strip_whitespace,
next_sibling
);
renderer.blocks.push(this.else.block); renderer.blocks.push(this.else.block);
@ -194,14 +186,18 @@ export default class EachBlockWrapper extends Wrapper {
const { renderer } = this; const { renderer } = this;
const { component } = renderer; const { component } = renderer;
const needs_anchor = this.next const needs_anchor = this.next ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node();
? !this.next.is_dom_node() :
!parent_node || !this.parent.is_dom_node();
this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`); this.context_props = this.node.contexts.map(
(prop) => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`
);
if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`); if (this.node.has_binding)
if (this.node.has_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`); this.context_props.push(
b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`
);
if (this.node.has_binding || this.node.index)
this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`);
const snippet = this.node.expression.manipulate(block); const snippet = this.node.expression.manipulate(block);
@ -224,7 +220,7 @@ export default class EachBlockWrapper extends Wrapper {
const update_anchor_node = needs_anchor const update_anchor_node = needs_anchor
? block.get_unique_name(`${this.var.name}_anchor`) ? block.get_unique_name(`${this.var.name}_anchor`)
: (this.next && this.next.var) || { type: 'Identifier', name: 'null' }; : (this.next && this.next.var) || { type: 'Identifier', name: 'null' };
const update_mount_node: Identifier = this.get_update_mount_node((update_anchor_node as Identifier)); const update_mount_node: Identifier = this.get_update_mount_node(update_anchor_node as Identifier);
const args = { const args = {
block, block,
@ -234,7 +230,7 @@ export default class EachBlockWrapper extends Wrapper {
initial_anchor_node, initial_anchor_node,
initial_mount_node, initial_mount_node,
update_anchor_node, update_anchor_node,
update_mount_node update_mount_node,
}; };
const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only
@ -258,12 +254,7 @@ export default class EachBlockWrapper extends Wrapper {
} }
if (needs_anchor) { if (needs_anchor) {
block.add_element( block.add_element(update_anchor_node as Identifier, x`@empty()`, parent_nodes && x`@empty()`, parent_node);
update_anchor_node as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_node
);
} }
if (this.else) { if (this.else) {
@ -354,7 +345,7 @@ export default class EachBlockWrapper extends Wrapper {
initial_anchor_node, initial_anchor_node,
initial_mount_node, initial_mount_node,
update_anchor_node, update_anchor_node,
update_mount_node update_mount_node,
}: { }: {
block: Block; block: Block;
parent_node: Identifier; parent_node: Identifier;
@ -365,12 +356,7 @@ export default class EachBlockWrapper extends Wrapper {
update_anchor_node: Identifier; update_anchor_node: Identifier;
update_mount_node: Identifier; update_mount_node: Identifier;
}) { }) {
const { const { create_each_block, iterations, data_length, view_length } = this.vars;
create_each_block,
iterations,
data_length,
view_length
} = this.vars;
const get_key = block.get_unique_name('get_key'); const get_key = block.get_unique_name('get_key');
const lookup = block.get_unique_name(`${this.var.name}_lookup`); const lookup = block.get_unique_name(`${this.var.name}_lookup`);
@ -382,18 +368,16 @@ export default class EachBlockWrapper extends Wrapper {
this.block.first = this.fragment.nodes[0].var; this.block.first = this.fragment.nodes[0].var;
} else { } else {
this.block.first = this.block.get_unique_name('first'); this.block.first = this.block.get_unique_name('first');
this.block.add_element( this.block.add_element(this.block.first, x`@empty()`, parent_nodes && x`@empty()`, null);
this.block.first,
x`@empty()`,
parent_nodes && x`@empty()`,
null
);
} }
block.chunks.init.push(b` block.chunks.init.push(b`
const ${get_key} = #ctx => ${this.node.key.manipulate(block)}; const ${get_key} = #ctx => ${this.node.key.manipulate(block)};
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} ${
this.renderer.options.dev &&
b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`
}
for (let #i = 0; #i < ${data_length}; #i += 1) { for (let #i = 0; #i < ${data_length}; #i += 1) {
let child_ctx = ${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i); let child_ctx = ${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx); let key = ${get_key}(child_ctx);
@ -424,12 +408,12 @@ export default class EachBlockWrapper extends Wrapper {
const dynamic = this.block.has_update_method; const dynamic = this.block.has_update_method;
const destroy = this.node.has_animation const destroy = this.node.has_animation
? (this.block.has_outros ? this.block.has_outros
? `@fix_and_outro_and_destroy_block` ? `@fix_and_outro_and_destroy_block`
: `@fix_and_destroy_block`) : `@fix_and_destroy_block`
: this.block.has_outros : this.block.has_outros
? `@outro_and_destroy_block` ? `@outro_and_destroy_block`
: `@destroy_block`; : `@destroy_block`;
if (this.dependencies.size) { if (this.dependencies.size) {
this.updates.push(b` this.updates.push(b`
@ -437,10 +421,17 @@ export default class EachBlockWrapper extends Wrapper {
${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`} ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`}
${this.block.has_outros && b`@group_outros();`} ${this.block.has_outros && b`@group_outros();`}
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].r();}`}
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} ${
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}); this.renderer.options.dev &&
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`} b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`
}
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${
this.vars.each_block_value
}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${
this.vars.get_each_context
});
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1){ ${iterations}[#i].a();}`}
${this.block.has_outros && b`@check_outros();`} ${this.block.has_outros && b`@check_outros();`}
`); `);
} }
@ -467,7 +458,7 @@ export default class EachBlockWrapper extends Wrapper {
initial_anchor_node, initial_anchor_node,
initial_mount_node, initial_mount_node,
update_anchor_node, update_anchor_node,
update_mount_node update_mount_node,
}: { }: {
block: Block; block: Block;
parent_nodes: Identifier; parent_nodes: Identifier;
@ -477,17 +468,10 @@ export default class EachBlockWrapper extends Wrapper {
update_anchor_node: Identifier; update_anchor_node: Identifier;
update_mount_node: Identifier; update_mount_node: Identifier;
}) { }) {
const { const { create_each_block, iterations, fixed_length, data_length, view_length } = this.vars;
create_each_block,
iterations,
fixed_length,
data_length,
view_length
} = this.vars;
block.chunks.init.push(b` block.chunks.init.push(b`
let ${iterations} = []; let ${iterations} = [];
for (let #i = 0; #i < ${data_length}; #i += 1) { for (let #i = 0; #i < ${data_length}; #i += 1) {
${iterations}[#i] = ${create_each_block}(${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i)); ${iterations}[#i] = ${create_each_block}(${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i));
} }
@ -529,7 +513,7 @@ export default class EachBlockWrapper extends Wrapper {
} }
` `
: has_transitions : has_transitions
? b` ? b`
if (${iterations}[#i]) { if (${iterations}[#i]) {
@transition_in(${this.vars.iterations}[#i], 1); @transition_in(${this.vars.iterations}[#i], 1);
} else { } else {
@ -539,7 +523,7 @@ export default class EachBlockWrapper extends Wrapper {
${iterations}[#i].m(${update_mount_node}, ${update_anchor_node}); ${iterations}[#i].m(${update_mount_node}, ${update_anchor_node});
} }
` `
: b` : b`
if (!${iterations}[#i]) { if (!${iterations}[#i]) {
${iterations}[#i] = ${create_each_block}(child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
@ -568,7 +552,9 @@ export default class EachBlockWrapper extends Wrapper {
`; `;
} else { } else {
remove_old_blocks = b` remove_old_blocks = b`
for (${this.block.has_update_method ? null : x`#i = ${data_length}`}; #i < ${this.block.has_update_method ? view_length : '#old_length'}; #i += 1) { for (${this.block.has_update_method ? null : x`#i = ${data_length}`}; #i < ${
this.block.has_update_method ? view_length : '#old_length'
}; #i += 1) {
${iterations}[#i].d(1); ${iterations}[#i].d(1);
} }
${!fixed_length && b`${view_length} = ${data_length};`} ${!fixed_length && b`${view_length} = ${data_length};`}

@ -1,6 +1,5 @@
import { cubicOut } from 'svelte/easing'; import { cubicOut } from 'svelte/easing';
import { AnimationConfig } from 'svelte/internal'; import { AnimationConfig, run_duration } from 'svelte/internal';
interface FlipParams { interface FlipParams {
delay: number; delay: number;
@ -23,7 +22,7 @@ export function flip(
return { return {
delay, delay,
duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration, duration: run_duration(duration, Math.sqrt(dx * dx + dy * dy)),
easing, easing,
css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);`, css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);`,
}; };

@ -1,3 +1,5 @@
import { dev$assert } from 'svelte/internal';
export { identity as linear } from 'svelte/internal'; export { identity as linear } from 'svelte/internal';
export const quadIn = (t: number) => t ** 2; export const quadIn = (t: number) => t ** 2;
export const quadOut = (t: number) => 1.0 - (1.0 - t) ** 2; export const quadOut = (t: number) => 1.0 - (1.0 - t) ** 2;
@ -47,9 +49,10 @@ export const circOut = (t: number) => Math.sin(Math.acos(1 - t));
export const circInOut = (t: number) => export const circInOut = (t: number) =>
0.5 * (t >= 0.5 ? 2.0 - Math.sin(Math.acos(1.0 - 2.0 * (1.0 - t))) : Math.sin(Math.acos(1.0 - 2.0 * t))); 0.5 * (t >= 0.5 ? 2.0 - Math.sin(Math.acos(1.0 - 2.0 * (1.0 - t))) : Math.sin(Math.acos(1.0 - 2.0 * t)));
export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => { export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {
if (__DEV__) dev$assert(
if (0 > x1 || x1 < 1 || 0 > x2 || x2 > 1) x1 >= 0 && x1 <= 1 && x2 >= 0 && x2 <= 1,
throw new Error(`CubicBezier x1 & x2 values must be { 0 < x < 1 }, got { x1 : ${x1}, x2: ${x2} }`); `CubicBezier x1 & x2 values must be { 0 < x < 1 }, got { x1 : ${x1}, x2: ${x2} }`
);
const ax = 1.0 - (x2 = 3.0 * (x2 - x1) - (x1 = 3.0 * x1)) - x1, const ax = 1.0 - (x2 = 3.0 * (x2 - x1) - (x1 = 3.0 * x1)) - x1,
ay = 1.0 - (y2 = 3.0 * (y2 - y1) - (y1 = 3.0 * y1)) - y1; ay = 1.0 - (y2 = 3.0 * (y2 - y1) - (y1 = 3.0 * y1)) - y1;
let r = 0.0, let r = 0.0,

@ -1,5 +1,8 @@
export * from './animations'; export * from './animations';
export * from './await_block'; export * from './await_block';
export * from './Component';
export * from './dev.legacy';
export * from './dev';
export * from './dom'; export * from './dom';
export * from './environment'; export * from './environment';
export * from './globals'; export * from './globals';
@ -9,8 +12,7 @@ export * from './loop';
export * from './scheduler'; export * from './scheduler';
export * from './spread'; export * from './spread';
export * from './ssr'; export * from './ssr';
export * from './stores';
// export * from './style_manager'
export * from './transitions'; export * from './transitions';
export * from './utils'; export * from './utils';
export * from './Component';
export * from './dev';
export * from './stores'

@ -53,7 +53,7 @@ export const loop = (fn) => {
next_frame[next_frame_length++] = (t) => !running || fn(t); next_frame[next_frame_length++] = (t) => !running || fn(t);
return () => void (running = false); return () => void (running = false);
}; };
export const setAnimationTimeout = (callback: () => void, timestamp: number): TaskCanceller => { export const setFrameTimeout = (callback: () => void, timestamp: number): TaskCanceller => {
const task: TimeoutTask = { callback, timestamp }; const task: TimeoutTask = { callback, timestamp };
if (running_timed) { if (running_timed) {
pending_inserts = !!pending_insert_timed.push(task); pending_inserts = !!pending_insert_timed.push(task);
@ -67,16 +67,16 @@ export const setAnimationTimeout = (callback: () => void, timestamp: number): Ta
/** /**
* Calls function every frame with a value going from 0 to 1 * Calls function every frame with a value going from 0 to 1
*/ */
export const useTween = ( export const setTweenTimeout = (
run: (now: number) => void,
stop: (now: number) => void, stop: (now: number) => void,
end_time: number, end_time: number,
run: (now: number) => void,
duration = end_time - now() duration = end_time - now()
): TaskCanceller => { ): TaskCanceller => {
let running = true; let running = true;
unsafe_loop((t) => { unsafe_loop((t) => {
if (!running) return false; if (!running) return false;
t = (end_time - t) / duration; t = 1 - (end_time - t) / duration;
if (t >= 1) return run(1), stop(t), false; if (t >= 1) return run(1), stop(t), false;
if (t >= 0) run(t); if (t >= 0) run(t);
return running; return running;
@ -102,6 +102,7 @@ export const onEachFrame = (
}); });
return () => void (running = false); return () => void (running = false);
}; };
// tests
/** tests only */
export const clear_loops = () => export const clear_loops = () =>
void (next_frame.length = running_frame.length = timed_tasks.length = pending_insert_timed.length = next_frame_length = +(running_timed = pending_inserts = false)); void (next_frame.length = running_frame.length = timed_tasks.length = pending_insert_timed.length = next_frame_length = +(running_timed = pending_inserts = false));

@ -1,6 +1,5 @@
import { safe_not_equal, noop, subscribe } from './utils'; import { safe_not_equal, noop, subscribe } from './utils';
import { onEachFrame, useTween, loop } from './loop'; import { onEachFrame, loop } from './loop';
import { now } from './environment';
type Setter<T> = (value: T) => void; type Setter<T> = (value: T) => void;
type StopCallback = () => void; type StopCallback = () => void;
export type StartStopNotifier<T> = (set: Setter<T>) => StopCallback | void; export type StartStopNotifier<T> = (set: Setter<T>) => StopCallback | void;
@ -140,11 +139,16 @@ export class Derived<S extends Observable_s, D extends Deriver<T>, T> extends St
} }
} }
export type initCreateMotionTick<T> = (set: (value: T) => void) => createMotionTick<T>; export type initCreateMotionTick<T> = (set: (value: T) => void) => createMotionTick<T>;
export type initCreateTweenTick<T> = (set: (value: T) => void) => createTweenTick<T>;
export type createMotionTick<T> = (prev_value: T, next_value: T) => SpringTick<T>; export type createMotionTick<T> = (prev_value: T, next_value: T) => SpringTick<T>;
export type createTweenTick<T> = (prev_value: T, next_value: T) => TweenTick;
export type SpringTick<T> = (current_value: T, elapsed: number, dt: number) => boolean; export type SpringTick<T> = (current_value: T, elapsed: number, dt: number) => boolean;
export type TweenTick = (t: number) => boolean; export type TweenTick = (t: number) => boolean;
/** applies motion fn to every leaf of any object */ /** applies motion fn to every leaf of any object */
function parseStructure<T>(obj: T, schema: initCreateMotionTick<T>): initCreateMotionTick<T> { function parseStructure<T>(
obj: unknown,
schema: initCreateMotionTick<T> | initCreateTweenTick<T>
): initCreateMotionTick<T> | initCreateTweenTick<T> {
const isArray = Array.isArray(obj); const isArray = Array.isArray(obj);
if (typeof obj === 'object' && obj !== null && (isArray || Object.prototype === Object.getPrototypeOf(obj))) { if (typeof obj === 'object' && obj !== null && (isArray || Object.prototype === Object.getPrototypeOf(obj))) {
const keys = Object.keys(obj); const keys = Object.keys(obj);
@ -155,15 +159,13 @@ function parseStructure<T>(obj: T, schema: initCreateMotionTick<T>): initCreateM
tickers = new Array(l), tickers = new Array(l),
pending = 0; pending = 0;
const target = { ...obj }; const target = { ...obj };
//@ts-ignore obj = isArray ? [...(obj as T[])] : { ...obj };
obj = isArray ? [...obj] : { ...obj };
return (set) => (_from_value, to_value) => { return (set) => (_from_value, to_value) => {
for (k in to_value) if (to_value[k] !== obj[k]) target[k] = to_value[k]; 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]])); for (i = 0; i < l; i++) (pending |= 1 << i), (tickers[i] = createTickers[i](obj[keys[i]], target[keys[i]]));
return (_current, elapsed, dt) => { return (_current, elapsed, dt) => {
for (i = 0; i < l; i++) if (pending & (1 << i) && !tickers[i](obj[keys[i]], elapsed, dt)) pending &= ~(1 << i); 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 as T[])] : { ...(obj as any) });
set(isArray ? [...obj] : { ...obj });
return !!pending; return !!pending;
}; };
}; };
@ -173,18 +175,17 @@ function parseStructure<T>(obj: T, schema: initCreateMotionTick<T>): initCreateM
abstract class MotionStore<T> extends Store<T> { abstract class MotionStore<T> extends Store<T> {
running = false; running = false;
cancel = noop; cancel = noop;
initCreateTicker: initCreateMotionTick<T>; initCreateTicker;
createTicker: createMotionTick<T>; createTicker;
tick; tick;
constructor(value: T, startSetTick: initCreateMotionTick<T>) { constructor(value: T, startSetTick: initCreateMotionTick<T> | initCreateTweenTick<T>) {
super(value); super(value);
this.createTicker = parseStructure(value, (this.initCreateTicker = startSetTick))(super.set.bind(this)); this.createTicker = parseStructure(value, (this.initCreateTicker = startSetTick))(super.set.bind(this));
} }
set(next_value: T) { set(next_value: T) {
const this_id = ++this.uidRunning; const this_id = ++this.uidRunning;
this.clearStateSubscribers(false); this.clearStateSubscribers(false);
//@ts-ignore if (!this.value && (this.value as unknown) !== 0) {
if (!this.value && this.value !== 0) {
this.setImmediate(next_value); this.setImmediate(next_value);
} else { } else {
this.tick = this.createTicker(this.value, next_value); this.tick = this.createTicker(this.value, next_value);
@ -232,14 +233,18 @@ abstract class MotionStore<T> extends Store<T> {
} }
} }
export class SpringMotion<T> extends MotionStore<T> { export class SpringMotion<T> extends MotionStore<T> {
elapsed = 0.0; initCreateTicker: initCreateMotionTick<T>;
createTicker: createMotionTick<T>;
tick: SpringTick<T>; tick: SpringTick<T>;
elapsed = 0.0;
loop(stop) { loop(stop) {
this.elapsed = 0.0; this.elapsed = 0.0;
if (!this.running) this.cancel = onEachFrame((dt) => this.tick(this.value, (this.elapsed += dt), dt), stop); if (!this.running) this.cancel = onEachFrame((dt) => this.tick(this.value, (this.elapsed += dt), dt), stop);
} }
} }
export class TweenMotion<T> extends MotionStore<T> { export class TweenMotion<T> extends MotionStore<T> {
initCreateTicker: initCreateTweenTick<T>;
createTicker: createTweenTick<T>;
tick: TweenTick; tick: TweenTick;
loop(stop) { loop(stop) {
if (this.running) this.cancel(); if (this.running) this.cancel();

@ -1,11 +1,13 @@
import { run_all } from './utils';
import { now } from './environment';
import { setAnimationTimeout, useTween } from './loop';
import { animate_css } from './style_manager';
import { custom_event } from './dom';
import { TransitionConfig } from '../transition'; import { TransitionConfig } from '../transition';
import { add_measure_callback, add_render_callback } from './scheduler';
import { Fragment } from './Component'; import { Fragment } from './Component';
import { custom_event } from './dom';
import { now } from './environment';
import { setFrameTimeout, setTweenTimeout } from './loop';
import { add_measure_callback } from './scheduler';
import { animate_css } from './style_manager';
type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig;
type StopResetReverse = (reset_reverse?: 1 | -1) => StopResetReverse;
function startStopDispatcher(node: Element, is_intro: boolean) { function startStopDispatcher(node: Element, is_intro: boolean) {
node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}start`)); node.dispatchEvent(custom_event(`${is_intro ? 'intro' : 'outro'}start`));
@ -14,92 +16,132 @@ function startStopDispatcher(node: Element, is_intro: boolean) {
const outroing = new Set(); const outroing = new Set();
let outros; let transition_group;
export function group_outros() { export const group_outros = () => void (transition_group = { p: transition_group, c: [], r: 0 });
outros = {
/* parent group */ p: outros,
/* callbacks */ c: [],
/* remaining outros */ r: 0,
};
}
export function check_outros() { export const check_outros = () => {
if (!outros.r) run_all(outros.c); if (!transition_group.r) for (let i = 0; i < transition_group.c.length; i++) transition_group.c[i]();
outros = outros.p; transition_group = transition_group.p;
} };
export function transition_in(block: Fragment, local?: 0 | 1) { export const transition_in = (block: Fragment, local?: 0 | 1) => {
if (!block || !block.i) return; if (!block || !block.i) return;
outroing.delete(block); outroing.delete(block);
block.i(local); block.i(local);
} };
export function transition_out(block: Fragment, local?: 0 | 1, detach?: 0 | 1, callback?: () => void) { export const transition_out = (block: Fragment, local?: 0 | 1, detach?: 0 | 1, callback?: () => void) => {
if (!block || !block.o || outroing.has(block)) return; if (!block || !block.o || outroing.has(block)) return;
outroing.add(block); outroing.add(block);
outros.c.push(() => { transition_group.c.push(() => {
if (!outroing.has(block)) return; if (!outroing.has(block)) return;
outroing.delete(block); else outroing.delete(block);
if (!callback) return; if (!callback) return;
if (detach) block.d(1); if (detach) block.d(1);
callback(); callback();
}); });
block.o(local); block.o(local);
} };
const eased = (fn: (t: number) => any, easing: (t: number) => number, start, end) => (t: number) => /* todo: deprecate */
fn(start + (end - start) * easing(t)); const swap = (fn, is_intro) => (is_intro ? (t) => fn(t, 1 - t) : (t) => fn(1 - t, t));
//easing ? (!is_intro ? (t: number) => fn(easing(t)) : (t: number) => fn(1 - easing(1 - t))) : fn;
const runner = (fn: (t0: number, t1: number) => any, is_intro: boolean) => const mirrored = (fn, is_intro, easing) => {
is_intro ? (t: number) => fn(t, 1 - t) : (t: number) => fn(1 - t, t); const run = swap(fn, is_intro);
const mirror = (fn, easing, is_intro) => { return easing ? (!is_intro ? (t) => run(1 - easing(1 - t)) : (t) => run(easing(t))) : run;
const run = is_intro ? (t) => fn(1 - t, t) : (t) => fn(t, 1 - t);
return easing ? (is_intro ? (t) => run(1 - easing(1 - t)) : (t) => run(easing(t))) : run;
}; };
type TransitionFn = (node: HTMLElement, params: any) => TransitionConfig;
export function run_transition( const reversed = (fn, is_intro, easing, start = 0, end = 1) => {
const run = swap(fn, is_intro);
const difference = end - start;
return easing ? (t) => run(start + difference * easing(t)) : (t) => run(start + difference * t);
};
export const run_transition = (
node: HTMLElement, node: HTMLElement,
fn: TransitionFn, fn: TransitionFn,
is_intro = true, is_intro = true,
params = {}, params = {},
left_duration = 0, /* internal to this file */
prev_left = 0 is_bidirectional = false,
): StopResetReverse { elapsed_duration = 0,
delay_left = -1,
elapsed_ratio = 0
) => {
let config; let config;
let running = true; let running = true;
let cancel_css; let cancel_css;
let cancel_raf; let cancel_raf;
let dispatch_end; let dispatch_end;
let end_time;
let t;
let start_ratio;
const group = outros; let start_time = 0;
if (!is_intro) group.r++; let end_time = 0;
const start = ({ delay = 0, duration = 300, easing, tick, css }: TransitionConfig) => { const group = transition_group;
if (!is_intro) transition_group.r++;
const start = ({ delay = 0, duration = 300, easing, tick, css, strategy = 'reverse' }: TransitionConfig) => {
if (!running) return; if (!running) return;
t = duration - (left_duration > 0 ? left_duration : 0);
end_time = now() + t; if (~delay_left) delay = delay_left;
start_ratio = 1 - easing((t - prev_left) / duration);
if (css) cancel_css = animate_css(eased(runner(css, is_intro), easing, start_ratio, 1), node, t, 0); const solver = strategy === 'reverse' ? reversed : mirrored;
const runner = (fn) => solver(fn, is_intro, easing, elapsed_ratio, 1);
if (solver === mirrored) {
delay -= elapsed_duration;
} else if (solver === reversed) {
duration -= elapsed_duration;
}
end_time = (start_time = now() + delay) + duration;
dispatch_end = startStopDispatcher(node, is_intro); dispatch_end = startStopDispatcher(node, is_intro);
cancel_raf = tick
? useTween(eased(runner(tick, is_intro), easing, start_ratio, 1), stop, end_time, t) if (css) cancel_css = animate_css(runner(css), node, duration, delay);
: setAnimationTimeout(stop, end_time); cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration) : setFrameTimeout(stop, end_time);
}; };
const stop = (end_reset_reverse?: number | 1 | -1) => { const stop: StopResetReverse = (t?: number | 1 | -1) => {
if (!running) return; if (!running) return;
else running = false; else running = false;
if (cancel_css) cancel_css(); if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf(); if (cancel_raf) cancel_raf();
if (t > end_time && dispatch_end) dispatch_end(); if (t > end_time) {
if (!is_intro && !--group.r) for (let i = 0; i < group.c.length; i++) group.c[i](); if (dispatch_end) {
if (!~end_reset_reverse) dispatch_end();
return run_transition(node, () => config, !is_intro, params, end_time - now(), left_duration); }
else if (left_duration) running_bidi.delete(node); }
if (!is_intro) {
if (!--group.r) {
for (let i = 0; i < group.c.length; i++) {
group.c[i]();
}
}
}
if (is_bidirectional) {
if (-1 === t) {
return (
(t = now()) < end_time &&
run_transition(
node,
() => config,
!is_intro,
params,
true,
end_time - t,
start_time > t ? start_time - t : 0,
(1 - elapsed_ratio) * (1 - config.easing(1 - (end_time - t) / (end_time - start_time)))
)
);
} else {
running_bidi.delete(node);
}
}
}; };
add_measure_callback(() => { add_measure_callback(() => {
@ -108,12 +150,14 @@ export function run_transition(
}); });
return stop; return stop;
} };
export type StopResetReverse = (reset_reverse?: 1 | -1) => StopResetReverse;
const running_bidi: Map<HTMLElement, StopResetReverse> = new Map(); const running_bidi: Map<HTMLElement, StopResetReverse> = new Map();
export function run_bidirectional_transition(node: HTMLElement, fn: TransitionFn, is_intro: boolean, params: any) { export const run_bidirectional_transition = (node: HTMLElement, fn: TransitionFn, is_intro: boolean, params: any) => {
let cancel; let cancel;
if (running_bidi.has(node) && (cancel = running_bidi.get(node)(-1))) running_bidi.set(node, cancel); if (running_bidi.has(node) && (cancel = running_bidi.get(node)(-1))) running_bidi.set(node, cancel);
else running_bidi.set(node, (cancel = run_transition(node, fn, is_intro, params, -1))); else running_bidi.set(node, (cancel = run_transition(node, fn, is_intro, params, true)));
return cancel; return cancel;
} };
export const run_duration = (duration, ...args): number =>
typeof duration === 'function' ? duration.apply(null, args) : duration;

@ -0,0 +1,9 @@
export function dates(a, b) {
const difference = (b = new Date(b).getTime()) - (a = new Date(a).getTime());
const d = new Date(a);
return (t) => (d.setTime(a + difference * t), d);
}
export function numbers(a, b) {
const d = (b = +b) - (a = +a);
return (t) => a + d * t;
}

@ -1,2 +1,119 @@
export * from './spring'; import { SpringMotion, TweenMotion, now, run_duration } from 'svelte/internal';
// export * from './_tweened'; import { numbers } from 'svelte/interpolate';
interface TweenParams<T> {
delay?: number;
duration?: number | ((from: T, to: T) => number);
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}
interface SpringParams {
stiffness?: number /* { 10 < 100 < 200 } */;
damping?: number /* { 1 < 10 < 20 } */;
mass?: number /* { 0.1 < 1 < 20 } */;
precision?: number /* = 0.001 */;
soft?: boolean /* disables damping */;
}
function solve_spring(
prev_value: number,
prev_velocity: number,
target_value: number,
{ stiffness, mass, damping, soft }: SpringParams
) {
const delta = target_value - prev_value;
if (soft || 1 <= damping / (2.0 * Math.sqrt(stiffness * mass))) {
const angular_frequency = -Math.sqrt(stiffness / mass);
return (t: number) =>
target_value - (delta + t * (-angular_frequency * delta - prev_velocity)) * Math.exp(t * angular_frequency);
} else {
const damping_frequency = Math.sqrt(4.0 * mass * stiffness - damping ** 2);
const leftover = (damping * delta - 2.0 * mass * prev_velocity) / damping_frequency;
const dfm = (0.5 * damping_frequency) / mass;
const dm = -(0.5 * damping) / mass;
let f = 0.0;
return (t: number) => target_value - (Math.cos((f = t * dfm)) * delta + Math.sin(f) * leftover) * Math.exp(t * dm);
}
}
export function spring(
value,
{ mass = 1.0, damping = 10.0, stiffness = 100.0, precision = 0.001, soft = false }: SpringParams = {}
) {
const store = new SpringMotion(value, (set) => {
let velocity = 0.0,
calc;
return (from_value, to_value) => {
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?: SpringParams) {
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;
}
export function tween<T>(
value: T,
{
delay: default_delay = 0,
duration: default_duration = 400,
easing: default_easing = (v) => v,
interpolate: default_interpolate = numbers,
}: TweenParams<T>
) {
let delay = default_delay,
duration = default_duration,
easing = default_easing,
interpolate = default_interpolate;
const store = new TweenMotion(value, (set) => {
let end_time = 0,
this_duration = 0,
calc;
return (from_value: T, to_value: T) => {
end_time = now() + delay + (this_duration = run_duration(duration, from_value, to_value));
calc = interpolate(from_value, to_value);
return (t) => {
t = 1 - (end_time - t) / this_duration;
if (t >= 1) return set(calc(easing(1))), false;
if (t >= 0) set(calc(easing(t)));
return true;
};
};
});
const set = (next_value, params) => {
delay = (params && params.delay) || default_delay;
duration = +((params && 'duration' in params) || default_duration);
easing = (params && params.easing) || default_easing;
interpolate = (params && params.interpolate) || default_interpolate;
return store.set(next_value);
};
return {
set,
/* todo: test update() */
update: (fn, params) => set(fn(store.value, value), params),
setImmediate: store.setImmediate.bind(store),
subscribe: store.subscribe.bind(store),
onRest: store.onRest.bind(store),
};
}

@ -1,111 +0,0 @@
import { SpringMotion, TweenMotion, now } from 'svelte/internal';
import { is_date } from './utils';
function solve_spring(
prev_value: number,
prev_velocity: number,
target_value: number,
{ stiffness, mass, damping, soft }
) {
const delta = target_value - prev_value;
if (soft || 1 <= damping / (2.0 * Math.sqrt(stiffness * mass))) {
const angular_frequency = -Math.sqrt(stiffness / mass);
return (t: number) =>
target_value - (delta + t * (-angular_frequency * delta - prev_velocity)) * Math.exp(t * angular_frequency);
} else {
const damping_frequency = Math.sqrt(4.0 * mass * stiffness - damping ** 2);
const leftover = (damping * delta - 2.0 * mass * prev_velocity) / damping_frequency;
const dfm = (0.5 * damping_frequency) / mass;
const dm = -(0.5 * damping) / mass;
let f = 0.0;
return (t: number) => target_value - (Math.cos((f = t * dfm)) * delta + Math.sin(f) * leftover) * Math.exp(t * dm);
}
}
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, 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`);
}
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 {
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,3 +0,0 @@
export function is_date(obj: any): obj is Date {
return Object.prototype.toString.call(obj) === '[object Date]';
}

@ -1,10 +1,12 @@
import { cubicOut, cubicInOut, linear } from 'svelte/easing'; import { cubicOut, cubicInOut } from 'svelte/easing';
import { run_duration } from 'svelte/internal';
type EasingFunction = (t: number) => number; type EasingFunction = (t: number) => number;
interface BasicConfig { interface BasicConfig {
delay?: number; delay?: number;
duration?: number; duration?: number;
easing?: EasingFunction; easing?: EasingFunction;
strategy?: 'reverse' | 'mirror';
} }
interface TimeableConfig extends Omit<BasicConfig, 'duration'> { interface TimeableConfig extends Omit<BasicConfig, 'duration'> {
duration?: number | ((len: number) => number); duration?: number | ((len: number) => number);
@ -31,18 +33,13 @@ export function blur(
delay, delay,
duration, duration,
easing, easing,
css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * amount}px);`, css: (_t, u) => `opacity: ${target_opacity - od * u}; filter:${f} blur(${u * amount}px);`,
}; };
} }
export function fade(node: Element, { delay = 0, duration = 400, easing = linear }: BasicConfig): TransitionConfig { export function fade(node: Element, { delay = 0, duration = 400, easing }: BasicConfig): TransitionConfig {
const o = +getComputedStyle(node).opacity; const o = +getComputedStyle(node).opacity;
return { return { delay, duration, easing, css: (t) => `opacity: ${t * o};` };
delay,
duration,
easing,
css: (t) => `opacity: ${t * o};`,
};
} }
interface FlyParams extends BasicConfig { interface FlyParams extends BasicConfig {
@ -57,15 +54,13 @@ export function fly(
): TransitionConfig { ): TransitionConfig {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const target_opacity = +style.opacity; const target_opacity = +style.opacity;
const transform = style.transform === 'none' ? '' : style.transform; const prev = style.transform === 'none' ? '' : style.transform;
const od = target_opacity * (1 - opacity); const od = target_opacity * (1 - opacity);
return { return {
delay, delay,
duration, duration,
easing, easing,
css: (t, u) => ` css: (_t, u) => `transform: ${prev} translate(${u * x}px, ${u * y}px); opacity: ${target_opacity - od * u};`,
transform: ${transform} translate(${u * x}px, ${u * y}px);
opacity: ${target_opacity - od * u}`,
}; };
} }
@ -114,10 +109,7 @@ export function scale(
delay, delay,
duration, duration,
easing, easing,
css: (_t, u) => ` css: (_t, u) => `transform: ${transform} scale(${1 - sd * u}); opacity: ${target_opacity - od * u};`,
transform: ${transform} scale(${1 - sd * u});
opacity: ${target_opacity - od * u};
`,
}; };
} }
@ -131,7 +123,7 @@ export function draw(
): TransitionConfig { ): TransitionConfig {
const len = node.getTotalLength(); const len = node.getTotalLength();
if (duration === undefined) duration = speed ? len / speed : 800; if (duration === undefined) duration = speed ? len / speed : 800;
else if (typeof duration === 'function') duration = duration(len); else duration = run_duration(duration, len);
return { delay, duration, easing, css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};` }; return { delay, duration, easing, css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};` };
} }
interface CrossFadeConfig extends TimeableConfig { interface CrossFadeConfig extends TimeableConfig {
@ -165,7 +157,7 @@ export function crossfade({
return { return {
delay, delay,
easing, easing,
duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration, duration: run_duration(duration, Math.sqrt(dx * dx + dy * dy)),
css: (t, u) => ` css: (t, u) => `
opacity: ${t * +opacity}; opacity: ${t * +opacity};
transform-origin: top left; transform-origin: top left;

Loading…
Cancel
Save