maciekgrzybek / svelte-inview

A Svelte action that monitors an element enters or leaves the viewport.🔥
MIT License
749 stars 23 forks source link

proposal: onInit callback #13

Closed eikaramba closed 2 years ago

eikaramba commented 2 years ago

i would like to propose an additional method to setup initial values.

use case

the use case is for animation. consider that i want to animate some stuff into the view by changing opacity and translateY. I am currently using sal.js and first set the opacity to 0 so that i actually see the items fading in (not using css because then people with js disabled will be unhappy)

of course i can also do this with your library, but it would be nice to have this coupled to the dom element instead of just at the top of the onMount method. do you agree?

additional function

In addition one could maybe also think about returning false or true in that callback method and on false remove the observer again. the use case here would be to check for reducedmotion setting of the users browser and then return false so that neither the initial setting (opacity=0) nor the observer is set.

example

<script>
  import { inview } from 'svelte-inview';

  let isInView;
  const options = {
    rootMargin: '-50px',
    unobserveOnEnter: true,
  };
  const handleInit = ()=>{
    const isReduced = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
    if(isReduced) return false; //of course this could also be something for a global inview function, or even a reactive variable just passed to inview options
    else {
      //do something like setting opacity to 0 to init animation
    }
    return true;
  }
  const handleEnter = ({ detail }) => {
    //animate me
  };
</script>

<div use:inview={options} on:init={handleInit} on:enter={handleEnter}>

of course there is also a way to do this with CSS alone. i just picked it because of simplicity. but if my little example uses a fancy js animation library, then again it would make sense.

maciekgrzybek commented 2 years ago

Hey @eikaramba thanks for submitting this proposal :) I'll have a read and get back to you :) but from a first glance, looks like a doable, great idea :)

maciekgrzybek commented 2 years ago

@eikaramba I read your proposal and it sounds really good :) I'll add it ASAP :) unless you want to work on it :)

eikaramba commented 2 years ago

@maciekgrzybek haha you got me, i don't currently have time for it unforunately. first i even need to switch from sal.js to your library. but if you don't want to work on it now, i can surely have a look. will just take some time (= meaning weeks) :)

and thanks for considering it!

maciekgrzybek commented 2 years ago

No probs :) I'm just working at this. Let me know if that's something you had in mind:

const handleInit = ({ detail: { observer, node } }) => {
  // observer will have all the methods coming from InteresectionObserver type, so you can "unobserve" if you need
  // node will be the element, you can do whatever you want wiith this :)
}
<div use:inview={options} on:init={handleInit} >

I've considered your additional functionality proposal, but I decided that it would be "locking" the user to specific behavior. So as a compromise, you will get an observer as an argument, which will allow you to stop observing at the very beginning if needed. So taking your example:

  const handleInit = ({ detail })=>{
    const isReduced = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
    if(isReduced) detail.observer.unobserve()
    else {
      //do something like setting opacity to 0 to init animation
    }
  }

Let me know what you think :)

eikaramba commented 2 years ago

looks good! absolutely no problem with the different approach how to stop the observer on init. i guess this is better, because then maybe one could also do this ONCE globally if reduced motion for example is changed inbetween (not that this is something anyone would regularly do)

maciekgrzybek commented 2 years ago

Awesome :) I'll add a PR then :)

maciekgrzybek commented 2 years ago

Hey, @eikaramba this change is now implemented, check version 2.1.0 :) Thanks again for the idea :)