Как создать эффект пульсации при клике - `Material Design`

314
15 апреля 2017, 23:07

Я новичок в CSS-анимации, и стараюсь в течение последних часов, чтобы анимация заработала. Пытаюсь понять код Material Design, но пока не могу заставить его работать.

Я говорю об этом эффекте: https://angular.io/ (эффект меню). В принципе, это анимация при клике, которая распространяется по кругу от курсора мыши.

Кажется, это сводится к этим двум строкам:

transition: box-shadow .4s cubic-bezier(.25,.8,.25,1),background-color .4s cubic-bezier(.25,.8,.25,1),-webkit-transform .4s cubic-bezier(.25,.8,.25,1);
transition: box-shadow .4s cubic-bezier(.25,.8,.25,1),background-color .4s cubic-bezier(.25,.8,.25,1),transform .4s cubic-bezier(.25,.8,.25,1);   

PS: Может быть, есть какой-то код jQuery, который реализует эту анимацию.

Перевод ответа: How to create Ripple effect on Click - Material Design @topleft

Answer 1

Эффект пульсации в Material Design с использованием jQuery и CSS3

jsBin demo

Чтобы создать UI Ripple effect, вам необходимо:

  • Добавить к любому элементу oveflow:hidden, чтобы ограничить круг пульсаций (так как вы не хотите изменять ваш исходный элемент и поэтому с помощью overflow не увидите, что эффект пульсации выходит за пределы желаемого контейнера)
  • Добавить к контейнеру c overflow просвечивающий радиальный элемент ripple wave
  • Взять координаты щелчка мышки, и с помощью CSS3 оживить масштабирование и непрозрачность ripple element
  • Прослушайте событие - анимация и уничтожьте пульсацию.

Основной код:

В основном добавьте data-ripple (по умолчанию - белая рябь) или data-ripple = "# 000" для нужного элемента:

<a data-ripple> EDIT </a>
<div data-ripple="rgba(0,0,0, 0.3)">Lorem ipsum</div>      

CSS:

/* MAD-RIPPLE EFFECT */
.ripple{
  position: absolute;
  top:0; left:0; bottom:0; right:0;
  overflow: hidden;
  -webkit-transform: translateZ(0); /* to contain zoomed ripple */
  transform: translateZ(0);
  border-radius: inherit; /* inherit from parent (rounded buttons etc) */
  pointer-events: none; /* allow user interaction */
          animation: ripple-shadow 0.4s forwards;
  -webkit-animation: ripple-shadow 0.4s forwards;
}
.rippleWave{
  backface-visibility: hidden;
  position: absolute;
  border-radius: 50%;
  transform: scale(0.7); -webkit-transform: scale(0.7);
  background: rgba(255,255,255, 1);
  opacity: 0.45;
          animation: ripple 2s forwards;
  -webkit-animation: ripple 2s forwards;
}
@keyframes ripple-shadow {
  0%   {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
  20%  {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
  100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
@-webkit-keyframes ripple-shadow {
  0%   {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
  20%  {box-shadow: 0 4px 16px rgba(0,0,0,0.3);}
  100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);}
}
@keyframes ripple {
  to {transform: scale(24); opacity:0;}
}
@-webkit-keyframes ripple {
  to {-webkit-transform: scale(24); opacity:0;}
}   

jQuery

jQuery(function($) {
  // MAD-RIPPLE // (jQ+CSS)
  $(document).on("mousedown", "[data-ripple]", function(e) {
    var $self = $(this);
    if($self.is(".btn-disabled")) {
      return;
    }
    if($self.closest("[data-ripple]")) {
      e.stopPropagation();
    }
    var initPos = $self.css("position"),
        offs = $self.offset(),
        x = e.pageX - offs.left,
        y = e.pageY - offs.top,
        dia = Math.min(this.offsetHeight, this.offsetWidth, 100), // start diameter
        $ripple = $('<div/>', {class : "ripple",appendTo : $self });
    if(!initPos || initPos==="static") {
      $self.css({position:"relative"});
    }
    $('<div/>', {
      class : "rippleWave",
      css : {
        background: $self.data("ripple"),
        width: dia,
        height: dia,
        left: x - (dia/2),
        top: y - (dia/2),
      },
      appendTo : $ripple,
      one : {
        animationend : function(){
          $ripple.remove();
        }
      }
    });
  });
});    

Вот полнофункциональная демонстрация:

jQuery(function($) { 
 
  // MAD-RIPPLE // (jQ+CSS) 
  $(document).on("mousedown", "[data-ripple]", function(e) { 
     
    var $self = $(this); 
     
    if($self.is(".btn-disabled")) { 
      return; 
    } 
    if($self.closest("[data-ripple]")) { 
      e.stopPropagation(); 
    } 
     
    var initPos = $self.css("position"), 
        offs = $self.offset(), 
        x = e.pageX - offs.left, 
        y = e.pageY - offs.top, 
        dia = Math.min(this.offsetHeight, this.offsetWidth, 100), // start diameter 
        $ripple = $('<div/>', {class : "ripple",appendTo : $self }); 
     
    if(!initPos || initPos==="static") { 
      $self.css({position:"relative"}); 
    } 
     
    $('<div/>', { 
      class : "rippleWave", 
      css : { 
        background: $self.data("ripple"), 
        width: dia, 
        height: dia, 
        left: x - (dia/2), 
        top: y - (dia/2), 
      }, 
      appendTo : $ripple, 
      one : { 
        animationend : function(){ 
          $ripple.remove(); 
        } 
      } 
    }); 
  }); 
 
});
*{box-sizing:border-box; -webkit-box-sizing:border-box;} 
html, body{height:100%; margin:0;} 
body{background:#f5f5f5; font: 14px/20px Roboto, sans-serif;} 
h1, h2{font-weight: 300;} 
 
 
/* MAD-RIPPLE EFFECT */ 
.ripple{ 
  position: absolute; 
  top:0; left:0; bottom:0; right:0; 
  overflow: hidden; 
  -webkit-transform: translateZ(0); /* to contain zoomed ripple */ 
  transform: translateZ(0); 
  border-radius: inherit; /* inherit from parent (rounded buttons etc) */ 
  pointer-events: none; /* allow user interaction */ 
          animation: ripple-shadow 0.4s forwards; 
  -webkit-animation: ripple-shadow 0.4s forwards; 
} 
.rippleWave{ 
  backface-visibility: hidden; 
  position: absolute; 
  border-radius: 50%; 
  transform: scale(0.7); -webkit-transform: scale(0.7); 
  background: rgba(255,255,255, 1); 
  opacity: 0.45; 
          animation: ripple 2s forwards; 
  -webkit-animation: ripple 2s forwards; 
} 
@keyframes ripple-shadow { 
  0%   {box-shadow: 0 0 0 rgba(0,0,0,0.0);} 
  20%  {box-shadow: 0 4px 16px rgba(0,0,0,0.3);} 
  100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);} 
} 
@-webkit-keyframes ripple-shadow { 
  0%   {box-shadow: 0 0 0 rgba(0,0,0,0.0);} 
  20%  {box-shadow: 0 4px 16px rgba(0,0,0,0.3);} 
  100% {box-shadow: 0 0 0 rgba(0,0,0,0.0);} 
} 
@keyframes ripple { 
  to {transform: scale(24); opacity:0;} 
} 
@-webkit-keyframes ripple { 
  to {-webkit-transform: scale(24); opacity:0;} 
} 
 
 
/* MAD-BUTTONS (demo) */ 
[class*=mad-button-]{ 
  display:inline-block; 
  text-align:center; 
  position: relative; 
  margin: 0; 
  white-space: nowrap; 
  vertical-align: middle; 
  font-family: "Roboto", sans-serif; 
  font-size: 14px; 
  font-weight: 500; 
  text-transform: uppercase; 
  text-decoration: none; 
  border: 0; outline: 0; 
  background: none; 
  transition: 0.3s; 
  cursor: pointer; 
  color: rgba(0,0,0, 0.82); 
} 
[class*=mad-button-] i.material-icons{ 
  vertical-align:middle; 
  padding:0; 
} 
.mad-button-raised{ 
  height: 36px; 
  padding: 0px 16px; 
  line-height: 36px; 
  border-radius: 2px; 
  box-shadow: /*amb*/ 0 0   2px rgba(0,0,0,0.15), 
    /*key*/ 0 1px 3px rgba(0,0,0,0.25); 
}.mad-button-raised:hover{ 
  box-shadow: /*amb*/ 0 0   2px rgba(0,0,0,0.13), 
    /*key*/ 0 2px 4px rgba(0,0,0,0.2); 
} 
.mad-button-action{ 
  width: 56px; height:56px; 
  padding: 16px 0; 
  border-radius: 32px; 
  box-shadow: /*amb*/ 0 0   2px rgba(0,0,0,0.13), 
    /*key*/ 0 5px 7px rgba(0,0,0,0.2); 
}.mad-button-action:hover{ 
  box-shadow: /*amb*/ 0 0   2px rgba(0,0,0,0.11), 
    /*key*/ 0 6px 9px rgba(0,0,0,0.18); 
} 
[class*=mad-button-].mad-ico-left  i.material-icons{ margin: 0 8px 0 -4px; } 
[class*=mad-button-].mad-ico-right i.material-icons{ margin: 0 -4px 0 8px; } 
 
/* MAD-COLORS */ 
.bg-primary-darker{background:#1976D2; color:#fff;} 
.bg-primary{ background:#2196F3; color:#fff; } 
.bg-primary.lighter{ background: #BBDEFB; color: rgba(0,0,0,0.82);} 
.bg-accented{ background:#FF4081; color:#fff; } 
 
/* MAD-CELL */ 
.cell{padding: 8px 16px; overflow:auto;}
<link href='https://fonts.googleapis.com/css?family=Roboto:500,400,300&amp;subset=latin,latin-ext' rel='stylesheet' type='text/css'> 
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> 
<script src="https://code.jquery.com/jquery-2.1.4.js"></script> 
 
<div class="cell"> 
  <button data-ripple class="mad-button-raised mad-ico-left bg-primary"><i class="material-icons">person</i>User settings</button> 
  <a data-ripple href="#" class="mad-button-action bg-accented"><i class="material-icons">search</i></a> 
</div> 
 
<div data-ripple class="cell bg-primary-darker"> 
  <h1>Click to Ripple</h1> 
  <p>data-ripple</p> 
</div> 
 
<div data-ripple="rgba(0,0,0, 0.4)" class="cell bg-primary"> 
  <p>data-ripple="rgba(0,0,0, 0.4)"</p> 
  <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore....</p> 
  <p><a data-ripple class="mad-button-raised mad-ico-right bg-accented">Edit<i class="material-icons">edit</i></a></p> 
</div>

Перевод ответа: How to create Ripple effect on Click - Material Design @Roko C. Buljan

Answer 2

Вариант с CSS-переменными, которые используются для раздельного использования свойства transform. Разделять это свойство понадобилось для того, чтобы правильно перемещать элемент (translate3d) и независимо от перемещения масштабировать (scale). Через JS так можно очень удобно управлять этими параметрами.

С точки зрения производительности это достаточно хорошая анимация такого эффекта, поскольку не делаются вставки и удаления узлов в DOM и вся работа происходит исключительно со свойствами transform и opacity, которые не влияют на композитный слой, а значит не вызывают множество перерисовок страницы.

let span = document.querySelector('button span'); 
 
document.querySelector('button').addEventListener('click', function(e) { 
  span.style.setProperty('--x', e.clientX - this.getBoundingClientRect().left - span.offsetWidth/2 + 'px'); 
  span.style.setProperty('--y', e.clientY - this.getBoundingClientRect().top - span.offsetHeight/2 + 'px'); 
   
  let scaleCount = 0, 
      opacityCount = 1; 
       
  const animationTime = 500; 
 
  let scaleUp = setInterval(function() { 
    scaleCount += 0.25; 
    span.style.setProperty('--scale', scaleCount); 
     
    opacityCount -= 0.05; 
    span.style.opacity = opacityCount; 
  }, animationTime / 20); 
 
  setTimeout(function() { 
    clearInterval(scaleUp); 
    span.style.setProperty('--scale', 0); 
  }, animationTime); 
});
body { 
  --x: 0px; 
  --y: 0px; 
  --scale: 0; 
} 
 
button { 
  padding: 10px 20px; 
  position: relative; 
  overflow: hidden; 
  border: 0; 
  border-radius: 3px; 
  background: linear-gradient(#c4e2fa, #c4effa); 
  outline: 0; 
  font: bold 17px arial; 
  text-shadow: 0 0 3px rgba(0,0,0,0.3); 
  color: #FFF; 
} 
 
button:hover { 
  background: linear-gradient(#c4e2fa, #c4e8fa); 
} 
 
span { 
  position: absolute; 
  width: 30px; 
  height: 30px; 
  border-radius: 50%; 
  background-color: rgba(0, 0, 0, 0.3); 
  top: 0; 
  left: 0; 
  transform: translate3d(var(--x), var(--y), 0) scale(var(--scale)); 
}
<button>This is text<span></span></button>

Answer 3

Такая анимация может быть достигнута с помощью box-shadows. Размещение начала окружности под курсором мышки, при щелчке мышью потребуется JS.

li{ 
    font-size:2em; 
    background:rgba(51, 51, 254, 0.8); 
    list-style-type:none; 
    display:inline-block; 
    line-height:2em; 
    width:6em; 
    text-align:center; 
    color:#fff; 
    position:relative; 
    overflow:hidden; 
} 
a{color:#fff;} 
a:after{ 
    content:''; 
    position:absolute; 
    border-radius:50%; 
    height:10em; width:10em; 
    top: -4em; left:-2em; 
    box-shadow: inset 0 0 0 5em rgba(255,255,255,0.2); 
    transition: box-shadow 0.8s; 
} 
a:focus:after{ 
    box-shadow: inset 0 0 0 0em rgba(255,255,255,0.2); 
}
<ul> 
    <li><a href="#">button</a></li> 
</ul>

Перевод ответа: How to create Ripple effect on Click - Material Design @web-tiki

Answer 4

Я использовал этот код раньше в нескольких моих проектах.

Используя jQuery, мы можем поместить эффект на него не просто статически, но и затем добавить на элемент span onclick.

Я добавил комментарии, чтобы было проще понять код.

Demo Here

jQuery

$("div").click(function (e) {
  // Remove any old one
  $(".ripple").remove();
  // Setup
  var posX = $(this).offset().left,
      posY = $(this).offset().top,
      buttonWidth = $(this).width(),
      buttonHeight =  $(this).height();
  // Add the element
  $(this).prepend("<span class='ripple'></span>");

 // Make it round!
  if(buttonWidth >= buttonHeight) {
    buttonHeight = buttonWidth;
  } else {
    buttonWidth = buttonHeight; 
  }
  // Get the center of the element
  var x = e.pageX - posX - buttonWidth / 2;
  var y = e.pageY - posY - buttonHeight / 2;

  // Add the ripples CSS and start the animation
  $(".ripple").css({
    width: buttonWidth,
    height: buttonHeight,
    top: y + 'px',
    left: x + 'px'
  }).addClass("rippleEffect");
});   

CSS

.ripple {
  width: 0;
  height: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.4);
  transform: scale(0);
  position: absolute;
  opacity: 1;
}
.rippleEffect {
    animation: rippleDrop .6s linear;
}
@keyframes rippleDrop {
  100% {
    transform: scale(2);
    opacity: 0;
  }
}   

Перевод ответа: How to create Ripple effect on Click - Material Design @Ruddy

Answer 5

Зачем нужен "велосипед"? Я просто взял библиотеку MDL от Google, взял из нее и ripple и другое, а в конце исследовал ее и удалил из нее все, что не пригодилось, - для оптимизации.
Так проще.

READ ALSO
C# POST запрос с несколькими параметрами

C# POST запрос с несколькими параметрами

Пример запроса на сервер:

290
С# Программа по бзе данных Sql server [требует правки]

С# Программа по бзе данных Sql server [требует правки]

Кто может помочь с кодом кнопки добавления записси в бд?

174
Загрузить БД на удаленный сервер

Загрузить БД на удаленный сервер

Есть программа на c# : сама программа + база данных созданная в MS SQL Server Manager StudioНа данный момент БД находится на моем ПК (Windows) но в дальнейшем...

155
Использует ли .NET WinApi

Использует ли .NET WinApi

Можно ли сказать чтоnet это обертка над языками с/с++? Поскольку например внутренно тот же класс File наверно вызывает WinApi функции для создания/удаления...

149