blockless
Rich Harris 2 years ago
parent 044c1a1975
commit 32202bdcb1

@ -58,7 +58,8 @@ import {
managed_effect,
derived,
pre_effect,
user_effect
user_effect,
pause_effect
} from './reactivity/computations.js';
import {
current_hydration_fragment,
@ -1651,115 +1652,28 @@ export function element(anchor_node, tag_fn, is_svg, render_fn) {
* @returns {void}
*/
export function component(anchor_node, component_fn, render_fn) {
const block = create_dynamic_component_block();
/** @type {null | import('./types.js').Render} */
let current_render = null;
hydrate_block_anchor(anchor_node);
/** @type {null | ((props: P) => void)} */
let component = null;
block.r =
/**
* @param {import('./types.js').Transition} transition
* @returns {void}
*/
(transition) => {
const render = /** @type {import('./types.js').Render} */ (current_render);
const transitions = render.s;
transitions.add(transition);
transition.f(() => {
transitions.delete(transition);
if (transitions.size === 0) {
// If the current render has changed since, then we can remove the old render
// effect as it's stale.
if (current_render !== render && render.e !== null) {
if (render.d !== null) {
remove(render.d);
render.d = null;
}
destroy_signal(render.e);
render.e = null;
}
}
/** @type {TODO} */
let Component = null;
/** @type {import('./types.js').ComputationSignal | null} */
let effect = null;
render_effect(() => {
if (Component === (Component = component_fn() ?? null)) return;
if (effect) {
const e = effect;
pause_effect(effect, () => {
if (e === effect) effect = null;
});
};
const create_render_effect = () => {
/** @type {import('./types.js').Render} */
const render = {
d: null,
e: null,
s: new Set(),
p: current_render
};
// Managed effect
const effect = render_effect(
() => {
const current = block.d;
if (current !== null) {
remove(current);
block.d = null;
}
if (component) {
render_fn(component);
}
render.d = block.d;
block.d = null;
},
block,
true
);
render.e = effect;
current_render = render;
};
const render = () => {
const render = current_render;
if (render === null) {
create_render_effect();
return;
}
const transitions = render.s;
if (transitions.size === 0) {
if (render.d !== null) {
remove(render.d);
render.d = null;
}
if (render.e) {
execute_effect(render.e);
} else {
create_render_effect();
}
} else {
create_render_effect();
trigger_transitions(transitions, 'out');
}
};
const component_effect = render_effect(
() => {
const next_component = component_fn();
if (component !== next_component) {
component = next_component;
render();
}
},
block,
false
);
push_destroy_fn(component_effect, () => {
let render = current_render;
while (render !== null) {
const dom = render.d;
if (dom !== null) {
remove(dom);
}
const effect = render.e;
if (effect !== null) {
destroy_signal(effect);
}
render = render.p;
if (Component) {
effect = render_effect(() => render_fn(Component), {}, true);
}
});
block.e = component_effect;
}
/**

@ -546,7 +546,7 @@ export function bind_transition(element, get_fn, get_params, direction, global)
for (let i = 0; i <= n; i += 1) {
const t = current_options.easing(p + (current_delta * i) / n);
const css = current_options.css(t);
const css = current_options.css(t, 1 - t);
keyframes.push(css_to_keyframe(css));
}
@ -578,6 +578,8 @@ export function bind_transition(element, get_fn, get_params, direction, global)
user_effect(() => {
untrack(() => transition.to(1));
});
} else {
p = 1;
}
}

@ -33,11 +33,9 @@ function tick(time) {
class Animation {
#keyframes;
#duration;
#timeline_offset;
#reversed;
#target;
#paused;
#finished;
#cancelled;
/**
* @param {HTMLElement} target
@ -48,47 +46,39 @@ class Animation {
this.#target = target;
this.#keyframes = keyframes;
this.#duration = options.duration || 0;
this.#timeline_offset = 0;
this.#reversed = false;
this.#paused = false;
this.onfinish = () => {};
this.pending = true;
this.currentTime = 0;
this.playState = 'running';
this.effect = {
setKeyframes: (/** @type {Keyframe[]} */ keyframes) => {
this.#keyframes = keyframes;
// Promise-like semantics, but call callbacks immediately on raf.tick
this.finished = {
then: (callback) => {
this.#finished = callback;
return {
catch: (callback) => {
this.#cancelled = callback;
}
};
}
};
this.finished = new Promise((fulfil) => {
this.#finished = fulfil;
});
}
play() {
this.#paused = false;
raf.animations.add(this);
this.playState = 'running';
this._update();
cancel() {
if (this.currentTime > 0 && this.currentTime < this.#duration) {
this._applyKeyFrame(0);
}
this.#cancelled();
}
_update() {
if (this.#reversed) {
if (this.#timeline_offset === 0) {
this.currentTime = this.#duration - raf.time;
} else {
this.currentTime = this.#timeline_offset + (this.#timeline_offset - raf.time);
}
} else {
this.currentTime = raf.time - this.#timeline_offset;
}
this.currentTime = raf.time;
const target_frame = this.currentTime / this.#duration;
this._applyKeyFrame(target_frame);
if (this.currentTime >= this.#duration) {
this.#finished();
raf.animations.delete(this);
}
}
@ -103,53 +93,13 @@ class Animation {
// @ts-ignore
this.#target.style[prop] = frame[prop];
}
if (this.#reversed) {
if (this.currentTime <= 0) {
this.finish();
for (let prop in frame) {
// @ts-ignore
this.#target.style[prop] = null;
}
}
} else {
if (this.currentTime >= this.#duration) {
this.finish();
for (let prop in frame) {
// @ts-ignore
this.#target.style[prop] = null;
}
}
}
}
finish() {
this.onfinish();
this.currentTime = this.#reversed ? 0 : this.#duration;
if (this.#reversed) {
raf.animations.delete(this);
}
this.playState = 'idle';
}
cancel() {
this.#paused = true;
if (this.currentTime > 0 && this.currentTime < this.#duration) {
this._applyKeyFrame(this.#reversed ? this.#keyframes.length - 1 : 0);
}
}
pause() {
this.#paused = true;
this.playState = 'paused';
}
reverse() {
if (this.#paused && !raf.animations.has(this)) {
raf.animations.add(this);
if (this.currentTime >= this.#duration) {
for (let prop in frame) {
// @ts-ignore
this.#target.style[prop] = null;
}
}
this.#timeline_offset = this.currentTime;
this.#reversed = !this.#reversed;
this.playState = 'running';
}
}
@ -160,6 +110,7 @@ class Animation {
*/
HTMLElement.prototype.animate = function (keyframes, options) {
const animation = new Animation(this, keyframes, options);
raf.animations.add(animation);
// @ts-ignore
return animation;
};

Loading…
Cancel
Save