skip to Main Content

I’m quite not sure how to approach this.
I have a div that has its position controlled with using inline style from javascript, ie:

<div style="transform: translate3d(20px, 40px, 0);"></div>

I have a generic "shake" animation as follow

.shake{
    animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
    backface-visibility: hidden;
    perspective: 1000px;
    transform-origin: center;
}
@keyframes shake {
    10%, 90% {
        transform: translateX(-2px);
    }  
    20%, 80% {
        transform: translateX(4px);
    }
    30%, 50%, 70% {
        transform: translateX(-8px);
    }
    40%, 60% {
        transform: translateX(8px);
    }
} 

If I apply my css class to the div, it will relocate the div to the top left of the screen. Is there a way to add to the existing translate3d from the animation, maybe with calc() ?
Ideally I’d like to do (pseudo code)

 @keyframes shake {
    10%, 90% {
        transform: translateX(@current_translate_x - 2px);
    }  
    ....


 

2

Answers


  1. There is now an animation-composition rule that you can set to tell how the properties set during the animation should affect the same properties already set before the animation:

    .shake {
        width: 30px;
        height: 30px;
        background: red;
    }
      
    :checked + .shake {
        animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
        backface-visibility: hidden;
        perspective: 1000px;
        transform-origin: center;
        animation-composition: add;
    }
    @keyframes shake {
        10%, 90% {
            transform: translateX(-2px);
        }  
        20%, 80% {
            transform: translateX(4px);
        }
        30%, 50%, 70% {
            transform: translateX(-8px);
        }
        40%, 60% {
            transform: translateX(8px);
        }
    } 
    <input type=checkbox>
    <div class="shake" style="transform: translate3d(20px, 40px, 0);"></div>

    If browser support is too low for you, then you can also achieve the same by using the translate property instead of transform: translate(), both will add up:

    .shake {
        width: 30px;
        height: 30px;
        background: red;
    }
      
    :checked + .shake {
        animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
        backface-visibility: hidden;
        perspective: 1000px;
        transform-origin: center;
    }
    @keyframes shake {
        10%, 90% {
            transform: translateX(-2px);
        }  
        20%, 80% {
            transform: translateX(4px);
        }
        30%, 50%, 70% {
            transform: translateX(-8px);
        }
        40%, 60% {
            transform: translateX(8px);
        }
    }
    <input type=checkbox>
    <!-- Note how we now use the 'translate' property -->
    <div class="shake" style="translate: 20px 40px;"></div>

    If it’s still too edgy for you, you could still resort to the old good wrap in container and transform said container:

    .shake-container > div {
        width: 30px;
        height: 30px;
        background: red;
    }
      
    :checked + .shake-container  > div {
        animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
        backface-visibility: hidden;
        perspective: 1000px;
        transform-origin: center;
    }
    @keyframes shake {
        10%, 90% {
            transform: translateX(-2px);
        }  
        20%, 80% {
            transform: translateX(4px);
        }
        30%, 50%, 70% {
            transform: translateX(-8px);
        }
        40%, 60% {
            transform: translateX(8px);
        }
    }
    <input type=checkbox>
    <!-- we set the initial transform on the container -->
    <div class="shake-container" style="transform: translate3d(20px, 40px, 0);">
      <div></div>
    </div>
    Login or Signup to reply.
  2. While I think @kaiido’s answers are better, here is one way of doing it using more the kind of structure you envisaged in the question – using CSS calc and introducing CSS variables for more flexibility:

    .shake {
      animation: shake 0.82s cubic-bezier(.36, .07, .19, .97) both;
      backface-visibility: hidden;
      perspective: 1000px;
      transform-origin: center;
    }
    
    @keyframes shake {
      10%,
      90% {
        transform: translate3d(calc(var(--x) - 2px), var(--y), var(--z));
      }
      20%,
      80% {
        transform: translate3d(calc(var(--x) + 4px), var(--y), var(--z));
      }
      30%,
      50%,
      70% {
        transform: translate3d(calc(var(--x) - 8px), var(--y), var(--z));
      }
      40%,
      60% {
        transform: translate3d(calc(var(--x) + 8px), var(--y), var(--z));
      }
    }
    <div style="--x: 20px; --y: 40px; --z: 0; transform: translate3d(var(--x), var(--y), var(--z));" class="shake">abc</div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search