Skip to content

Visual effect on click or touch with pure Javascript and CSS

I have a responsive webpage which contains pictures and text and is designed to be viewed on a mobile device. I would like to add visual effects such that whenever a user touches or clicks on the page something happens at the point of contact (eg. a ripple or expanding circle).

Ideally I could put a bunch of different visual effects in a CSS file and pick the effect I want by adding the effects corresponding class to the page.

I can already detect the clicks and touches in JavaScript and know their co-ordinates, I just don’t know how to make an effect happen at the co-ordinates.

The following website does what I want on a blank page Andrew Gilmore. Unfortunately it uses jQuery and various other libraries, I am after pure JavaScript and CSS. I do not know enough jQuery to transpile to JavaScript.

The following website Mary Lou has a nice library of effects embedded in the htmls <style> element (click ‘view demo’). Unfortunately the effects are tied to buttons not to screen position clicks and touches, and my attempts to make them work on the screen have failed.

Based on Andrew Gilmore’ codepen.

HTML

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>CodePen - Click/Touch Visual Feedback</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="ping"></div>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
  <script src='https://cdn.jsdelivr.net/velocity/1.1.0/velocity.min.js'></script>
  <script  src="./script.js"></script>
</body>
</html>

CSS

#ping {
  background-size: 100%;
  background-image: -moz-radial-gradient(center, ellipse cover, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
  background-image: -webkit-radial-gradient(center, ellipse cover, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
  background-image: radial-gradient(ellipse cover at center, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
  position: absolute;
  height: 0;
  width: 0;
  opacity: 0;
}

And her is the jQuery plus velocity.js that I want to translate into pure JavaScript.

jQuery

(function($){
  var $ping = $('#ping');
  
  $(document).on('touchstart click', function(e){
    e.stopPropagation();
    e.preventDefault();
    
    $ping.velocity({
      opacity: 1,
      width: 100,
      height: 100
    },
    {
      easing: 'easeIn',
      duration: 300,
      begin: function(event, elements) {
        $(elements).css({
          display: 'block',
          width: 0,
          height: 0,
          top: e.clientY || e.originalEvent.touches[0].pageY,
          left: e.clientX || e.originalEvent.touches[0].pageX,
          transform: 'translate(-50%, -50%)'
        });
      }.bind(null, e)
    }).velocity({
      opacity: 0,
      width: 200,
      height: 200
    },
    {
      easing: 'linear',
      duration: 300,
      delay: 250,
      queue: false,
      complete: function(elements) {
        $(elements).css({
          display: 'none'
        });
      }
    });
    
  });
})(jQuery);

My JavaScript and CSS skills are average, so a simplified working answer to get me started would be enough for me to work out the rest. Given the co-ordinates, how do I get a visual effect to happen at them?

Answer

Answering my own question, the following code works. The basic principles are:-

  1. In a stylesheet define a radial gradient, this is what will be expanded from nothing when you click/touch the page. The radial gradient defines graduated colours in a elliptical box.
  2. In a stylesheet define your animation keyframes. These are the animations that can be applied to the radial gradient. The keyframes will usually change the height and width of the radial gradient box.
  3. In a script define code to get the click/touch co-ordinates, center the radial gradient on them, then expand the radial gradient from zero to max size. Once the animation ends ping reverts to being hidden.
  4. Note that to create a library of on-click visual effects you would need to library the pair of a keyframe and a radial gradient (as at the top of the CSS below).

Here is the code.

HTML

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="ping" class="example"></div>
  <script  src="./script.js"></script>
</body>
</html>

CSS

@keyframes example {
  0%   {height: 0%; width: 0%; }
  100% {height: 10%; width: 10%; }
}
.example {
  animation-name: example;
  animation-duration: 2s;
  background-image: -moz-radial-gradient(center, ellipse cover, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
  background-image: -webkit-radial-gradient(center, ellipse cover, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
  background-image: radial-gradient(ellipse cover at center, rgba(228, 245, 252, 0) 0%, rgba(191, 232, 249, 0.45) 30%, #72cbf3 66%, rgba(112, 202, 243, 0) 67%, rgba(42, 176, 237, 0) 100%);
}

#ping {
  display: none;
  background-size: 100%;
  position: absolute;
}

JavaScript

function doTouch(event) {
  var clientX = event.touches[0].clientX;
  var clientY = event.touches[0].clientY;
  pingEffect(clientX, clientY);
}
function doClick(event) {
  // get the click co-ords
  var clientX = event.clientX;
  var clientY = event.clientY;
  pingEffect(clientX, clientY);
}

var pingName = 'example';

function pingEffect(clientX, clientY) {
  // get and position the ping element
  var ping = document.getElementById('ping');
  ping.style.left = clientX+'px';
  ping.style.top = clientY+'px';
  ping.style.transform = 'translate(-50%, -50%)';
  ping.style.display = 'block';
  // run the animation
  ping.classList.remove(pingName);
  void ping.offsetWidth; // trigger a DOM reflow
  ping.classList.add(pingName);
}
document.addEventListener('touchstart', doTouch);
document.addEventListener('click', doClick);