GithubHelp home page GithubHelp logo

Comments (19)

ryansolid avatar ryansolid commented on May 5, 2024 8

I've made a handful of demos playing around with different stuff but all outside of the core library. Grabbing from various demos I found around the web:

DOM Lifecycles
createFlipState
createAnimation

Since these aren't core I'm not sure where they belong but they are at least in this thread for reference.

There is a lot you can do with the few Hooks Solid has. It's going to take a lot more effort to get the more complicated scenarios though,

from solid.

ryansolid avatar ryansolid commented on May 5, 2024 5

Ok I believe I've done the Scoreboard now.

https://codesandbox.io/s/solid-scoreboard-sjpje

I still not sure how to show off lifecycles since I don't really have any. If anything the Scoreboard example has all the lifecycle Solid has. I was trying to think of a scenario with nested cleanup but there is nothing fancy about it. I think what I really need to do is write a basic tutorial on the fundamentals of fine grained reactive programming. It takes a little bit to think this way, which I take for granted since after nearly a decade it is the way I think about solving any problem.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024 1

Ok moved into a repo: https://github.com/ryansolid/solid-transition-group

from solid.

ryansolid avatar ryansolid commented on May 5, 2024 1

I think this issue has run its course. We've demonstrated all sorts of animations with Solid including exit transitions. Examples have been added to examples page.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

That's good idea. My CSS has always been a bit weaker on my demos so far so I can look at polishing that up a bit. I've also been considering adding transitions via the compiler like Svelte but I'm not there just yet. Something like the Scoreboard makes sense and I can look at doing something like that. Control flows have afterRender hooks which is how I used to solve this in Knockout.

Lifecycle functions aren't so explicit. They are more like React Hooks (thank god for them otherwise I'd be arguing with people for hours about their necessity). There is no lifecycle around rendering specifically just 2 hooks at your disposal:

onCleanup - runs before next invocation to cleanup, or when parent context is destroyed
afterEffects - runs after synchronous graph updates (current implementation is a bit simple deferring to next microtask)

These can be used inside any other effect/memo to schedule work to be done. To achieve the same similar to React useEffect and useLayoutEffect. So everything is very fine grained there is no absolute did_____. I need think of an example that does better at showing this off.

from solid.

leeoniya avatar leeoniya commented on May 5, 2024

Ok I believe I've done the Scoreboard now.

nice. though i think lifecycle-hooks is actually the more interesting case here since it has to retain dom elements that have been removed from state to ensure the outro transitions complete. also discerning a reorder/reinsert from a delete/recreate in new position.

while my demo does not show this, the actions following the removal do not rely on the outro finishing, it's just timed that way so as not to be too seizure-inducing. you can add transition: 3500ms; to .view1 .removed {} to observe that it still works with variable timings.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

Ok yeah that is interesting. I currently have nothing of that nature. I'd even be interested to see such an example in React since I don't think they have anything like that either. What you'd do with React is where I'd first start. Although I know Svelte also handles those sort of cases pretty well.

Looking at domvm in the example you handle that on a per node basis which is interesting. Native DOM nodes having lifecycle hooks could be powerful. This actually came up in #36. At a minimum connected, disconnected, reconnected seems viable, although I suppose willDisconnect is the more interesting case. The first 3 could be handled in a generic way independent of the Solid and work with any library. But knowing the intent to remove could only be handled in Solid's Control flow (conditional/list reconcilers). Ironically this would have been a lot easier when I did the updates in 2 passes but since I optimized to a single pass this actually is can't simply be a single hook to compare node lists before and after and asynchronously resolve completion. Mind you that optimization actually was almost unnoticeable from execution time perspective so perhaps if this is important enough I could go that way.

Honestly I'm not sure. I know some Virtual DOM libraries have done a lot of research in this area, and it's definitely an area that I think fine grained computations could do some really powerful stuff. I'm just unsure where it fits in the priorities. For now I think to be pragmatic, I'm going on the side of what can React do. If anyone wants to look into this sort of thing I'm all ears.

from solid.

leeoniya avatar leeoniya commented on May 5, 2024

Ironically this would have been a lot easier when I did the updates in 2 passes but since I optimized to a single pass this actually is can't simply be a single hook to compare node lists before and after and asynchronously resolve completion.

in domvm this isn't actually done in 2 passes either. the idea is that physical dom removal can be deferred/queued by any willRemove or willUnmount hook that returns a Promise. any followup redraws can see if an encountered dom node is in the removal queue and skip over it.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

@leeoniya Thanks for that. As you can probably tell from my thinking out loud I haven't really thought about it a ton. And that comment alone probably saved me some time digging. A simple check pre-removal and a flag on the indicating DOM nodes would work. The trickiest part would actually be identifying the situation where shortcut disposal is involved, since any parent.textContent ="" would have to switch the the less optimized method. You'd have to know down the whole tree before making that decision. It could come down to owning context. Any descendant with a registered transition handler would signify non-optimized execution path. I think Svelte handles things similarly.

from solid.

leeoniya avatar leeoniya commented on May 5, 2024

Any descendant with a registered transition handler would signify non-optimized execution path.

yep, domvm bubbles this info up in the initial vtree normalization pass [1]

[1] https://github.com/domvm/domvm/blob/master/src/view/preProc.js#L35-L37

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

Ok if you will, one more question. How do you handle nested contexts? Like if you had a conditional in a list. And the transitioning element is in that conditional. Parent removes the whole list/panel etc, do you delay doing so until the one item fades out? If there are nested removal effects do they cascade upwards? Consecutive/parallel? I honestly have never used a library that had something like this so I'm unclear on expected behavior.

from solid.

leeoniya avatar leeoniya commented on May 5, 2024

yes. since the presence of will* removal hooks taints all ancestors, they will await resolution of any descendant removal promises. admittedly, the bookkeeping is imperfect because it's expensive, so the de-optimized removal flags may remain on ancestors if a leaf component is explicitly redrawn in isolation.

notably, did* removal hooks will not de-optimize since they have no way of preventing removal, so usually it's in those that user-defined cleanup/teardown should be done.

honestly, i don't do too much transition/animation-heavy UIs in concert with dom removals, so there's sure to be more than a few gaps. e.g. i don't collect all promises from descendants for Promise.all() [1], and the ancestors are tainted with a boolean rather than a counter which should get decremented as the offending descendants are removed. i'm not terribly motivated to spend time on it though :)

[1] https://github.com/domvm/domvm/blob/master/src/view/dom.js#L30

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

Thank you. That gives me a better idea of the complexity. Svelte has transition feature like this and constantly has issues reported on it. I'm unsure how much to invest but this has been infinitely helpful.

from solid.

popaprozac avatar popaprozac commented on May 5, 2024

Just started playing with Solid and I had the same desire for mount/unmount animations.
I took inspiration from Framer Motion and ultimately just followed this quite closely. I may be naive but is this suitable? (Using anime.js as my pref but inline animation from link above works too)

import { render } from "solid-js/dom";
import { createSignal } from "solid-js";
import Fade from "./Fade";

export default () => {
  const [ show, setShow ] = createSignal(false);

  return (
    <div>
      <button onClick={() => setShow(!show())}>
        {show() ? "hide" : "show"}
      </button>
      <Fade show={show}>
        <div> HELLO </div>
      </Fade>
    </div>
  );
};

render(() => (
  <App />
), document.body);

Fade.js

import { createEffect, createSignal } from "solid-js";
import anime from "animejs";

export default function Fade({ show, children }) {
  const state = show();
  const [ shouldRender, setRender ] = createSignal(state);

  createEffect(() => {
    if(show()) {
      setRender(true);
      anime({
        targets: children,
        easing: "linear",
        opacity: [0, 1]
      });
    } else {
      anime({
        targets: children,
        easing: "linear",
        opacity: [1, 0],
        complete: () => {
          setRender(false);
        }
      });
    }
  }, [show()]);

  return (
    <Show when={shouldRender()}>
      {show() && 
        children
      }
    </Show>
  );
}

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

This as is won't quite work with Solid due to syntax differences and that the data is reactive, but an approach like this should work. Reading the article I'd probably keep things simple for the fade component using signals. And outside we'd use whatever makes sense. But in this case being a toy demo it would be a signal. Admittedly that is the one thing always awkward about demos. In real applications I use state way more. But for simple piping signals are great.

But it would look like:

index.js

import { createSignal } from "solid-js";
import { render } from "solid-js/dom";

import Fade from "./fade";

const App = () => {
  const [show, setShow] = createSignal(false);

  return (
    <>
      <button onClick={() => setShow(!show())}>
        {show() ? "hide" : "show"}
      </button>
      <Fade show={show()}>
        <div> HELLO </div>
      </Fade>
    </>
  );
};

render(App, document.body);

fade.js

import { createSignal, createEffect } from "solid-js";

const Fade = props => {
  const [shouldRender, setRender] = createSignal(props.show),
    onAnimationEnd = () => !props.show && setRender(false);

  createEffect(() => props.show && setRender(true));

  return (
    <Show when={shouldRender()}>
      <div
        style={{ animation: `${props.show ? "fadeIn" : "fadeOut"} 1s` }}
        onAnimationEnd={onAnimationEnd}
      >
        {props.children}
      </div>
    </Show>
  );
};

export default Fade;

But yeah generally you'd approach these problems the same way you do with React Hooks. Replacing with your anime library could work. Just unclear what it wants with targets. Since children are lazy evaluated I think there are some options here. But it's going to basically come down to hoisting the children. If they are static this is easy as you can just evaluate it outside of the JSX. But otherwise we might need to wrap them in a createMemo so that they are cached unless changed from the outside.

from solid.

popaprozac avatar popaprozac commented on May 5, 2024

@ryansolid I just changed my example to use signals and started to also clean it up a bit to be more terse. Seems to be working well.

I'll play around with children opts. Thanks for the quick reply.

from solid.

whatisaphone avatar whatisaphone commented on May 5, 2024

I think a valuable example would look something like this:

  • Here's a list of items
  • Now, here's how you add animations when an item is added/deleted

React's story here (with react-transition-group) is close to ideal. You're basically looking at a diff like this:

   return (
     <ul>
+      <ReactTransitionGroup>
       {items.map((item) => (
+        <CSSTransition>
           <li>{item.text}</li>
+        </CSSTransition>
       )}
+      </ReactTransitionGroup>
     </ul>
   );

Surely this is made easier by the fact that React has a virtual dom. I haven't thought about how a Solid approach might look.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

We use list reconciliation similar to a virtual dom so the same pattern interface would work more or less. It just works basically opposite direction since changes notify up rather than re-render down. We wrap each item in an object that contains extra state data about animation state, and then feed the reconciled nodes into a parent that keeps track of all these nodes and merges them into what gets attached to the DOM.

That being said it would be nice to have the means to do this less verbose ala Svelte. The challenge is that Solid's components aren't real nodes from our perspective. So not clear how to get the same level of control using directives but only on intrinsic elements. Not even sure if it would be the best pattern, but I suspect on inspection something similar should be possible.

from solid.

ryansolid avatar ryansolid commented on May 5, 2024

@whatisaphone Still WIP progress but I've mimic'd Vue's Transition/TransitionGroup here. As you can see something like this should be possible. There are some limitations in this implementation like children must be single DOM node. I still have jitteryness on transition group but I think it demonstrates the potential.

https://codesandbox.io/s/basic-css-transition-36rln?file=/styles.css

from solid.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.