How to transition child when parent goes from display: none to block

前端 未结 4 2079
轻奢々
轻奢々 2021-02-13 15:55

I\'m having trouble creating a flyout menu with a specific effect. The flyout goes from display:none to block and then I use jquery to animate opacity from 0 to 1 (and vice vers

相关标签:
4条回答
  • 2021-02-13 16:17

    if you want only to change the opacity you can use FadeIn and FadeOut functions of JQuery but if you want more complex transition you can use CSS3 (this is a realy good library). See this DEMO where you can see this two different way.

    You can also add a controll to the class like this:

        $("OBJECT").click(function(){
        if ($("OBJECT").hasClass("CLASS")){
            $("OBJECT").removeClass("CLASS");
        } else {
            $("OBJECT").addClass("CLASS");
        }
    });
    

    to make two way functions.

    $(document).ready(function(){
    $("#fadeOut").click(function(){
        var duration = 500;
        $("#div").fadeOut(duration);
    });
    $("#css").click(function(){
        $("#div").addClass("out");
        setTimeout(
           function() {
              $("#div").css("display", "none");
           },
        2001);
    });
    });
    #div {
        width:200px;
        height:200px;
        background-color:red;
        text-align:center;
        vertical-align:middle;
        /* Animation CSS */
        -webkit-animation-duration: 2s;
        animation-duration: 2s;
        -webkit-animation-fill-mode: both;
        animation-fill-mode: both;
    }
    /* Setup CSS3 animations */
    @-webkit-keyframes out {
      0% {
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
      }
    
      20%, 60% {
        -webkit-transform: rotate3d(0, 0, 1, 80deg);
        transform: rotate3d(0, 0, 1, 80deg);
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
      }
    
      40%, 80% {
        -webkit-transform: rotate3d(0, 0, 1, 60deg);
        transform: rotate3d(0, 0, 1, 60deg);
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
        opacity: 1;
      }
    
      to {
        -webkit-transform: translate3d(0, 700px, 0);
        transform: translate3d(0, 700px, 0);
        opacity: 0;
      }
    }
    
    @keyframes out {
      0% {
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
      }
    
      20%, 60% {
        -webkit-transform: rotate3d(0, 0, 1, 80deg);
        transform: rotate3d(0, 0, 1, 80deg);
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
      }
    
      40%, 80% {
        -webkit-transform: rotate3d(0, 0, 1, 60deg);
        transform: rotate3d(0, 0, 1, 60deg);
        -webkit-transform-origin: top left;
        transform-origin: top left;
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
        opacity: 1;
      }
    
      to {
        -webkit-transform: translate3d(0, 700px, 0);
        transform: translate3d(0, 700px, 0);
        opacity: 0;
      }
    }
    .out {
        -webkit-animation-name: out;
        animation-name: out;
    }
    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
        <script type="text/javascript" src="script.js"></script>
    </head>
    <body>
    <div id="div"></div>
    <button id="fadeOut">fadeOut</button>
    
    <button id="css">CSS3</button>
    </body>
    </html>

    0 讨论(0)
  • 2021-02-13 16:21

    Is there a way around this? I knew that CSS animation alongside a display change on the same element did not work, but finding out that any child animations also do not work is a little frustrating.

    It does not only apply to the same element, but the entire sub-tree - as the entire sub-tree is not rendered.

    • you can set display: block on the wrapper, then force a reflow (by flushing the style buffer with wrapperElement.offsetHeight;), then add a class that sets opacity:1 to your children (or do whatever you're doing to kick off the animations).
    • you can use a different method of visually hiding your wrapper, eg width: 0; height: 0; overflow: hidden; visibility: hidden; (or, for nicer transitions transform: scale(0); visibility: hidden; pointer-events: none;)

    As soon as display:none is involved, you're screwed when it comes to transitions. The best way is to avoid it. I've been using the second option without any significant problems for quite a while now.


    edit after OP added some demo code:

    • the .animate() of the wrapper can be done in CSS as well
    • do not only use the vendor-prefixed CSS property -webkit-transition, but the proper transition as well
    • // delay increases with each column looks like a misconception. all elements the selector .columns > li:first-child applies to will have the exact same delay - they won't wait for the previous element to finish its transition. If you want to define that in CSS, you'll have to play with :nth-child() or one of its cousins
    0 讨论(0)
  • 2021-02-13 16:22

    @rodneyrehm's answer pretty much sums up everything you need when handling animations with css display property.

    You need to trigger a reflow after toggling the display property and apply an animation class after it.

    // find elements
    const banner = $("#banner")
    const button = $(".banner__button")
    const text = $(".banner__text")
    let isVisible = false
    
    // toggle display
    button.on("click", () => {
    	if (!isVisible) {
      	text.addClass("display--block")
        text.outerWidth()
        text.addClass("text--show")
        isVisible = true
      } else {
      	text.addClass("text--hide")
        .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', () => {
      		text.removeClass("display--block")
          text.removeClass("text--show")
          text.removeClass("text--hide")
          isVisible = false
        })
      }
      
    })
    body {
      background: #20262E;
      padding: 20px;
      font-family: Helvetica;
    }
    
    #banner {
      background: #fff;
      border-radius: 4px;
      padding: 20px;
      font-size: 25px;
      text-align: center;
      margin: 0 auto;
      width: 300px;
      height: 150px;
      display: flex;
      flex-flow: column nowrap;
    }
    
    .banner__button {
      background: #0084ff;
      border: none;
      border-radius: 5px;
      padding: 8px 14px;
      font-size: 15px;
      color: #fff;
      cursor: pointer;
      transition: box-shadow 0.3s, transform 0.6s;
    }
    
    .banner__button:hover {
      box-shadow: 0 3px 8px 2px #9d9d91;
      transform: translateY(-2px)
    }
    
    .banner__button:focus {
      outline: 0;
    }
    
    .flex--1 {
      flex: 1;
    }
    
    .banner__text {
      display: none;
      opacity: 0;
      transition: all 0.6s;
    }
    
    .display--block {
      display: block;
    }
    .text--show {
      animation: slide-in 1s forwards;
    }
    .text--hide {
      animation: slide-out 1s forwards;
    }
    
    @keyframes slide-in {
      0% {
        opacity: 0;
        transform: translateX(-30px)
      }
      100% {
        opacity: 1;
        transform: translateX(0px)
      }
    }
    @keyframes slide-out {
      0% {
        opacity: 1;
        transform: translateX(0px)
      }
      100% {
        opacity: 0;
        transform: translateX(30px)
      }
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="banner">
      <div class="flex--1">
        <p class="banner__text">Hello World</p>  
      </div>
      <button class="banner__button">Toggle Text</button>
    </div>

    0 讨论(0)
  • 2021-02-13 16:27

    I went into this same problem a while ago, my workaround was very hacky but mostly works

    When you change a, lets say, non transitionable property, like 'display' thing go wrong really fast, i figured that if you use a timmer to change the not transitionable property and a few miliseconds later you change another transitionable property, it kind of works, an you also need to use another timer for turning things back

    The HTML

    <div class="content_menu_item">
       <a href="#">Im a menu item</a>
       <ul>
         <li>
           <a href="#">Sub Item 1</a>
         </li>
         <li>
          <a href="#">Sub Item 2</a>
         </li>
         <li>
          <a href="#">Sub Item 3</a>
         </li>
       </ul>
    </div><div class="content_menu_item">
       <a href="#">Im a menu item</a>
       <ul>
         <li>
           <a href="#">Sub Item 1</a>
         </li>
         <li>
          <a href="#">Sub Item 2</a>
         </li>
         <li>
          <a href="#">Sub Item 3</a>
         </li>
       </ul>
    </div>
    

    The CSS

    .content_menu_item{
      vertical-align: top;
      display:inline-block;
      width: 140px;
      height: 32px;
      position:relative;
      border:1px solid #b388ff;
      text-align: center;
        background-color: #6200ea;
    }
    
    .content_menu_item a{
      line-height: 32px;
      display: inline-block;
      text-decoration: none;
      color:white;
      width: 140px;
    }
    
    ul{
      padding: 0;
      list-style:none;
      display:none;
      margin: 0;
      opacity:0.5;
    }
    .content_menu_item ul li{
      color:white;
      background: #1e88e5;
      line-height: 26px;
      vertical-align: top;
      transition:all 385ms cubic-bezier(0.895, 0.03, 0.685, 0.22);
      opacity:0;
    }
    
    .content_menu_item ul li.on{
      opacity:1;
    }
    
    .content_menu_item ul li.on:nth-child(1){
      transition-delay:0ms;
    }
    .content_menu_item ul li.on:nth-child(2){
      transition-delay:50ms;
    }
    .content_menu_item ul li.on:nth-child(3){
      transition-delay:100ms;
    }
    
    .content_menu_item ul li.off{
      opacity:0;
    }
    
    .content_menu_item ul li.off:nth-child(3){
      transition-delay:0ms;
    }
    .content_menu_item ul li.off:nth-child(2){
      transition-delay:50ms;
    }
    .content_menu_item ul li.off:nth-child(1){
      transition-delay:100ms;
    }
    

    jQuery for handlig the states of the mouse

    $('.content_menu_item').hover(
     function(){
      // mouse over
      $(this).find('ul').show(); // show the sub list of the menu, basicaly display block
      timmeron = setTimeout(()=>{ // 10 miliseconds later add the class to change the opacity, the on class has a transition-delay for every element usin nth-child
        $(this).find('li').addClass('on');
      },10);
     },function(){
        //mouse out
        $(this).find('li').removeClass('on'); // remove the on class 
        $(this).find('li').addClass('off'); // add the off class to invert the transition-delay
            timmeroff = setTimeout(()=>{
              $(this).find('ul').hide(); // hide the element with time after the transition completes 
              $(this).find('li').removeClass('off'); // remove both classes
              $(this).find('li').removeClass('on');
          },500);
    })
    

    And here is a working example https://codepen.io/Teobis/pen/QxmqGQ

    Hope this helps

    0 讨论(0)
提交回复
热议问题