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) {
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 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 outro_block = b`${name} = @run_bidirectional_transition(${this.var}, ${fn}, 2, ${snippet});`;
let intro_block = b`${transition}.i();`;
let outro_block = b`${transition}.o();`;
if (intro.is_local) {
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.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) {
if (outro) {
@ -779,7 +780,7 @@ export default class ElementWrapper extends Wrapper {
const [intro_var, node, transitionFn, params] = run_transition(this, block, intro, `intro`);
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 (intro.is_local) start_intro = b`if (#local) { ${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`);
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} }`;
block.chunks.outro.push(start_outro);
@ -824,7 +825,7 @@ export default class ElementWrapper extends Wrapper {
`);
block.chunks.animate.push(b`
if (${unfreeze_var}) return
if (${unfreeze_var} || !${rect_var}) return
else {
${stop_animation_var}();
${stop_animation_var} = @run_animation(${this.var}, ${rect_var}, ${name_var}, ${params_var});

@ -1,6 +1,5 @@
import { cubicOut } from "svelte/easing";
import { run_duration } from "svelte/internal";
import { CssTransitionConfig, TimeableConfig } from "svelte/transition";
import { run_duration, TimeableConfig, CssTransitionConfig } from "svelte/internal";
export function flip(
node: Element,
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
? () => window.performance.now()
: () => Date.now();
export let raf = is_client ? cb => 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;
});
});
export let raf = /*#__PURE__*/ is_client ? (cb: FrameRequestCallback) => requestAnimationFrame(cb) : noop;
// used internally for testing
export function set_now(fn) {

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

@ -1,5 +1,31 @@
import { now, raf, framerate } from './environment';
import { now, raf } from './environment';
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 TaskCanceller = () => void;
@ -13,7 +39,7 @@ let next_frame: TaskCallback[] = [];
const run = (t: number) => {
[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)) {
next_frame[n++] = v;
}
@ -33,22 +59,26 @@ let running_timed = false;
const run_timed = (now: number) => {
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) {
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);
else {
for (j = last_index; ~j && this_task.timestamp > (that_task = timed_tasks[j]).timestamp; j--)
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);
} 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] = this_task;
last_index++;
}
}
pending_insert_timed.length = 0;
pending_inserts = false;
}
return (running_timed = !!(timed_tasks.length = last_index + 1));
};
const unsafe_loop = (fn) => {
if (0 === n) raf(run);
next_frame[n++] = fn;
@ -56,8 +86,7 @@ const unsafe_loop = (fn) => {
export const loop = (fn) => {
let running = true;
if (0 === n) raf(run);
next_frame[n++] = (t) => !running || fn(t);
unsafe_loop((t) => running && fn(t));
return () => void (running = false);
};
@ -70,52 +99,23 @@ export const setFrameTimeout = (callback: (t: number) => void, timestamp: number
running_timed = true;
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 = (
stop: (now: number) => void,
end_time: number,
run: (now: number) => void,
duration = end_time - now(),
is_outro = false
duration = end_time - frame.sync()
): TaskCanceller => {
let running = true;
let t = 1 - (end_time - now()) / duration || 0;
if (!is_outro && t <= 1.0) run(t >= 0.0 ? t : 0);
unsafe_loop((now) => {
if (!running) return false;
let t = 0.0;
return loop((now) => {
t = 1 - (end_time - now) / duration;
if (t >= 1.0) return run(1), stop(now), false;
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 */

@ -1,6 +1,6 @@
import { set_current_component } from './lifecycle';
import { now } from './environment';
import { T$$ } from './Component';
import { frame } from './loop';
const resolved_promise = Promise.resolve();
@ -17,7 +17,9 @@ const flush_callbacks = [];
// todo : remove add_flush_callback
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();
export const add_render_callback = (fn) => {
@ -48,9 +50,10 @@ export const flush = () => {
if (is_flushing) return;
else is_flushing = true;
frame.sync();
let i = 0;
let j = 0;
let t = 0;
let $$: T$$;
let dirty;
let before_update;
@ -103,7 +106,7 @@ export const flush = () => {
// apply styles
// 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;
is_flushing = false;

@ -1,5 +1,5 @@
import { framerate } from './environment';
import { methodify } from './utils';
import { frame } from './loop';
import { methodify, noop } from './utils';
let documents_uid = 0;
let running_animations = 0;
@ -9,7 +9,7 @@ const document_stylesheets = new Map();
const current_rules = new Set();
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)) {
document_uid.set(this.ownerDocument, documents_uid++);
document_stylesheets.set(
@ -18,7 +18,7 @@ export const animate_css = /*#__PURE__*/ methodify(
);
}
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}`;
// darkskyapp/string-hash
@ -34,6 +34,7 @@ export const animate_css = /*#__PURE__*/ methodify(
}
const previous = this.style.animation;
if (previous) {console.error("stacked animations"); return noop}
this.style.animation = `${
previous ? `${previous}, ` : ''
}${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 { 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';
import { methodify, noop } from './utils';
type TransitionFn = (node: HTMLElement, params: any) => CssTransitionConfig;
export type StopResetReverseFn = (t?: number | -1) => StopResetReverseFn | void;
import { Fragment } from "./Component";
import { custom_event } from "./dom";
import { setFrameTimeout, setTweenTimeout, frame } from "./loop";
import { add_measure_callback, tick } from "./scheduler";
import { animate_css } from "./style_manager";
import { methodify, noop } from "./utils";
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?) => {
// todo : is `!block` necessary ?
if (!block || !block.i) return;
outroing.delete(block);
block.i(local);
};
export const transition_out = (block: Fragment, local?) => {
// todo : are `!block` and `outroing.has` checks necessary ?
if (!block || !block.o || outroing.has(block)) return;
outroing.add(block);
block.o(local);
};
type TransitionGroup = {
/* parent group */ p: TransitionGroup;
/* callbacks */ c: Array<((cancelled: boolean) => void)>;
/* callbacks */ c: Array<(cancelled: boolean) => void>;
/* running outros */ r: number;
/* stop callbacks */ s: Array<((t: number) => void)>;
/* stop callbacks */ s: Array<(t: number) => void>;
/* outro timeout */ t: number;
};
let transition_group: TransitionGroup;
const outroing = new Set();
export const group_transition_out = (fn) => {
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) => {
if (!block || !block.o || outroing.has(block)) return;
outroing.add(block);
c.push((cancelled = false) => {
if (cancelled) {
// block was destroyed before outro ended
// block destroyed before outro ended
outroing.delete(block);
} else if (outroing.has(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]();
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
? rx & tx.intro
? is_intro
? fn
: (t) => fn(1 - t)
: rx & tx.intro
: is_intro
? (t) => fn(t, 1 - t)
: (t) => fn(1 - t, t);
const mirrored = (fn, rx, easing, _start, _end) => {
const run = swap(fn, rx);
const mirrored = (fn, is_intro, easing, _start?, _end?) => {
const run = swap(fn, is_intro);
return easing
? rx & tx.intro
? is_intro
? (t) => run(easing(t))
: (t) => run(1 - easing(1 - t))
: 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;
return easing
? (t) => run(start + difference * easing(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 cancel_css;
let cancel_raf;
let start_time = 0;
let end_time = 0;
const current_group = transition_group;
if (rx & tx.outro) current_group.r++;
let cancel_raf;
add_measure_callback(() => {
if (null === (config = fn(this, params))) return noop;
const to = this.getBoundingClientRect();
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;
current_group.r++;
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, 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;
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;
let { delay = 0, duration = 300, easing, tick, css, strategy = 'reverse' }: CssTransitionConfig =
'function' === typeof config ? (config = config()) : config;
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();
const solver = 'reverse' === strategy ? reversed : mirrored;
const runner = (fn) => solver(fn, rx, easing, elapsed_ratio, 1);
const run_transition = (is_intro:boolean, cancel_previous?) => {
const delayed_start = transition_delay && cancel_previous && pending;
if (rx & tx.bidirectional) {
if (-1 !== delay_left) delay = delay_left;
if (solver === reversed) duration -= elapsed_duration;
else if (solver === mirrored) delay -= elapsed_duration;
}
let config;
end_time = (start_time = current_frame_time + delay) + duration;
let running = true;
let cancelled = false;
if (0 === (rx & tx.animation)) {
this.dispatchEvent(custom_event(`${rx & tx.intro ? 'in' : 'ou'}trostart`));
}
let cancel_css;
let cancel_raf;
if (css) cancel_css = animate_css(this, runner(css), duration, delay);
let start_time = 0.0;
let end_time = 0.0;
let ratio_left = 0.0;
if (rx & tx.outro) {
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);
}, Math.max(end_time, current_group.t));
} else {
current_group.t = Math.max(end_time, current_group.t);
}
if (tick) cancel_raf = setTweenTimeout(noop, end_time, runner(tick), duration, true);
} else {
cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration) : setFrameTimeout(stop, end_time);
const current_group = transition_group;
if (!is_intro) current_group.r++;
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) {
setFrameTimeout((t) => {
for (let i = 0; i < current_group.s.length; i++) {
current_group.s[i](t);
}
}, current_group.t);
}
}
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 cancel = (t) => {
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))];
};
});
const stop: StopResetReverseFn = (t?: number | 1 | -1) => {
if (t === 1 && rx & tx.outro && 0 === (rx & tx.bidirectional) && 'tick' in config) config.tick(1, 0);
const stop: StopResetReverseFn = (t?: number | -1 | 1) => {
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 (false === running) return;
else running = false;
if (delayed_start) {
setFrameTimeout((t) => {
add_measure_callback(run(t));
tick();
}, frame.time + transition_delay);
} else {
add_measure_callback(run(frame.time));
}
if (cancel_css) cancel_css();
if (cancel_raf) cancel_raf(rx & tx.outro && t >= end_time);
if (rx & tx.animation) return;
if (t >= end_time) this.dispatchEvent(custom_event(`${rx & tx.intro ? 'in' : 'ou'}troend`));
if (rx & tx.outro && !--current_group.r)
for (let i = 0; i < current_group.c.length; i++) current_group.c[i](t === void 0);
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 stop;
});
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;
return {
u,
o() {
prev = prev ? prev(-1) : run_transition(false);
},
i() {
prev = prev ? prev(-1) : run_transition(true);
},
d() {
prev = prev(1);
}
}
);
});
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) {
return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
}
export const methodify = /*#__PURE__*/ (function() {
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: <T extends (...args: any) => any>(fn: T) => Methodify<T> = /*#__PURE__*/ (function () {
const call = Function.prototype.call;
return call.bind.bind(call);
})();

@ -1,25 +1,12 @@
import { cubicOut, cubicInOut } from 'svelte/easing';
import { run_duration } from 'svelte/internal';
import { run_duration, CssAnimationConfig, CssTransitionConfig, TimeableConfig } from 'svelte/internal';
interface CssAnimationConfig {
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 FlyParams = FadingConfig & { x: number; y: number; rotate: number };
type BlurParams = FadingConfig & { amount: number };
type ScaleParams = FadingConfig & { start: number };
type DrawParams = CssAnimationConfig & { speed: number };
type FadingConfig = CssAnimationConfig & { opacity: number };
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 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};` };
}
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 target_opacity = +style.opacity;
const prev = style.transform === 'none' ? '' : style.transform;
@ -50,7 +37,7 @@ export function fly(node: Element, { delay = 0, duration = 400, easing = cubicOu
delay,
duration,
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;
};
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;
a.set(key, node);
a.set(key, to_node);
if (b.has(key)) {
const from_node = b.get(key);
b.delete(key);
return crossfade(from_node, node, params);
return crossfade(from_node, to_node, params);
} else {
return () => {
if (b.has(key)) {
const from_node = b.get(key);
b.delete(key);
return crossfade(from_node, node, params);
return crossfade(from_node, to_node, params);
} else {
debugger
a.delete(key);
return fallback && fallback(node, params, is_intro);
return fallback && fallback(to_node, params, is_intro);
}
};
}

Loading…
Cancel
Save