Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transition component does not work with “visibility” #2531

Open
yaquawa opened this issue Oct 30, 2020 · 7 comments · Fixed by #2597
Open

Transition component does not work with “visibility” #2531

yaquawa opened this issue Oct 30, 2020 · 7 comments · Fixed by #2597

Comments

@yaquawa
Copy link

yaquawa commented Oct 30, 2020

Version

3.0.2

Reproduction link

https://codesandbox.io/s/competent-hermann-b1s5q

Steps to reproduce

see https://codesandbox.io/s/competent-hermann-b1s5q

What is expected?

The transition element of vue only works with display:none but not visibility:hidden.
Is there any way to make it work with visibility?
I want to get the clientWidth of the element before it shows up, with display:none I can't get that value.

@yaquawa yaquawa changed the title Transition work with “visibility” Transition does not work with “visibility” Oct 30, 2020
@yaquawa yaquawa changed the title Transition does not work with “visibility” Transition component does not work with “visibility” Oct 30, 2020
@yaquawa
Copy link
Author

yaquawa commented Oct 31, 2020

It's very easy to add a modifier like v-show.visibility, if it's OK to have such a modifier, I would like to send a PR.

@skirtles-code
Copy link
Contributor

For the benefit of anyone else reading this, there is a corresponding Stack Overflow question here:

https://stackoverflow.com/questions/64615373/how-to-make-transition-work-with-visibility-but-not-display

I believe this is really a feature request rather than a bug report. Currently it is possible to hide an element in a transition-safe way using v-show. However, that forces the hiding to be via display: none. In cases where you want to hide an element by other means, for example visibility, you currently need to recreate the logic in v-show. It would be nice to have a way to reuse the transition logic in v-show without having to copy/paste/maintain it in a totally separate directive.

@yaquawa
Copy link
Author

yaquawa commented Oct 31, 2020

Thanks @skirtles-code ! Really helpful solution on the Stack Overflow.

It would be nice to have a way to reuse the transition logic in v-show without having to copy/paste/maintain it in a totally separate directive.

Yeah, absolutely right. Or may be it could be a strategy function something like this.

<template>
    <div v-show:strategy="setDisplay">foobar</div>
</template>

<script lang="ts">
  interface ElementDisplay {
    new(el: HTMLElement): void
    show()
    hide()
  }

  class VisibilityStrategy implements ElementDisplay {
    private el: HTMLElement;

    constructor(el: HTMLElement) {
      this.el = el
    }

    show() {
      this.el.style.visibility = 'visible'
      this.el.style.opacity = '1'
      this.el.classList.remove('is-hidden')
    }

    hide() {
      this.el.style.visibility = 'hidden'
      this.el.style.opacity = '0'
      this.el.classList.add('is-hidden')
    }
  }


  export default {
    data() {
      return {
        setDisplay: VisibilityStrategy
      }
    }
  }
</script>

then this function can be replaced by the ElementDisplay instance.

But a simple v-show.visibility modifier is enough to handle most of the use cases I think.

@luwuer
Copy link
Contributor

luwuer commented Nov 11, 2020

I would like to implement it, make transition component can effect on visibility.

@luwuer
Copy link
Contributor

luwuer commented Nov 11, 2020

I find that this issue should be added a bug label @LinusBorg , here is my analysis:

Suppose the user-defined direcitve looks like following(similar to v-show), pay attention to the comments(step 1 & step 2) and now let's look at the classes change.

const visibility = {
  beforeMount(el, { value }, { transition }) {
    el._vod = el.style.visibility === '' ? 'hidden' : el.style.visibility
    if (transition && value) {
      transition.beforeEnter(el)
    } else {
      el.style.visibility = value || 'hidden'
    }
  },
  updated(el, { value, oldValue }, { transition }) {
    if (!value === !oldValue) {
      return
    }

    if (value) {
      // step 1
      transition.beforeEnter(el)
      el.style.visibility = 'visible'
      // step 2
      transition.enter(el)
    } else {
      transition.leave(el, () => {
        el.style.visibility = 'hidden'
      })
    }
  }
}

The following code is part of Transition.ts.

// step 1: transition.beforeEnter
onBeforeEnter(el) {
  onBeforeEnter && onBeforeEnter(el)
  // add *-enter-active
  addTransitionClass(el, enterActiveClass)
  // add *-enter-from
  addTransitionClass(el, enterFromClass)
}
// step 2: transition.enter
const makeEnterHook = (isAppear: boolean) => {
  return (el: Element, done: () => void) => {
    const hook = isAppear ? onAppear : onEnter
    const resolve = () => finishEnter(el, isAppear, done)
    hook && hook(el, resolve)
    nextFrame(() => {
      // remove *-enter-from
      removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass)
      // add *-enter-to
      addTransitionClass(el, isAppear ? appearToClass : enterToClass)
      if (!(hook && hook.length > 1)) {
        if (enterDuration) {
          setTimeout(resolve, enterDuration)
        } else {
          whenTransitionEnds(el, type, resolve)
        }
      }
    })
  }
}
// step 3: transition end
const finishEnter = (el: Element, isAppear: boolean, done?: () => void) => {
  // remove *-enter-to
  removeTransitionClass(el, isAppear ? appearToClass : enterToClass)
  // remove *-enter-active
  removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass)
  done && done()
}

We can see classes change processes:

  1. When we use v-show it's ok, because the DOM was inserted after add *-enter-active & *-enter-from, so that the DOM initial status is *-enter-from
    image
  2. When we use v-visibility , the initial status is certain (status a) and status was changed when we add *-enter-active & *-enter-from (status b), but now DOM initial status is not *-enter-from .
    image

Wait for confirmation and i can fix it.

@LinusBorg
Copy link
Member

Disclaimer: I currently don't have a complete understanding of the transition mechanics in play here.

That being said, I don't see why I should label this a bug when so far, we clearly only support v-if and v-show.

The fact that you were not able to implement a custom directive with the existing internal APIs does not make an unsupported feature a bug.

This still is, essentially, a feature request.

@luwuer
Copy link
Contributor

luwuer commented Nov 12, 2020

OK, i see your point and it makes sense.

@posva posva added the has PR A pull request has already been submitted to solve the issue label Nov 18, 2020
@posva posva removed the has PR A pull request has already been submitted to solve the issue label Nov 30, 2020
yyx990803 pushed a commit to luwuer/vue-next that referenced this issue Dec 2, 2020
yyx990803 pushed a commit to luwuer/vue-next that referenced this issue Dec 2, 2020
@yyx990803 yyx990803 reopened this Dec 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants