vueuse / motion

🤹 Vue Composables putting your components in motion
https://motion.vueuse.org
MIT License
2.35k stars 80 forks source link

Issue with "visible" / "visible-once" variant #143

Closed Rene-Roscher closed 8 months ago

Rene-Roscher commented 1 year ago

Description: When using the Vueuse/Motion library, I have encountered an issue with the "visible" / "visible-once" variant. Normally, the element should be immediately visible when it is within the viewport. However, the problem occurs where the element is only displayed when I scroll over the viewport.

Steps to reproduce:

Open the relevant page where the element with the "visible" / "visible-once" variant is used. Ensure that the viewport directly shows the element without the need for scrolling. Expected behavior: The element should be immediately visible when it is within the viewport.

Current behavior: The element is only displayed when scrolling over the viewport. It appears that the "visible" / "visible-once" variant is not functioning as expected.

Additional information:

Used version of Vueuse/Motion library: 2.0.0 Browser and version used: Brave [Version 1.52.130] (Chromium) Operating system: Windows 10

Notes: This issue affects the user experience as the element is not immediately visible when the viewport contains it. It would be desirable for the element to be correctly displayed according to the "visible" / "visible-once" variant, without the need for scrolling.

devloos commented 1 year ago

Hey @Rene-Roscher,

I have the same issue, I initially thought it was a user error but it seems like people are also experiencing this.

This issue dates back to September of 2021: https://github.com/vueuse/motion/issues/29

It is unfortunate that it hasn't yet been addressed. It is definitely killing this package for me.

naveeng2402 commented 1 year ago

@Tahul I found that this issue is found in chromium based browsers (tested in Google Chrome, Microsoft Edge) but not in Firefox.

ezzer87 commented 1 year ago

Yes I would love a fix for this as the rest of the package is so good!

GabrieleRizzo19 commented 1 year ago

I have the same problem, this bring me to not use this library.

mmorrii commented 1 year ago

I have the same problem, this bring me to not use this library.

can you tell me which library you are using instead of this one?

rylanharper commented 1 year ago

Does anyone have a temporary solution for this? This is very unfortunate lol.. 😭

fabienbergerat commented 1 year ago

Facing the same issue here. Works fine on Firefox and Safari, broken on all Chromium-based browsers. Happens when navigating back to any previously visited page. Makes the library virtually useless for me... would be great if anyone has a fix or alternative

rylanharper commented 1 year ago

Hey everyone! I've found a temporary solution for this.. Its not ideal, but it works. Let me first post how this should work if the v-motion module behaved as expected:

How its suppose to work

The following animation should work when the item(s) are in the initial viewport view. However, it does not. Here I have an animation function I simply pass into my project array (v-for loop) using v-motion.

Note this only works with enter, but not visible or visibleOnce

const animation = (i) => ({
  initial: {
    y: 30,
    opacity: 0
  },
  visibleOnce: {
    y: 0,
    opacity: 1,
    transition: {
      type: 'spring',
      delay: 50 * i
    }
  }
})

// template code
<div v-for="(project, i) in projects" :key="project._id" v-motion="animation(i)">

Temporary solution

After a lot messing around, I found that you can use the useMotion composable within an onMounted hook. There might be another way, but so far its the only thing that has worked for me. Now my animation triggers on the initial viewport view and is triggered if it leaves and then come back into the viewport again (if I am using visible)

const animation = (i) => ({
  initial: {
    y: 30,
    opacity: 0
  },
  visibleOnce: {
    y: 0,
    opacity: 1,
    transition: {
      type: 'spring',
      delay: 50 * i
    }
  }
})

onMounted(() => {
  const mediaItems = document.querySelectorAll('.media-item')

  mediaItems.forEach((mediaItem, i) => {
    useMotion(mediaItem, animation(i))
  })
})

// template code
<div v-for="(project, i) in projects" :key="project._id" class="media-item">

Maybe @Tahul you can take a look at this? This is really the only issue that is stopping this library from being perfect :)

Also, @fabienbergerat is correct in that it seems the first solution (how its suppose to work) works fine in Safari and Firefox, but not Chrome. Seems to be a Chrome issue for whatever reason...

rclambino commented 1 year ago

Hey everyone! I've found a temporary solution for this.. Its not ideal, but it works. Let me first post how this should work if the v-motion module behaved as expected:

How its suppose to work

The following animation should work when the item(s) are in the initial viewport view. However, it does not. Here I have an animation function I simply pass into my project array (v-for loop) using v-motion.

Note this only works with enter, but not visible or visibleOnce

const animation = (i) => ({
  initial: {
    y: 30,
    opacity: 0
  },
  visibleOnce: {
    y: 0,
    opacity: 1,
    transition: {
      type: 'spring',
      delay: 50 * i
    }
  }
})

// template code
<div v-for="(project, i) in projects" :key="project._id" v-motion="animation(i)">

Temporary solution

After a lot messing around, I found that you can use the useMotion composable within an onMounted hook. There might be another way, but so far its the only thing that has worked for me. Now my animation triggers on the initial viewport view and is triggered if it leaves and then come back into the viewport again (if I am using visible)

const animation = (i) => ({
  initial: {
    y: 30,
    opacity: 0
  },
  visibleOnce: {
    y: 0,
    opacity: 1,
    transition: {
      type: 'spring',
      delay: 50 * i
    }
  }
})

onMounted(() => {
  const mediaItems = document.querySelectorAll('.media-item')

  mediaItems.forEach((mediaItem, i) => {
    useMotion(mediaItem, animation(i))
  })
})

// template code
<div v-for="(project, i) in projects" :key="project._id" class="media-item">

Maybe @Tahul you can take a look at this? This is really the only issue that is stopping this library from being perfect :)

Also, @fabienbergerat is correct in that it seems the first solution (how its suppose to work) works fine in Safari and Firefox, but not Chrome. Seems to be a Chrome issue for whatever reason...

Thanks for this man! You saved my day

hareland commented 1 year ago

@Tahul Will there be a fix for this or should people just drop this package for others that are maintained?

I see this particular issue coming up again and again ever since 2021.

If you know where to point me to in order to fix this, i will gladly have a look myself but this issue is a dealbreaker.

Original issue: https://github.com/vueuse/motion/issues/29

Nemure231 commented 1 year ago

i can confirm this, got same problem here

wanxe commented 1 year ago

Yep i can confirm it too. Same problem.

YehorPytomets commented 1 year ago

Same issue.

HerrSammyDE commented 1 year ago

Same problem...

ericuldall commented 11 months ago

+1 also finding this problematic especially when trying to get animations that work equally well in all screen sizes.

ericuldall commented 11 months ago

const animation = (i) => ({ initial: { y: 30, opacity: 0 }, visibleOnce: { y: 0, opacity: 1, transition: { type: 'spring', delay: 50 * i } } })

Here's a slightly more vue-esque variation of your solution @rylanharper

    const statEls = [];
    const setEl = (el) => {
        statEls.push(el);
    }
    const animate = (el, i) => useMotion(el, {
        initial: {
            y: 30,
            opacity: 0
        },
        visibleOnce: {
            y: 0,
            opacity: 1,
            transition: {
                type: 'spring',
                delay: 100 * i
            }
        }
    })
    onMounted(() => {
        statEls.forEach(animate);
    });

// template code
<div v-for="(project, i) in projects" :key="project._id" :ref="setEl">

Thanks for the fix!

thomasjonas commented 11 months ago

Another option would be to use the onVnodeMounted hook. Maybe not the best idea as that hook is undocumented, but useful if you want to attach the animation to items that are added to the list after the parent component has been mounted (as the onMounted hook is only called once).

const animation = (i) => ({
  initial: {
    y: 30,
    opacity: 0
  },
  visibleOnce: {
    y: 0,
    opacity: 1,
    transition: {
      type: 'spring',
      delay: 50 * i
    }
  }
})

// template code
<div v-for="(project, i) in projects" :key="project._id" class="media-item" @vnodeMounted="(node) => useMotion(node.el, animation(i))" >
nikuscs commented 10 months ago

Yeah, came from framer motion, this works ok at first page load, but if you push router and come back, it doesnt work in chrome. Any more elegant solutions? Because at this point if we need to use a non-declaritve way, i rather use gsap all together :p Im not sure where the issue is either, but let us know if PRs are accepted to fix this?

BobbieGoede commented 8 months ago

This should be resolved by #171, I have tested it locally in some small test projects and it seems to work as expected. Can someone else check to see if it works in existing projects? Curious to hear if it's a complete fix 😅.