vue modal dropping frames when using transitions

Viewed 249

I wrote a custom vue modal and it works perfectly fine on my pc but it keeps dropping frames on some mobile devices. It seems to be related to the transitions - without it works as expected (but looks and feels bad).

Even a transition with opacity 1 to opacity 0 causing lag in this case. (Any animation causing lag)

It's really simple code so it should work on every device - even mid range devices like the Samsung M31 can't display the animation without lag. (Even when the transition is only opacity or scale)

Menu.vue

    <Modal :is-open="testModalIsOpen" >
      test1
      test2
      test3
      test4
      test5
    <template #footer>
      <button @click="testModalIsOpen=false">BACK</button>
    </template>
  </Modal>

Modal.vue

<template>
  <transition name="rpg-modal__transition">
    <div class="rpg-modal" v-if="isOpen" :class="classes">
      <div class="rpg-modal__backdrop"></div>
      <div class="mx-auto rpg-modal__content-wrapper">
        <div class="rpg-modal__box" style="background-color: #0b1b3d">
            <div>
              <div class="rpg-modal__body">
                <slot/>
              </div>
              <div class="rpg-modal__footer d-flex justify-content-end">
                <slot name="footer"/>
              </div>
            </div>
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
import {computed} from "@vue/reactivity";

export default {
  name: "Modal",
  props: {
    isOpen: {
      type: Boolean,
      default: false
    },
    autoPadding: {
      type: Boolean,
      default: true
    }
  },
  setup(props) {
    const classes = computed(() => ({
      'rpg-modal--auto-padding': props.autoPadding
    }));

    return {
      classes,
    };
  }
}
</script>

<style lang="scss">
.rpg-modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  $transition-duration: 0.3s;

  &__transition {
    &-enter-active, &-leave-active {
      transition: all $transition-duration;
    }

    &-enter-from, &-leave-to {
      opacity: 0;
      filter: blur(5px);
      transform: scale(1.3);
    }
  }

  &__backdrop {
    position: absolute;
    height: 100%;
    width: 100%;
    background-color: rgba(0, 0, 0, 0.5);
  }

  &__content-wrapper {
    max-width: 64vh;
    margin: auto;
  }

  &__box {
    $boxMargin: 13px;
    position: relative;
    margin: $boxMargin;

    max-height: calc(100vh - #{$boxMargin * 2});
    border-radius: 15px;

    background-repeat: no-repeat;
    background-size: 100% 100%;

    box-shadow: #00000073 0 0 10px;
  }

  &--auto-padding &__body {
    padding: 12px;
  }

  &__footer {
    border-top: 1px solid #000000;
    padding: 9px;

    > * {
      margin: .25rem;
    }
  }

  &__close {
    font-size: 1.5rem;
    line-height: 1;
    text-shadow: 0 1px 0 #fff;

    position: absolute;
    z-index: 20;
    background-color: rgb(62 62 62);
    box-shadow: #00000052 2px 2px 5px;
    color: white;
    height: 30px;
    width: 30px;
    padding: 0 0 4px;
    border-radius: 50px;
    border: none;
    right: 17px;
    top: 17px;
    transition: background-color 200ms;

    &:hover {
      background-color: #9f3e3e;
    }
  }
}
</style>

enter image description here

Hope anyone can help & thanks for any answers!

2 Answers

Have you tried the will-change property?

The will-change property in CSS optimizes animations by letting the browser know which properties and elements are just about to be manipulated, potentially increasing the performance of that particular operation.
from here

So, try this:

.rpg-modal{
  will-change: transform, opacity, filter;
}

Also, set the initial value of the modal, the opacity:0 and the others...

Is this why my spinner isn't working?

<b-modal>
  <b-card>
    <my-spinner v-if="loading" />
    <my-content content="myData" />
  </b-card>
</b-modal>

mounted() {
  this.loading = true
  this.readSomeData()
    .then(response => {
      this.myData = response.data
    })
    .finally(() => {
      this.loading = false
    })
}

Note. I even added a setTimeout of 5s before fetching data, and spinner only wants to display after data fetch. also tried putting this.loading = true inside a this.$nextTick(() => this.loading = true ) did not work either.

Related