Как воспроизвести анимацию пульсаций материала в Vuejs?

#javascript #vue.js #sass #material-design #vue-transitions

Вопрос:

Я пытаюсь воспроизвести эффект пульсации от Material Design, так как текущее приложение, над которым я работаю, собирается избавиться от Квазара; поэтому я создаю все элементы с нуля.

Этот эффект: введите описание изображения здесь

Я посмотрел несколько видеороликов, в которых это делается только в чистом CSS и JS, и я попытался преобразовать их в свой проект, но где-то застрял. У меня есть эффект наведения и правильная регистрация местоположения мыши, но эффект пульсации просто не срабатывает, и я не знаю, почему.

Любая помощь будет очень признательна! Ваше здоровье!

Код и код коробки

CButton.vue

 <template>
  <button
    @click="onClick"
    :class="[
      'c-btn',
      `c-btn--${kind}`,
      disabled ? `_disabled` : '',
      kind === 'icon-round' ? 'shadow-5' : '',
    ]"
  >
    <transition
      name="ripple"
      @enter="rippleEnter"
      @after-enter="afterRippleEnter"
    >
      <span v-if="ripple" ref="ripple" class="ripple" />
    </transition>
    <div class="_inner">
      <div class="_text">
        <slot>{{ btnText }}</slot>
      </div>
    </div>
  </button>
</template>

<script>
export default {
  name: "CcBtn",
  components: {},
  props: {
    btnText: { type: String },
    kind: { type: String, default: "main" },
    isBusy: { type: Boolean, default: false },
    /**
     * HTML5 attribute
     * @category state
     */
    disabled: { type: Boolean, default: false },
    color: { type: String, default: "" },
  },
  data() {
    return {
      ripple: false,
      x: 0,
      y: 0,
    };
  },
  methods: {
    onClick(e) {
      this.x = e.layerX;
      this.y = e.layerY;
      this.ripple = !this.ripple;
      console.log(`x`, this.x);
      console.log(`y`, this.y);
      console.log(`ripple`, this.ripple);
    },
    rippleEnter() {
      this.$refs.ripple.style.top = `${this.y}px`;
      this.$refs.ripple.style.left = `${this.x}px`;
    },
    afterRippleEnter() {
      this.ripple = false;
    },
  },
};
</script>

<style lang="sass" scoped>
.c-btn
  color: white
  padding: 10px 16px
  border-radius: 4px
  line-height: 1em
  min-height: 2em
  font-weight: bold
  font-size: 16px
  color: White
  cursor: pointer
  border: 1px solid transparent
  transition: background-color 0.5s
  ._inner
    display: flex
    align-items: center
    justify-content: center
  amp;--main
    background: #9759ff
    min-width: 228px
    border-radius: 100px
    amp;:hover
      background-color: lighten(#9759ff, 10%)

  amp;--sub
    background: #f3eefe
    min-width: 228px
    border-radius: 100px
    color: black
    amp;:hover
      background-color: darken(#f3eefe, 5%)

.ripple
  display: block
  width: 20px
  height: 20px
  border-radius: 10px
  position: absolute
  top: 0
  left: 0
  pointer-events: none
  background-color: rgba(lighten(#9759ff, 20%), 0.8)
  opacity: 0
  transform: translate(-50%, -50%) scale(10)
  transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out
  amp;-enter
    opacity: 1
    transform: translate(-50%, -50%) scale(0)
</style>
 

Приложение.vue

 <template>
  <CButton :btnText="'Button'" kind="main" />
  <br />
  <br />
  <br />
  <CButton :btnText="'Button'" kind="sub" />
</template>

<script>
import CButton from "./components/CButton.vue";
export default {
  name: "App",
  components: {
    CButton,
  },
};
</script>
 

Ответ №1:

Вот рабочий код для кнопки, которая оказывает волновой эффект при нажатии. Использование CSS и JS:

 function createRipple(event) {
  const button = event.currentTarget;

  const circle = document.createElement("span");
  const diameter = Math.max(button.clientWidth, button.clientHeight);
  const radius = diameter / 2;

  circle.style.width = circle.style.height = `${diameter}px`;
  circle.style.left = `${event.clientX - button.offsetLeft - radius}px`;
  circle.style.top = `${event.clientY - button.offsetTop - radius}px`;
  circle.classList.add("ripple");

  const ripple = button.getElementsByClassName("ripple")[0];

  if (ripple) {
    ripple.remove();
  }

  button.appendChild(circle);
}

const buttons = document.getElementsByTagName("button");
for (const button of buttons) {
  button.addEventListener("click", createRipple);
} 
 body {
  height: 100vh;
  margin: 0;
  display: grid;
  place-items: center;
}

@import url('https://fonts.googleapis.com/css2?family=Robotoamp;display=swap');

button {
  position: relative;
  overflow: hidden;
  transition: background 400ms;
  color: #fff;
  background-color: #ff0000;
  padding: 1rem 2rem;
  font-family: 'Roboto', sans-serif;
  font-size: 1.5rem;
  outline: 0;
  border: 0;
  border-radius: 0.25rem;
  box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.3); /* black with 30% opacity */
  cursor: pointer;
}

span.ripple {
  position: absolute;
  border-radius: 50%;
  transform: scale(0);
  animation: ripple 600ms linear;
  background-color: rgba(255, 255, 255, 0.7);
}

@keyframes ripple {
  to {
    transform: scale(4);
    opacity: 0;
  }
} 
 <button>Click For Effect</button> 

Комментарии:

1. Спасибо за код и комментарий! Тем не менее, я видел много таких, сделанных в codesandbox или где-то еще, и пытаюсь преобразовать их в Vue. Не знаю, где я сейчас ошибаюсь. Может быть, это потому, что я не использую ключевые кадры? В любом случае, я более внимательно посмотрю на ваш комментарий и посмотрю, не смогу ли я это понять