pull/4999/head
pushkine 5 years ago
parent 5d0430e795
commit 0353d52d91

@ -740,15 +740,16 @@ export default class ElementWrapper extends Wrapper {
} }
add_bidi_transition(block: Block, intro: Transition) { add_bidi_transition(block: Block, intro: Transition) {
const name = block.get_unique_name(`${this.var.name}_transition`); const transition = block.get_unique_name(`${this.var.name}_transition`);
const snippet = intro.expression ? intro.expression.manipulate(block) : null; const snippet = intro.expression ? intro.expression.manipulate(block) : null;
const fn = this.renderer.reference(intro.name);
block.add_variable(name, x`@noop`); block.add_variable(transition);
block.chunks.create.push(b`${transition} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet})`)
const fn = this.renderer.reference(intro.name);
let intro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, 1, ${snippet});`; let intro_block = b`${transition}.i();`;
let outro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, 2, ${snippet});`; let outro_block = b`${transition}.o();`;
if (intro.is_local) { if (intro.is_local) {
intro_block = b`if (#local) { ${intro_block} }`; intro_block = b`if (#local) { ${intro_block} }`;
@ -757,7 +758,7 @@ export default class ElementWrapper extends Wrapper {
block.chunks.intro.push(intro_block); block.chunks.intro.push(intro_block);
block.chunks.outro.push(outro_block); block.chunks.outro.push(outro_block);
block.chunks.destroy.push(b`if (detaching) ${name}();`); block.chunks.destroy.push(b`if (detaching) ${transition}.d();`);
} }
add_intro(block: Block, intro: Transition, outro: Transition) { add_intro(block: Block, intro: Transition, outro: Transition) {
if (outro) { if (outro) {
@ -779,7 +780,7 @@ export default class ElementWrapper extends Wrapper {
const [intro_var, node, transitionFn, params] = run_transition(this, block, intro, `intro`); const [intro_var, node, transitionFn, params] = run_transition(this, block, intro, `intro`);
block.add_variable(intro_var, outro ? x`@noop`: null); block.add_variable(intro_var, outro ? x`@noop`: null);
let start_intro = b`${intro_var} = @run_transition(${node}, ${transitionFn}, 1, ${params});`; let start_intro = b`${intro_var} = @run_in(${node}, ${transitionFn}, ${params}, this);`;
if (!outro) start_intro = b`if (!${intro_var}) { ${start_intro} }`; if (!outro) start_intro = b`if (!${intro_var}) { ${start_intro} }`;
if (intro.is_local) start_intro = b`if (#local) { ${start_intro} }`; if (intro.is_local) start_intro = b`if (#local) { ${start_intro} }`;
block.chunks.intro.push(start_intro); block.chunks.intro.push(start_intro);
@ -797,7 +798,7 @@ export default class ElementWrapper extends Wrapper {
const [outro_var, node, transitionFn, params] = run_transition(this, block, outro, `outro`); const [outro_var, node, transitionFn, params] = run_transition(this, block, outro, `outro`);
block.add_variable(outro_var, x`@noop`); block.add_variable(outro_var, x`@noop`);
let start_outro = b`${outro_var} = @run_transition(${node}, ${transitionFn}, 2, ${params});`; let start_outro = b`${outro_var} = @run_out(${node}, ${transitionFn}, ${params}, this);`;
if (outro.is_local) start_outro = b`if (#local) { ${start_outro} }`; if (outro.is_local) start_outro = b`if (#local) { ${start_outro} }`;
block.chunks.outro.push(start_outro); block.chunks.outro.push(start_outro);
@ -824,7 +825,7 @@ export default class ElementWrapper extends Wrapper {
`); `);
block.chunks.animate.push(b` block.chunks.animate.push(b`
if (${unfreeze_var}) return if (${unfreeze_var} || !${rect_var}) return
else { else {
${stop_animation_var}(); ${stop_animation_var}();
${stop_animation_var} = @run_animation(${this.var}, ${rect_var}, ${name_var}, ${params_var}); ${stop_animation_var} = @run_animation(${this.var}, ${rect_var}, ${name_var}, ${params_var});

@ -1,6 +1,5 @@
import { cubicOut } from "svelte/easing"; import { cubicOut } from "svelte/easing";
import { run_duration } from "svelte/internal"; import { run_duration, TimeableConfig, CssTransitionConfig } from "svelte/internal";
import { CssTransitionConfig, TimeableConfig } from "svelte/transition";
export function flip( export function flip(
node: Element, node: Element,
animation: { from: DOMRect; to: DOMRect }, animation: { from: DOMRect; to: DOMRect },

@ -1,42 +0,0 @@
import { run_transition } from './transitions';
import { methodify, noop } from './utils';
import { CssTransitionConfig } from 'svelte/transition';
type Rect = DOMRect | ClientRect;
type AnimationFn = (node: Element, { from, to }: { from: Rect; to: Rect }, params: any) => CssTransitionConfig;
export const run_animation = /*#__PURE__*/ methodify(
function run_animation(this: HTMLElement, from: Rect, fn: AnimationFn, params = {}) {
if (!from) return noop;
return run_transition(
this,
(_, params) => {
const to = this.getBoundingClientRect();
if (from.left !== to.left || from.right !== to.right || from.top !== to.top || from.bottom !== to.bottom) {
return fn(this, { from, to }, params);
} else return null;
},
9,
params
);
}
);
export const fix_position = /*#__PURE__*/ methodify(
function fix_position(this: HTMLElement, { left, top }: Rect) {
const { position, width, height, transform } = getComputedStyle(this);
if (position === 'absolute' || position === 'fixed') return noop;
const { position: og_position, width: og_width, height: og_height } = this.style;
this.style.position = 'absolute';
this.style.width = width;
this.style.height = height;
const b = this.getBoundingClientRect();
this.style.transform = `${transform === 'none' ? '' : transform} translate(${left - b.left}px, ${top - b.top}px)`;
return () => {
this.style.position = og_position;
this.style.width = og_width;
this.style.height = og_height;
this.style.transform = ''; // unsafe
};
}
);

@ -1,20 +1,12 @@
import { noop } from './utils'; import { noop } from "./utils";
export const is_client = typeof window !== 'undefined'; export const is_client = typeof window !== "undefined";
export let now: () => number = is_client export let now: () => number = is_client
? () => window.performance.now() ? () => window.performance.now()
: () => Date.now(); : () => Date.now();
export let raf = is_client ? cb => requestAnimationFrame(cb) : noop; export let raf = /*#__PURE__*/ is_client ? (cb: FrameRequestCallback) => requestAnimationFrame(cb) : noop;
export let framerate = 1000 / 60;
/*#__PURE__*/ raf((t1) => {
raf((d) => {
const f24 = 1000 / 24;
const f144 = 1000 / 144;
framerate = (d = d - t1) > f144 ? f144 : d < f24 ? f24 : d;
});
});
// used internally for testing // used internally for testing
export function set_now(fn) { export function set_now(fn) {

@ -1,4 +1,3 @@
export * from './animations';
export * from './await_block'; export * from './await_block';
export * from './dom'; export * from './dom';
export * from './environment'; export * from './environment';
@ -12,4 +11,5 @@ export * from './ssr';
export * from './transitions'; export * from './transitions';
export * from './utils'; export * from './utils';
export * from './Component'; export * from './Component';
export * from './style_manager'
export * from './dev'; export * from './dev';

@ -1,5 +1,31 @@
import { now, raf, framerate } from './environment'; import { now, raf } from './environment';
import { noop } from './utils'; import { noop } from './utils';
export const frame = {
rate: 1000 / 60,
time: 0.0,
sync() {
return n ? this.time : (this.time = now());
},
};
function calc_framerate() {
raf((t1) => {
raf((t2) => {
const delta = t2 - t1;
raf((t3) => {
if (Math.abs(t3 - t2 - delta) > 1) {
calc_framerate();
} else {
const f24 = 1000 / 24;
const f144 = 1000 / 144;
frame.rate = delta > f144 ? f144 : delta < f24 ? f24 : delta;
}
});
});
});
}
calc_framerate();
type TaskCallback = (t: number) => boolean; type TaskCallback = (t: number) => boolean;
type TaskCanceller = () => void; type TaskCanceller = () => void;
@ -13,7 +39,7 @@ let next_frame: TaskCallback[] = [];
const run = (t: number) => { const run = (t: number) => {
[running_frame, next_frame] = [next_frame, running_frame]; [running_frame, next_frame] = [next_frame, running_frame];
for (t = now(), i = n = 0, j = running_frame.length; i < j; i++) { for (t = (frame.time = now()), i = n = 0, j = running_frame.length; i < j; i++) {
if ((v = running_frame[i])(t)) { if ((v = running_frame[i])(t)) {
next_frame[n++] = v; next_frame[n++] = v;
} }
@ -33,22 +59,26 @@ let running_timed = false;
const run_timed = (now: number) => { const run_timed = (now: number) => {
let last_index = timed_tasks.length - 1; let last_index = timed_tasks.length - 1;
while (~last_index && now >= timed_tasks[last_index].timestamp) timed_tasks[last_index--].callback(now); while (-1 !== last_index && now >= timed_tasks[last_index].timestamp) {
timed_tasks[last_index--].callback(now);
}
if (pending_inserts) { if (pending_inserts) {
for (let i = 0, j = 0, this_task: TimeoutTask, that_task: TimeoutTask; i < pending_insert_timed.length; i++) for (let i = 0, j = 0, this_task: TimeoutTask, that_task: TimeoutTask; i < pending_insert_timed.length; i++) {
if (now >= (this_task = pending_insert_timed[i]).timestamp) this_task.callback(now); if (now >= (this_task = pending_insert_timed[i]).timestamp) {
else { this_task.callback(now);
for (j = last_index; ~j && this_task.timestamp > (that_task = timed_tasks[j]).timestamp; j--) } else {
for (j = last_index; -1 !== j && this_task.timestamp > (that_task = timed_tasks[j]).timestamp; j--) {
timed_tasks[j + 1] = that_task; timed_tasks[j + 1] = that_task;
}
timed_tasks[j + 1] = this_task; timed_tasks[j + 1] = this_task;
last_index++; last_index++;
} }
}
pending_insert_timed.length = 0; pending_insert_timed.length = 0;
pending_inserts = false; pending_inserts = false;
} }
return (running_timed = !!(timed_tasks.length = last_index + 1)); return (running_timed = !!(timed_tasks.length = last_index + 1));
}; };
const unsafe_loop = (fn) => { const unsafe_loop = (fn) => {
if (0 === n) raf(run); if (0 === n) raf(run);
next_frame[n++] = fn; next_frame[n++] = fn;
@ -56,8 +86,7 @@ const unsafe_loop = (fn) => {
export const loop = (fn) => { export const loop = (fn) => {
let running = true; let running = true;
if (0 === n) raf(run); unsafe_loop((t) => running && fn(t));
next_frame[n++] = (t) => !running || fn(t);
return () => void (running = false); return () => void (running = false);
}; };
@ -70,52 +99,23 @@ export const setFrameTimeout = (callback: (t: number) => void, timestamp: number
running_timed = true; running_timed = true;
timed_tasks.push(task); timed_tasks.push(task);
} }
return () => void (task.callback = noop); return () => {
task.callback = noop;
};
}; };
/**
* Calls function every frame with linear tween from 0 to 1
*/
export const setTweenTimeout = ( export const setTweenTimeout = (
stop: (now: number) => void, stop: (now: number) => void,
end_time: number, end_time: number,
run: (now: number) => void, run: (now: number) => void,
duration = end_time - now(), duration = end_time - frame.sync()
is_outro = false
): TaskCanceller => { ): TaskCanceller => {
let running = true; let t = 0.0;
let t = 1 - (end_time - now()) / duration || 0; return loop((now) => {
if (!is_outro && t <= 1.0) run(t >= 0.0 ? t : 0);
unsafe_loop((now) => {
if (!running) return false;
t = 1 - (end_time - now) / duration; t = 1 - (end_time - now) / duration;
if (t >= 1.0) return run(1), stop(now), false; if (t >= 1.0) return run(1), stop(now), false;
if (t >= 0.0) run(t); if (t >= 0.0) run(t);
return running; return true;
}); });
return (run_last = false) => {
if (run_last) run(1);
running = false;
};
};
/**
* Calls function every frame with time elapsed in seconds
*/
export const onEachFrame = (
callback: (seconds_elapsed: number) => boolean,
on_stop?,
max_skipped_frames = 4
): TaskCanceller => {
max_skipped_frames *= framerate;
let lastTime = now();
let running = true;
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 callback((-lastTime + (lastTime = t)) / 1000) ? true : cancel(t);
});
return () => void (running = false);
}; };
/** tests only */ /** tests only */

@ -1,6 +1,6 @@
import { set_current_component } from './lifecycle'; import { set_current_component } from './lifecycle';
import { now } from './environment';
import { T$$ } from './Component'; import { T$$ } from './Component';
import { frame } from './loop';
const resolved_promise = Promise.resolve(); const resolved_promise = Promise.resolve();
@ -17,7 +17,9 @@ const flush_callbacks = [];
// todo : remove add_flush_callback // todo : remove add_flush_callback
export const add_flush_callback = /*#__PURE__*/ Array.prototype.push.bind(flush_callbacks); export const add_flush_callback = /*#__PURE__*/ Array.prototype.push.bind(flush_callbacks);
export const add_measure_callback = /*#__PURE__*/ Array.prototype.push.bind(measure_callbacks); type MeasureCallback = () => FlushCallback
type FlushCallback = (current_frame_time: number) => void
export const add_measure_callback: (...args: MeasureCallback[]) => number = /*#__PURE__*/ Array.prototype.push.bind(measure_callbacks);
const seen_render_callbacks = new Set(); const seen_render_callbacks = new Set();
export const add_render_callback = (fn) => { export const add_render_callback = (fn) => {
@ -48,9 +50,10 @@ export const flush = () => {
if (is_flushing) return; if (is_flushing) return;
else is_flushing = true; else is_flushing = true;
frame.sync();
let i = 0; let i = 0;
let j = 0; let j = 0;
let t = 0;
let $$: T$$; let $$: T$$;
let dirty; let dirty;
let before_update; let before_update;
@ -103,7 +106,7 @@ export const flush = () => {
// apply styles // apply styles
// todo : remove every non style callback from flush_callbacks // todo : remove every non style callback from flush_callbacks
for (t = now(); i < j; i++) flush_callbacks[i](t); for (const t = frame.time; i < j; i++) flush_callbacks[i](t);
flush_callbacks.length = i = j = 0; flush_callbacks.length = i = j = 0;
is_flushing = false; is_flushing = false;

@ -1,5 +1,5 @@
import { framerate } from './environment'; import { frame } from './loop';
import { methodify } from './utils'; import { methodify, noop } from './utils';
let documents_uid = 0; let documents_uid = 0;
let running_animations = 0; let running_animations = 0;
@ -9,7 +9,7 @@ const document_stylesheets = new Map();
const current_rules = new Set(); const current_rules = new Set();
export const animate_css = /*#__PURE__*/ methodify( export const animate_css = /*#__PURE__*/ methodify(
function animate_css(this: HTMLElement, css: (t: number) => string, duration: number, delay = 0) { function (this: HTMLElement, css: (t: number) => string, duration: number, delay = 0) {
if (!document_uid.has(this.ownerDocument)) { if (!document_uid.has(this.ownerDocument)) {
document_uid.set(this.ownerDocument, documents_uid++); document_uid.set(this.ownerDocument, documents_uid++);
document_stylesheets.set( document_stylesheets.set(
@ -18,7 +18,7 @@ export const animate_css = /*#__PURE__*/ methodify(
); );
} }
let rule = '{\n'; let rule = '{\n';
for (let t = 0, step = framerate / duration; t < 1; t += step) rule += `${100 * t}%{${css(t)}}\n`; for (let t = 0, step = frame.rate / duration; t < 1; t += step) rule += `${100 * t}%{${css(t)}}\n`;
rule += `100% {${css(1)}}\n}`; rule += `100% {${css(1)}}\n}`;
// darkskyapp/string-hash // darkskyapp/string-hash
@ -34,6 +34,7 @@ export const animate_css = /*#__PURE__*/ methodify(
} }
const previous = this.style.animation; const previous = this.style.animation;
if (previous) {console.error("stacked animations"); return noop}
this.style.animation = `${ this.style.animation = `${
previous ? `${previous}, ` : '' previous ? `${previous}, ` : ''
}${duration}ms linear ${delay}ms 1 normal both running ${name}`; }${duration}ms linear ${delay}ms 1 normal both running ${name}`;
@ -59,3 +60,22 @@ export const animate_css = /*#__PURE__*/ methodify(
}; };
} }
); );
export const fix_position = /*#__PURE__*/ methodify(
function (this: HTMLElement, { left, top }: DOMRect | ClientRect) {
const { position, width, height, transform } = getComputedStyle(this);
if (position === 'absolute' || position === 'fixed') return noop;
const { position: og_position, width: og_width, height: og_height, transform: og_transform } = this.style;
this.style.position = 'absolute';
this.style.width = width;
this.style.height = height;
const b = this.getBoundingClientRect();
this.style.transform = `${transform === 'none' ? '' : transform} translate(${left - b.left}px, ${top - b.top}px)`;
return () => {
// unsafe
this.style.position = og_position;
this.style.width = og_width;
this.style.height = og_height;
this.style.transform = og_transform;
};
}
);

@ -1,44 +1,70 @@
import { CssTransitionConfig } from '../transition'; import { Fragment } from "./Component";
import { Fragment } from './Component'; import { custom_event } from "./dom";
import { custom_event } from './dom'; import { setFrameTimeout, setTweenTimeout, frame } from "./loop";
import { now } from './environment'; import { add_measure_callback, tick } from "./scheduler";
import { setFrameTimeout, setTweenTimeout } from './loop'; import { animate_css } from "./style_manager";
import { add_measure_callback } from './scheduler'; import { methodify, noop } from "./utils";
import { animate_css } from './style_manager';
import { methodify, noop } from './utils';
type TransitionFn = (node: HTMLElement, params: any) => CssTransitionConfig;
export type StopResetReverseFn = (t?: number | -1) => StopResetReverseFn | void;
export interface CssAnimationConfig {
delay?: number;
duration?: number;
easing?: (t: number) => number;
}
export interface CssTransitionConfig extends CssAnimationConfig {
css?: (t: number, u?: number) => string;
tick?: (t: number, u?: number) => void;
strategy?: EasingStrategy;
}
export type TimeableConfig = Omit<CssAnimationConfig, 'duration'> & { duration?: number | ((len: number) => number) };
const enum EasingStrategy {
reversed = "reversed",
balanced = "balanced",
mirrored = "mirrored",
}
const enum TransitionEvent {
introstart = "introstart",
introend = "introend",
outrostart = "outrostart",
outroend = "outroend"
}
export const transition_in = (block: Fragment, local?) => { export const transition_in = (block: Fragment, local?) => {
// todo : is `!block` necessary ?
if (!block || !block.i) return; if (!block || !block.i) return;
outroing.delete(block); outroing.delete(block);
block.i(local); block.i(local);
}; };
export const transition_out = (block: Fragment, local?) => { export const transition_out = (block: Fragment, local?) => {
// todo : are `!block` and `outroing.has` checks necessary ?
if (!block || !block.o || outroing.has(block)) return; if (!block || !block.o || outroing.has(block)) return;
outroing.add(block); outroing.add(block);
block.o(local); block.o(local);
}; };
type TransitionGroup = { type TransitionGroup = {
/* parent group */ p: TransitionGroup; /* parent group */ p: TransitionGroup;
/* callbacks */ c: Array<((cancelled: boolean) => void)>; /* callbacks */ c: Array<(cancelled: boolean) => void>;
/* running outros */ r: number; /* running outros */ r: number;
/* stop callbacks */ s: Array<((t: number) => void)>; /* stop callbacks */ s: Array<(t: number) => void>;
/* outro timeout */ t: number; /* outro timeout */ t: number;
}; };
let transition_group: TransitionGroup; let transition_group: TransitionGroup;
const outroing = new Set(); const outroing = new Set();
export const group_transition_out = (fn) => { export const group_transition_out = (fn) => {
const c = []; const c = [];
const current_group = (transition_group = { p: transition_group, c, r: 0, s: [], t: 0 }); const current_group = (transition_group = {
p: transition_group,
c,
r: 0,
s: [],
t: 0,
});
fn((block, callback, detach = true) => { fn((block, callback, detach = true) => {
if (!block || !block.o || outroing.has(block)) return; if (!block || !block.o || outroing.has(block)) return;
outroing.add(block); outroing.add(block);
c.push((cancelled = false) => { c.push((cancelled = false) => {
if (cancelled) { if (cancelled) {
// block was destroyed before outro ended // block destroyed before outro ended
outroing.delete(block); outroing.delete(block);
} else if (outroing.has(block)) { } else if (outroing.has(block)) {
outroing.delete(block); outroing.delete(block);
@ -51,149 +77,256 @@ export const group_transition_out = (fn) => {
if (!current_group.r) for (let i = 0; i < c.length; i++) c[i](); if (!current_group.r) for (let i = 0; i < c.length; i++) c[i]();
transition_group = transition_group.p; transition_group = transition_group.p;
}; };
type Rect = DOMRect | ClientRect;
type MeasureCallback = () => CssTransitionConfig
type CustomTransitionFunction = (node: HTMLElement, params: any) => MeasureCallback | CssTransitionConfig;
type AnimationFn = (node: Element, { from, to }: { from: Rect; to: Rect }, params: any) => CssTransitionConfig;
type StopResetReverseFn = (t?: number | 1 | -1) => StopResetReverseFn | void;
const swap = (fn, rx) => const swap = (fn, is_intro) =>
fn.length === 1 fn.length === 1
? rx & tx.intro ? is_intro
? fn ? fn
: (t) => fn(1 - t) : (t) => fn(1 - t)
: rx & tx.intro : is_intro
? (t) => fn(t, 1 - t) ? (t) => fn(t, 1 - t)
: (t) => fn(1 - t, t); : (t) => fn(1 - t, t);
const mirrored = (fn, rx, easing, _start, _end) => { const mirrored = (fn, is_intro, easing, _start?, _end?) => {
const run = swap(fn, rx); const run = swap(fn, is_intro);
return easing return easing
? rx & tx.intro ? is_intro
? (t) => run(easing(t)) ? (t) => run(easing(t))
: (t) => run(1 - easing(1 - t)) : (t) => run(1 - easing(1 - t))
: run; : run;
}; };
const reversed = (fn, rx, easing, start = 0, end = 1) => {
const run = swap(fn, rx); const reversed = (fn, is_intro, easing, start = 0, end = 1) => {
const run = swap(fn, is_intro);
const difference = end - start; const difference = end - start;
return easing return easing
? (t) => run(start + difference * easing(t)) ? (t) => run(start + difference * easing(t))
: (t) => run(start + difference * t); : (t) => run(start + difference * t);
}; };
const enum tx {
intro = 1,
outro = 2,
reverse = 3,
bidirectional = 4,
animation = 8,
}
export const run_transition = /*#__PURE__*/ methodify(function transition(
this: HTMLElement,
fn: TransitionFn,
rx: tx,
params = {},
/* internal to this file */
elapsed_duration = 0,
delay_left = -1,
elapsed_ratio = 0
) {
let config;
export const run_animation = /*#__PURE__*/ methodify(function (this: HTMLElement, from : Rect, fn: AnimationFn, params: CssTransitionConfig = {}) {
let running = true; let running = true;
let cancel_css; let cancel_css;
let cancel_raf; let cancel_raf;
add_measure_callback(() => {
let start_time = 0; const to = this.getBoundingClientRect();
let end_time = 0; if (from.top === to.top && from.left === to.left && from.right === to.right && from.bottom === to.bottom) return noop;
const config = fn(this, { from, to }, params);
return (current_frame_time) => {
if (false === running) return;
const { delay = 0, duration = 300, easing, tick, css }: CssTransitionConfig = config;
const end_time = current_frame_time + delay + duration;
const runner = (fn) => reversed(fn, true, easing);
if (css) cancel_css = animate_css(this, runner(css), duration, delay);
cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration) : setFrameTimeout(stop, end_time);
};
});
const stop = () => {
if (false === running) return;
else running = false;
if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf();
}
return stop
});
export const run_in = /*#__PURE__*/ methodify(function (this: HTMLElement, fn: CustomTransitionFunction, params: CssTransitionConfig = {}) {
let config;
let running = true;
let cancel_css;
let cancel_raf;
let end_time;
add_measure_callback(() => {
config = fn(this, params);
return (current_frame_time) => {
let { delay = 0, duration = 300, easing, tick, css, strategy = EasingStrategy.balanced }: CssTransitionConfig =
"function" === typeof config ? (config = config()) : config;
const solver = EasingStrategy.balanced === strategy ? reversed : mirrored;
const runner = (fn) => solver(fn, true, easing);
end_time = current_frame_time + delay + duration;
this.dispatchEvent(custom_event(TransitionEvent.introstart));
if (css) cancel_css = animate_css(this, runner(css), duration, delay);
cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration ) : setFrameTimeout(stop, end_time);
};
});
const stop = (t?: number) => {
if (false === running) return;
else running = false;
if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf();
if (t && t >= end_time) this.dispatchEvent(custom_event(TransitionEvent.introend));
}
return stop
});
export const run_out = /*#__PURE__*/ methodify(function (this: HTMLElement, fn: CustomTransitionFunction, params: CssTransitionConfig = {}) {
let config;
let running = true;
let cancel_css;
let cancel_raf;
let end_time;
const current_group = transition_group; const current_group = transition_group;
if (rx & tx.outro) current_group.r++; current_group.r++;
add_measure_callback(() => { add_measure_callback(() => {
if (null === (config = fn(this, params))) return noop; config = fn(this, params);
return (current_frame_time) => { return (current_frame_time) => {
let { delay = 0, duration = 300, easing, tick, css, strategy = EasingStrategy.balanced }: CssTransitionConfig =
"function" === typeof config ? (config = config()) : config;
const solver = EasingStrategy.balanced === strategy ? reversed : mirrored;
const runner = (fn) => solver(fn, false, easing);
end_time = current_frame_time + delay + duration;
current_group.t = Math.max(end_time, current_group.t);
if (current_group.s.push(stop) === current_group.r) {
setFrameTimeout((t) => {
for (let i = 0; i < current_group.s.length; i++) {
current_group.s[i](t);
}
}, current_group.t);
}
this.dispatchEvent(custom_event(TransitionEvent.outrostart));
if (css) cancel_css = animate_css(this, runner(css), duration, delay);
if (tick) cancel_raf = setTweenTimeout(noop, end_time, runner(tick), duration);
};
});
const stop = (t?: number) => {
if (1 === t && "tick" in config) config.tick(1, 0);
if (false === running) return; if (false === running) return;
else running = false;
if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf();
if (t && t >= end_time) {
if ("tick" in config) config.tick(0, 1);
this.dispatchEvent(custom_event(TransitionEvent.outroend));
}
if(!--current_group.r) for (let i = 0, { c } = current_group, r = t === void 0;i < c.length;i++) c[i](r);
}
return stop
});
export const create_bidirectional_transition = /*#__PURE__*/ methodify(function(this: HTMLElement, fn: CustomTransitionFunction, params?: CssTransitionConfig) {
let transition_delay;
let pending = 0;
let prev;
const u = (new_fn = fn, new_params = params) => {
let test_config;
if (typeof (test_config = (fn = new_fn)(this,(params = new_params))) === "function") test_config = test_config();
transition_delay = test_config.delay || 0.0;
}
u();
let { delay = 0, duration = 300, easing, tick, css, strategy = 'reverse' }: CssTransitionConfig = const run_transition = (is_intro:boolean, cancel_previous?) => {
'function' === typeof config ? (config = config()) : config; const delayed_start = transition_delay && cancel_previous && pending;
const solver = 'reverse' === strategy ? reversed : mirrored; let config;
const runner = (fn) => solver(fn, rx, easing, elapsed_ratio, 1);
if (rx & tx.bidirectional) { let running = true;
if (-1 !== delay_left) delay = delay_left; let cancelled = false;
if (solver === reversed) duration -= elapsed_duration;
else if (solver === mirrored) delay -= elapsed_duration;
}
end_time = (start_time = current_frame_time + delay) + duration; let cancel_css;
let cancel_raf;
if (0 === (rx & tx.animation)) { let start_time = 0.0;
this.dispatchEvent(custom_event(`${rx & tx.intro ? 'in' : 'ou'}trostart`)); let end_time = 0.0;
} let ratio_left = 0.0;
if (css) cancel_css = animate_css(this, runner(css), duration, delay); const current_group = transition_group;
if (!is_intro) current_group.r++;
if (rx & tx.outro) { const run = (flush_frame_time) => {
pending++;
const [prev_duration_left, prev_ratio_left] = ((cancel_previous && cancel_previous(flush_frame_time)) || [0.0, 0.0] );
ratio_left = prev_ratio_left;
return () => {
config = fn(this, params);
return (current_frame_time) => {
let { tick, css, duration = 300.0, delay = 0.0, easing, strategy = EasingStrategy.balanced }: CssTransitionConfig =
"function" === typeof config ? (config = config()) : config;
const solver = EasingStrategy.balanced === strategy ? reversed : mirrored;
const runner = (fn) => solver(fn, is_intro, easing, ratio_left, 1);
if (delayed_start) delay = 0;
if (solver === reversed) duration -= prev_duration_left;
else if (solver === mirrored) delay -= prev_duration_left;
start_time = current_frame_time + delay;
end_time = start_time + duration;
if (cancelled) return;
if (!is_intro) {
current_group.t = Math.max(end_time, current_group.t);
if (current_group.s.push(stop) === current_group.r) { if (current_group.s.push(stop) === current_group.r) {
setFrameTimeout((t) => { setFrameTimeout((t) => {
for (let i = 0; i < current_group.s.length; i++) current_group.s[i](t); for (let i = 0; i < current_group.s.length; i++) {
}, Math.max(end_time, current_group.t)); current_group.s[i](t);
} else {
current_group.t = Math.max(end_time, current_group.t);
} }
if (tick) cancel_raf = setTweenTimeout(noop, end_time, runner(tick), duration, true); }, current_group.t);
} else {
cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration) : setFrameTimeout(stop, end_time);
} }
}
this.dispatchEvent(custom_event(is_intro ? TransitionEvent.introstart : TransitionEvent.outrostart));
if (css) cancel_css = animate_css(this, runner(css), duration, delay);
if (tick) cancel_raf = setTweenTimeout(is_intro ? stop : noop, end_time, runner(tick), duration);
else if (is_intro) cancel_raf = setFrameTimeout(stop, end_time);
}; };
}); };
}
const stop: StopResetReverseFn = (t?: number | 1 | -1) => { const cancel = (t) => {
if (t === 1 && rx & tx.outro && 0 === (rx & tx.bidirectional) && 'tick' in config) config.tick(1, 0); if (!cancelled) {
pending--;
cancelled = true;
if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf();
if (1 === t && cancel_previous) cancel_previous();
}
if (!config) return;
const duration_left = end_time - t;
const next_ratio_left = 1 - duration_left / (end_time - start_time);
return duration_left > 0 && next_ratio_left > 0 && [duration_left, (1 - ratio_left) * (1 - (config.easing || ((v) => v))(next_ratio_left))];
};
if (false === running) return; const stop: StopResetReverseFn = (t?: number | -1 | 1) => {
else running = false; if (running) {
running = false;
if (config) {
if (t >= end_time) {
if (!is_intro && "tick" in config) config.tick(0, 1);
if (pending === 1) cancel(t);
this.dispatchEvent(custom_event(is_intro ? TransitionEvent.introend : TransitionEvent.outroend));
}
if (!is_intro) {
if (!--current_group.r) {
for (let i = 0, { c } = current_group, r = t === 1; i < c.length; i++) c[i](r);
}
}
}
}
if (t === -1) return run_transition(!is_intro, cancel);
};
if (cancel_css) cancel_css(); if (delayed_start) {
if (cancel_raf) cancel_raf(rx & tx.outro && t >= end_time); setFrameTimeout((t) => {
add_measure_callback(run(t));
if (rx & tx.animation) return; tick();
}, frame.time + transition_delay);
if (t >= end_time) this.dispatchEvent(custom_event(`${rx & tx.intro ? 'in' : 'ou'}troend`)); } else {
add_measure_callback(run(frame.time));
if (rx & tx.outro && !--current_group.r) }
for (let i = 0; i < current_group.c.length; i++) current_group.c[i](t === void 0);
return stop
if (0 === (rx & tx.bidirectional)) return;
if (-1 === t)
return (
(t = now()) < end_time &&
run_transition(
this,
() => config,
rx ^ tx.reverse,
params,
end_time - t,
start_time > t ? start_time - t : 0,
(1 - elapsed_ratio) * (1 - (config.easing || ((v) => v))(1 - (end_time - t) / (end_time - start_time)))
)
);
else running_bidi.delete(this);
}; };
return stop; return {
u,
o() {
prev = prev ? prev(-1) : run_transition(false);
},
i() {
prev = prev ? prev(-1) : run_transition(true);
},
d() {
prev = prev(1);
}
}
}); });
const running_bidi: Map<HTMLElement, StopResetReverseFn> = new Map();
export const run_bidirectional_transition = /*#__PURE__*/ methodify(
function bidirectional(this: HTMLElement, fn: TransitionFn, rx: tx.intro | tx.outro, params: any ) {
let cancel;
running_bidi.set(
this,
(cancel =
(running_bidi.has(this) && running_bidi.get(this)(-1)) || run_transition(this, fn, rx | tx.bidirectional, params))
);
return cancel;
}
);
export const run_duration = (duration, value1, value2?): number => export const run_duration = (duration, value1, value2?): number =>
typeof duration === 'function' ? duration(value1, value2) : duration; typeof duration === "function" ? duration(value1, value2) : duration;

@ -146,8 +146,8 @@ export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj,
export function action_destroyer(action_result) { export function action_destroyer(action_result) {
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
} }
type Methodify<T extends (...args: any) => any> = (thisType : ThisParameterType<T>, ...parameters :Parameters<T>) => T extends (...args: Parameters<T>) => infer R ? R : any
export const methodify = /*#__PURE__*/ (function() { export const methodify: <T extends (...args: any) => any>(fn: T) => Methodify<T> = /*#__PURE__*/ (function () {
const call = Function.prototype.call; const call = Function.prototype.call;
return call.bind.bind(call); return call.bind.bind(call);
})(); })();

@ -1,25 +1,12 @@
import { cubicOut, cubicInOut } from 'svelte/easing'; import { cubicOut, cubicInOut } from 'svelte/easing';
import { run_duration } from 'svelte/internal'; import { run_duration, CssAnimationConfig, CssTransitionConfig, TimeableConfig } from 'svelte/internal';
interface CssAnimationConfig { type FlyParams = FadingConfig & { x: number; y: number; rotate: number };
delay?: number;
duration?: number;
easing?: (t: number) => number;
strategy?: 'reverse' | 'mirror';
}
export interface CssTransitionConfig extends CssAnimationConfig {
css?: (t: number, u?: number) => string;
tick?: (t: number, u?: number) => void;
}
type FlyParams = FadingConfig & { x: number; y: number };
type BlurParams = FadingConfig & { amount: number }; type BlurParams = FadingConfig & { amount: number };
type ScaleParams = FadingConfig & { start: number }; type ScaleParams = FadingConfig & { start: number };
type DrawParams = CssAnimationConfig & { speed: number }; type DrawParams = CssAnimationConfig & { speed: number };
type FadingConfig = CssAnimationConfig & { opacity: number }; type FadingConfig = CssAnimationConfig & { opacity: number };
type MarkedCrossFadeConfig = TimeableConfig & { key: any }; type MarkedCrossFadeConfig = TimeableConfig & { key: any };
export type TimeableConfig = Omit<CssAnimationConfig, 'duration'> & { duration?: number | ((len: number) => number) };
type CrossFadeConfig = TimeableConfig & { fallback(node: Element, params: TimeableConfig, intro: boolean): CssTransitionConfig }; type CrossFadeConfig = TimeableConfig & { fallback(node: Element, params: TimeableConfig, intro: boolean): CssTransitionConfig };
type ElementMap = Map<any, Element>; type ElementMap = Map<any, Element>;
@ -41,7 +28,7 @@ export function fade(node: Element, { delay = 0, duration = 400, easing }: CssAn
return { delay, duration, easing, css: (t) => `opacity: ${t * o};` }; return { delay, duration, easing, css: (t) => `opacity: ${t * o};` };
} }
export function fly(node: Element, { delay = 0, duration = 400, easing = cubicOut, x = 0, y = 0, opacity = 0 }: FlyParams ): CssTransitionConfig { export function fly(node: Element, { delay = 0, duration = 400, easing = cubicOut, x = 0, y = 0, opacity = 0, rotate = 0 }: FlyParams ): CssTransitionConfig {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const target_opacity = +style.opacity; const target_opacity = +style.opacity;
const prev = style.transform === 'none' ? '' : style.transform; const prev = style.transform === 'none' ? '' : style.transform;
@ -50,7 +37,7 @@ export function fly(node: Element, { delay = 0, duration = 400, easing = cubicOu
delay, delay,
duration, duration,
easing, easing,
css: (_t, u) => `transform: ${prev} translate(${u * x}px, ${u * y}px); opacity: ${target_opacity - od * u};`, css: (_t, u) => `transform: ${prev} translate(${u * x}px, ${u * y}px) rotate(${u * rotate}deg); opacity: ${target_opacity - od * u};`,
}; };
} }
@ -129,22 +116,23 @@ export function crossfade({ delay: default_delay = 0, duration: default_duration
} as CssTransitionConfig; } as CssTransitionConfig;
}; };
const transition = (a: ElementMap, b: ElementMap, is_intro: boolean) => ( node: Element, params: MarkedCrossFadeConfig ) => { const transition = (a: ElementMap, b: ElementMap, is_intro: boolean) => ( to_node: Element, params: MarkedCrossFadeConfig ) => {
const { key } = params; const { key } = params;
a.set(key, node); a.set(key, to_node);
if (b.has(key)) { if (b.has(key)) {
const from_node = b.get(key); const from_node = b.get(key);
b.delete(key); b.delete(key);
return crossfade(from_node, node, params); return crossfade(from_node, to_node, params);
} else { } else {
return () => { return () => {
if (b.has(key)) { if (b.has(key)) {
const from_node = b.get(key); const from_node = b.get(key);
b.delete(key); b.delete(key);
return crossfade(from_node, node, params); return crossfade(from_node, to_node, params);
} else { } else {
debugger
a.delete(key); a.delete(key);
return fallback && fallback(node, params, is_intro); return fallback && fallback(to_node, params, is_intro);
} }
}; };
} }

Loading…
Cancel
Save