Skip to content
Advertisement

Unsmooth sliding-up animation

I have created this page and when the “got it” button on the top panel is clicked, the panel will slide up and disappear while the page will also move up to cover the panel space. I have noticed that when the animation plays, it is not that smooth as white space can be seen in the split second that the page is sliding up. Is there any way to improve the animation to remove the split second in which the white space can be seen?

The code below is only a part of the complete one and won’t be working properly like this one here as it is for reference only. Only pure HTML, CSS and Javascript are used.

const panel = document.getElementById("panel");
const page = document.getElementById("main-page");

function closePanel() {
  panel.classList.add("panel-animation");
  page.classList.add("page-animation");
  setTimeout((panel.style.transform = "translateY(-1000px)"), 1000);
  setTimeout((page.style.top = "0"), 1000);
}
* {
  margin: 0;
  padding: 0;
}

.panel {
  height: 10%;
  width: 100%;
}

.notif-panel {
  z-index: 1;
  position: fixed;
  display: grid;
  height: 10%;
  width: 100%;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "panel-content";
}

.panel-content {
  display: flex;
  height: 100%;
  background-color: var(--smokeGrey);
  justify-content: center;
  align-items: center;
}

.panel-text {
  display: inline;
}

.cookie,
.privacy,
.tos {
  text-decoration: none;
}

.panel-button {
  display: inline;
  margin-left: 40px;
  padding: 10px 16px;
  background-color: #007bc1;
  color: white;
  border: none;
  border-radius: 2px;
}

.panel-button:hover {
  background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}

@media (max-width: 675px) {
  .notif-panel {
    height: 10%;
  }
  .panel-content {
    padding: 0 5px;
  }
  .panel-text {
    padding-right: 15px;
  }
  .panel-button {
    font-size: 17px;
    margin: 0;
  }
}

@media (max-width: 605px) {
  .notif-panel {
    height: 15%;
  }
  .panel-content {
    align-items: left;
    display: block;
    padding: 10px;
    font-size: 18px;
  }
  .panel-text {
    padding-bottom: 10px;
    display: block;
  }
  .panel-button {
    font-size: 17px;
    margin: 0;
  }
}

.main-page {
  position: absolute;
  width: 100%;
}

.hero {
  display: flex;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(
      rgba(0, 74, 117, 0.52),
      rgba(0, 74, 117, 0.52)
    ),
    url("assets/work-desk__dustin-lee.jpg");
  height: 600px;
}

.logo {
  height: 50px;
  width: 50px;
  position: absolute;
  left: 30px;
  top: 25px;
}

.hero-content {
  margin: 0;
  text-align: center;
  color: white;
}

.name {
  font-size: 30px;
}

.contact-text {
  margin-top: 8px;
  padding-bottom: 25px;
}

.contact-button {
  font-weight: bold;
  color: white;
  background-color: transparent;
  border: white 2px solid;
  padding: 12px 15px;
  border-radius: 2px;
}

.contact-button:hover {
  color: var(--blue);
  background-color: white;
}

.panel-animation {
  animation: animation1 1s ease-in-out;
}

.page-animation {
  animation: animation2 0.2s ease-in-out;
}

@keyframes animation1 {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-500px);
  }
}

@keyframes animation2 {
  0% {
    top: 10%;
  }
  100% {
    top: 0;
  }
}
<div class="panel" id="panel">
  <div class="notif-panel" id="notifPanel">
    <div class="panel-content">
      <p class="panel-text">
        By accessing and using this website, you acknowledge that you have read
        and <span class="brCust" id="brCust"></span>understand our
        <a class="cookie" href="#">Cookie Policy</a>,
        <a class="privacy" href="#">Privacy Policy</a>, and our
        <a class="tos" href="#">Terms of Service</a>.
      </p>
      <button class="panel-button" onclick="closePanel()">Got it</button>
    </div>
  </div>
</div>

<div class="main-page" id="main-page">
  <section class="hero" id="hero">
    <img src="assets/y-logo-white.png" class="logo" />
    <div class="hero-content">
      <p class="name">Hello! I'm Dylan Anderton</p>
      <h2 class="slogan">Consult, Design, and Develop Websites</h2>
      <p class="contact-text">
        Have something great in mind? Feel free to contact me.<br />
        I'll help you to make it happen.
      </p>
      <button class="contact-button">LET'S MAKE CONTACT</button>
    </div>
  </section>
</div>

Advertisement

Answer

The problem

Your #panel is a sibling of main, but you want #panel to be fixed on the upper side of the viewport even when scrolling: You solved that with position: fixed.

But using position: fixed means that #panel will only be perfectly layed out on certain screen sizes. On any other screen size, and it may overlap main‘s content. Also, it may cause visual jitter when animating (see section “Using position: sticky“).

You also want #panel to slide out when requested, and main to take up the then-empty space: You solved that slide-out and “slide into place” with 2 different animations; #panel moves out by a lot, but main moves only enough to take up the empty space. Additionally, they move for a different duration.

The different distances and durations of the animations cause them to be not in sync with each other. Ideally both should move by the same amount and in the same time to make it look smooth.

Overexaggerated example of the issue:

section:hover #banner {
  top: -400px;
  transition-duration: 1s;
}
section:hover #content {
  top: -80px;
  transition-duration: 2s;
}

/* PRESENTATIONAL STYLING */
body {font-family:sans-serif}
section {
  width: fit-content;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}

#viewport {
  border: 1px solid black;
  grid-row: 1/1;
  grid-column: 1/-1;
}
#page {
  grid-row: 1/1;
  grid-column: 2/2;
}

#banner, #content {
z-index: -1;
  --size: 80px;
  position: relative;
  top: 0;
  width: var(--size);
  height: var(--size);
  color: white;
  transition-property: top;
}
#banner {background-color:firebrick}
#content {background-color:darkblue}
<section>
  <div id="viewport">Viewport</div>
  <div id="page">
    <div id="banner">Banner</div>
    <div id="content">Content</div>
  </div>
</section>

Solutions

Using position: sticky

Using position: sticky on #panel means that when scrolled, #panel will be on top of main‘s content, as with position: fixed. But unlike fixed, sticky will still place the element in the flow, which means it will not overlap main when not scrolled. No magic numbers or JS needed for this!

And when wanting to hide #panel, we can shrink it and slide it out of the viewport, so that main can take up the empty space:

const panel = document.getElementById('panel');
const page = document.getElementById('main-page');

function closePanel(){
  const {bottom} = panel.getBoundingClientRect();
  panel.style.marginBottom = 0 + "px";
  panel.addEventListener("transitionend", () => panel.replaceWith());
  
  // marginBottom shrinks the element;
  // translateY() slides it out.
  
  setTimeout(() => {
    panel.style.marginBottom = `${-bottom}px`;
    panel.style.transform = `translateY(${-bottom}px)`;
  }, 0);
}
.panel{
  z-index: 1;
  position: sticky;
  top: 0;
  transition:
    margin-bottom 1s,
    transform 1s;
}
.hero {
  position: relative;
}

*{
  margin:0;
  padding:0;
}
html {--smokeGrey:#eee}

.notif-panel{
  display:grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "panel-content";
}

.panel-content{
  display:flex;
  justify-content:center;
  align-items:center;
  background-color:var(--smokeGrey);
}

.panel-text{
  display:inline;
}

.cookie, .privacy, .tos{
  text-decoration:none;
}

.panel-button{
  display:inline;
  padding: 10px 16px;
  background-color: #007bc1;
  color: white;
  border: none;
  border-radius: 2px;
}

.panel-button:hover{
  background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}

@media(max-width:675px){
  .notif-panel{
    height: 10%;
  }
  .panel-content{
    padding: 0 5px;
  }
  .panel-text{
    padding-right: 15px;
  }
  .panel-button{
    font-size: 17px;
}
}

@media(max-width:605px){
  .notif-panel{
    height: 15%;
  }
  .panel-content{
    align-items: left;
    display: block;
    padding: 10px;
    font-size: 18px;
  }
  .panel-text{
    padding-bottom: 10px;
    display: block;
  }
  .panel-button{
    font-size: 17px;
}
}

.main-page{
  width: 100%;
}

.hero{
  display: flex;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg');
  height: 600px;
}

.logo{
  height: 50px;
  width: 50px;
  position: absolute;
  left: 30px;
  top: 25px;
}

.hero-content{
  margin:0;
  text-align: center;
  color: white;
}

.name{
  font-size: 30px;
}

.contact-text{
  margin-top: 8px;
  padding-bottom: 25px;
}

.contact-button{
  font-weight: bold;
  color: white;
  background-color: transparent;
  border: white 2px solid;
  padding: 12px 15px;
  border-radius: 2px;
}

.contact-button:hover{
  color: var(--blue);
  background-color: white;
}
<div class="panel" id="panel">
  <div class="notif-panel" id="notifPanel">
    <div class="panel-content">
      <p class="panel-text">By accessing and using this website, you acknowledge that you have read and
        <span class="brCust" id="brCust"></span>understand our
        <a class="cookie" href="#">Cookie Policy</a>,
        <a class="privacy" href="#">Privacy Policy</a>, and our
        <a class="tos" href="#">Terms of Service</a>.
      </p>
      <button class="panel-button" onclick="closePanel()">Got it</button>
    </div>
  </div>
</div>

<div class="main-page" id="main-page">
  <section class="hero" id="hero">
      <img src="assets/y-logo-white.png" class="logo">
      <div class="hero-content">
          <p class="name">Hello! I'm Dylan Anderton</p>
          <h2 class="slogan">Consult, Design, and Develop Websites</h2>
          <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p>
          <button class="contact-button">LET'S MAKE CONTACT</button>
      </div>
  </section>
</div>

Did you notice that when scrolled and hiding #panel, main won’t jitter? Using position: sticky is the least visually jarring, which makes it my preferred method to use. Compare with the visual jitter of the other examples, or even your own webpage on smaller screen sizes!

Using transitions

To move both elements (#panel and main) by the same amount, we have to find the y-position of #panel. This can be done with Element.getBoundingClientRect().

Then, we want to move both elements upwards:

  1. Move bottom of #panel from found y-position to 0.
  2. Move top of main from found y-position to 0.

This can be done as follows:

const panel = document.getElementById('panel');
const page = document.getElementById('main-page');

function closePanel(){
  const rectPanel = panel.getBoundingClientRect();
  panel.style.top = 0;
  page.style.top = rectPanel.bottom + "px";
  
  panel.style.transition = "top 1s ease-in-out";
  page.style.transition = "top 1s ease-in-out";
  
  // Don't forget to remove it from the DOM
  panel.addEventListener("transitionend", () => panel.replaceWith());
  
  setTimeout(() => {
    panel.style.top = -rectPanel.bottom + "px";
    page.style.top = 0;
  }, 0);
}
/* I had to move position:fixed to this top-level element */
.panel{
  z-index: 1;
  position: fixed;
}

*{
  margin:0;
  padding:0;
}
html {--smokeGrey:#eee}

.notif-panel{
  display:grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "panel-content";
}

.panel-content{
  display:flex;
  justify-content:center;
  align-items:center;
  background-color:var(--smokeGrey);
}

.panel-text{
  display:inline;
}

.cookie, .privacy, .tos{
  text-decoration:none;
}

.panel-button{
  display:inline;
  padding: 10px 16px;
  background-color: #007bc1;
  color: white;
  border: none;
  border-radius: 2px;
}

.panel-button:hover{
  background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}

@media(max-width:675px){
  .notif-panel{
    height: 10%;
  }
  .panel-content{
    padding: 0 5px;
  }
  .panel-text{
    padding-right: 15px;
  }
  .panel-button{
    font-size: 17px;
}
}

@media(max-width:605px){
  .notif-panel{
    height: 15%;
  }
  .panel-content{
    align-items: left;
    display: block;
    padding: 10px;
    font-size: 18px;
  }
  .panel-text{
    padding-bottom: 10px;
    display: block;
  }
  .panel-button{
    font-size: 17px;
}
}

.main-page{
  position: absolute;
  width: 100%;
}

.hero{
  display: flex;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg');
  height: 600px;
}

.logo{
  height: 50px;
  width: 50px;
  position: absolute;
  left: 30px;
  top: 25px;
}

.hero-content{
  margin:0;
  text-align: center;
  color: white;
}

.name{
  font-size: 30px;
}

.contact-text{
  margin-top: 8px;
  padding-bottom: 25px;
}

.contact-button{
  font-weight: bold;
  color: white;
  background-color: transparent;
  border: white 2px solid;
  padding: 12px 15px;
  border-radius: 2px;
}

.contact-button:hover{
  color: var(--blue);
  background-color: white;
}
<div class="panel" id="panel">
  <div class="notif-panel" id="notifPanel">
    <div class="panel-content">
      <p class="panel-text">By accessing and using this website, you acknowledge that you have read and
        <span class="brCust" id="brCust"></span>understand our
        <a class="cookie" href="#">Cookie Policy</a>,
        <a class="privacy" href="#">Privacy Policy</a>, and our
        <a class="tos" href="#">Terms of Service</a>.
      </p>
      <button class="panel-button" onclick="closePanel()">Got it</button>
    </div>
  </div>
</div>

<div class="main-page" id="main-page">
  <section class="hero" id="hero">
      <img src="assets/y-logo-white.png" class="logo">
      <div class="hero-content">
          <p class="name">Hello! I'm Dylan Anderton</p>
          <h2 class="slogan">Consult, Design, and Develop Websites</h2>
          <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p>
          <button class="contact-button">LET'S MAKE CONTACT</button>
      </div>
  </section>
</div>

Using Web Animations API

Same steps as when using transitions, but implemented via the Web Animations API:

const panel = document.getElementById('panel');
const page = document.getElementById('main-page');

function closePanel(){
  const {bottom} = panel.getBoundingClientRect();
  const duration = 1000; // ms
  
  panel.animate(
      { top: [0, `${-bottom}px`] },
    { duration }
  ).finished.then(() => {
    panel.replaceWith(); // Don't forget to remove element from DOM!
  });
  
  page.animate(
    { top: [`${bottom}px`, 0] },
    { duration }
  );
}
/* I had to move position:fixed to this top-level element */
.panel{
  z-index: 1;
  position: fixed;
}

*{
  margin:0;
  padding:0;
}
html {--smokeGrey:#eee}

.notif-panel{
  display:grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "panel-content";
}

.panel-content{
  display:flex;
  justify-content:center;
  align-items:center;
  background-color:var(--smokeGrey);
}

.panel-text{
  display:inline;
}

.cookie, .privacy, .tos{
  text-decoration:none;
}

.panel-button{
  display:inline;
  padding: 10px 16px;
  background-color: #007bc1;
  color: white;
  border: none;
  border-radius: 2px;
}

.panel-button:hover{
  background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}

@media(max-width:675px){
  .notif-panel{
    height: 10%;
  }
  .panel-content{
    padding: 0 5px;
  }
  .panel-text{
    padding-right: 15px;
  }
  .panel-button{
    font-size: 17px;
}
}

@media(max-width:605px){
  .notif-panel{
    height: 15%;
  }
  .panel-content{
    align-items: left;
    display: block;
    padding: 10px;
    font-size: 18px;
  }
  .panel-text{
    padding-bottom: 10px;
    display: block;
  }
  .panel-button{
    font-size: 17px;
}
}

.main-page{
  position: absolute;
  width: 100%;
}

.hero{
  display: flex;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(rgba(0,74,117,.52),rgba(0,74,117,.52)), url('assets/work-desk__dustin-lee.jpg');
  height: 600px;
}

.logo{
  height: 50px;
  width: 50px;
  position: absolute;
  left: 30px;
  top: 25px;
}

.hero-content{
  margin:0;
  text-align: center;
  color: white;
}

.name{
  font-size: 30px;
}

.contact-text{
  margin-top: 8px;
  padding-bottom: 25px;
}

.contact-button{
  font-weight: bold;
  color: white;
  background-color: transparent;
  border: white 2px solid;
  padding: 12px 15px;
  border-radius: 2px;
}

.contact-button:hover{
  color: var(--blue);
  background-color: white;
}
<div class="panel" id="panel">
  <div class="notif-panel" id="notifPanel">
    <div class="panel-content">
      <p class="panel-text">By accessing and using this website, you acknowledge that you have read and
        <span class="brCust" id="brCust"></span>understand our
        <a class="cookie" href="#">Cookie Policy</a>,
        <a class="privacy" href="#">Privacy Policy</a>, and our
        <a class="tos" href="#">Terms of Service</a>.
      </p>
      <button class="panel-button" onclick="closePanel()">Got it</button>
    </div>
  </div>
</div>

<div class="main-page" id="main-page">
  <section class="hero" id="hero">
      <img src="assets/y-logo-white.png" class="logo">
      <div class="hero-content">
          <p class="name">Hello! I'm Dylan Anderton</p>
          <h2 class="slogan">Consult, Design, and Develop Websites</h2>
          <p class="contact-text">Have something great in mind? Feel free to contact me.<br> I'll help you to make it happen.</p>
          <button class="contact-button">LET'S MAKE CONTACT</button>
      </div>
  </section>
</div>

Notice that I used a keyframe format where I specify the values of a property in steps.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement