Guide to recreating the Material Ripple effect using Vuejs

I am in the process of recreating the ripple effect inspired by Material Design for my current project. I have decided to move away from Quasar and build all the elements from scratch.

Here is the effect: https://i.sstatic.net/VKJIc.gif

I have seen some tutorials on creating this effect using pure CSS and JS, and I attempted to integrate it into my project. However, I am encountering difficulties as the ripple effect is not triggering despite having the hover effect and proper logging of mouse location.

Any assistance with resolving this issue would be highly appreciated! Thank you!

Access CodeSandbox Code

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
  &--main
    background: #9759ff
    min-width: 228px
    border-radius: 100px
    &:hover
      background-color: lighten(#9759ff, 10%)

  &--sub
    background: #f3eefe
    min-width: 228px
    border-radius: 100px
    color: black
    &: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
  &-enter
    opacity: 1
    transform: translate(-50%, -50%) scale(0)
</style>

App.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>

Answer ā„–1

Below is a functional code snippet for creating a button with a ripple effect when clicked, using a combination of CSS and JavaScript:

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=Roboto&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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Each time I invoke the setInterval function, my counter speeds up - using vuejs

In my development process, I am creating a countdown that is triggered by a function. The main objective is to reset the countdown each time a user answers a question in the game and a new question appears. However, I have encountered a challenge where i ...

Resizing tables dynamically using HTML and JavaScript

I am attempting to reproduce the functionality demonstrated in this example When dealing with a large table, I want it to resize to display 10 entries and paginate them accordingly. The only thing I require is the pagination feature without the search bar ...

ACE.js enhances website security through Content Security Policy

I've been working on setting up a helmet-csp with ace running smoothly. Here's how my helmet-setup looks: var csp = require("helmet-csp"); app.use(csp({ directives: { defaultSrc: ["'self'", "https://localhost:8000"], ...

Methods for transferring data to controller in Angular Modal service

After implementing the angular modal service discussed in this article: app.service('modalService', ['$modal', function ($modal) { var modalDefaults = { backdrop: true, keyboard: true, m ...

Struggling to make dynamically created SVG 'use' elements function properly

SVG offers a unique system with symbol and use where icons can be defined once and reused throughout the SVG using use. However, I am having trouble getting it to work from JavaScript. You can view an example on this JSFiddle link. When programmatically ...

Getting a `undefined` value when changing templates in VueJS

Currently, I am transitioning a list to a table to enable more columns in the future, but I have encountered an undefined variable error that has left me confused. Below is the original list: <ul class="collection with-header"> ...

Passing data from an API in Vue.js to a different page

I'm a beginner with Vue Js and I'm looking for guidance on how to send data between two components. Currently, I am working on a Vue app that fetches a list of users from an API and displays them. My goal is to transfer data between these compone ...

Building a table using a JSON object in a React component

I have been dynamically creating a table in React based on the API response received. data = {"name":"tom", "age":23, "group":null, "phone":xxx} Everything was working fine until I encountered a scenario w ...

Having trouble getting npm install @material-ui/core to work properly

I'm facing an issue while trying to set up material ui on my project. The following error is appearing: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: <a href="/cdn-cgi/l/email-protecti ...

The jCapSLide Jquery Plugin is experiencing compatibility issues in Chrome browser

While this JQuery plugin works perfectly in Internet Explorer and Firefox, it seems to be malfunctioning in Chrome. The plugin is not being recognized at all by Chrome, and the captions are appearing below the image instead of on top with a sliding effect. ...

Conceal and unveil stationary navigation when scrolling back to the very top of the screen

My current setup includes a navigation that dynamically hides as the user scrolls down and reappears when scrolling up. Additionally, I have applied the class .fixed on #top-wrapper to alter the layout/styling of this specific div. However, I am facing dif ...

Highlight or unhighlight text using Javascript

Currently, I am facing a challenge in highlighting specific words on an HTML page. Although I have succeeded in highlighting the desired element, I am struggling with unhighlighting the previous word when a new search is conducted. Despite my attempts to i ...

Trading keys between arrays in JavaScript

I have a set of simple objects contained within an array: var myObject1 = [ {name: "value1", adddress: "545454545445"}, {name: "value2", adddress: "323233223"}, {name: "value3", adddress: "332 ...

Step-by-step guide on creating a login functionality in Node using the Mean.io stack

I'm currently working on a web app using the MEAN.io stack. I have completed the frontend part with HTML, CSS, and AngularJS along with some logic. Now, I am looking to implement server-side login functionality, but I'm unsure where to begin sinc ...

Every time I attempt to execute mupx deploy, an error message appears

issue in console shubhabrata@shubhabrata-VirtualBox:~/Meteor/myapp$ mupx deploy Meteor Up: Advancing Meteor Deployments for Production Configuration file : mup.json Settings file : settings.json ā€œ Discover Kadira! A powerful tool to monitor yo ...

sent a data entry through ajax and performed validation using jquery

Is there a solution to validating the existence of an email value using ajax after validation work is completed? Despite attempting to check for the email value and display an alert if it exists, the form continues to submit regardless. See below for the ...

Locating the elusive comma - unraveling the mystery of Javascript Arrays nested within Objects

I've managed to put together something that functions as intended, but I'm facing an issue where I get a comma between entries when there are multiple ones. Here's the code snippet: chrome.runtime.onMessage.addListener(function (message, s ...

There seems to be an issue with the audioElement.src, which I used to run and play different songs. However, it is now displaying a 404

Array.from(document.getElementsByClassName('songItemPlay')).forEach(function (item) { item.addEventListener('click', (evt) => { playAllSongs(); position = parseInt(evt.target.id); evt.target.classList.re ...

Transferring data from a JavaScript variable to PHP using AJAX

Iā€™m currently working through the tutorial at http://www.w3schools.com/php/php_ajax_database.asp and I think I have a good grasp of how it all operates. This is my code: PHP: <?php include('./db.php'); $PM = mysqli_query($con, "SELECT DIS ...

Encountering a Vue warning while attempting to perform semantic binding in Vue.js

I am brand new to working with Vue.js and I wanted to experiment with semantic binding. The issue is that I have my Vue.js file in the same directory as my testing page, but for some reason I keep receiving a warning saying "Cannot find element: #growler". ...