Angular 1 到 Angular 2 指令涟漪效应

Angular 1 to Angular 2 Directive Ripple Effect

任何人都可以指导如何将下面的 Angular 1 指令移植到 Angular 2:

(function() {
  'use strict';

  angular.module('whimsicalRipple', [])
    .config(function() {
      var styleEl = document.createElement('style'),
        styleSheet,
        rippleCSS,
        rippleLightCSS,
        rippleKeyframes,
        rippleWebkitKeyframes;

      rippleCSS = [
        '-webkit-animation: ripple 800ms ease-out;',
        'animation: ripple 800ms ease-out;',
        'background-color: rgba(0, 0, 0, 0.16);',
        'border-radius: 100%;',
        'height: 10px;',
        'pointer-events: none;',
        'position: absolute;',
        'transform: scale(0);',
        'width: 10px;'
      ];

      rippleLightCSS = 'background-color: rgba(255, 255, 255, 0.32);';

      rippleKeyframes = [
        '@keyframes ripple {',
          'to {',
            'transform: scale(2);',
            'opacity: 0;',
          '}',
        '}'
      ];

      rippleWebkitKeyframes = [
        '@-webkit-keyframes ripple {',
          'to {',
            '-webkit-transform: scale(2);',
            'opacity: 0;',
          '}',
        '}'
      ];

      document.head.appendChild(styleEl);
      styleSheet = styleEl.sheet;
      styleSheet.insertRule('.ripple-effect {' + rippleCSS.join('') + '}', 0);
      styleSheet.insertRule('.ripple-light .ripple-effect {' + rippleLightCSS  + '}', 0);

      if (CSSRule.WEBKIT_KEYFRAMES_RULE) { // WebKit
        styleSheet.insertRule(rippleWebkitKeyframes.join(''), 0);
      }
      else if (CSSRule.KEYFRAMES_RULE) { // W3C
        styleSheet.insertRule(rippleKeyframes.join(''), 0);
      }
    })
    .directive('ripple', function() {
      return {
        restrict: 'C',
        link: function(scope, element, attrs) {
          element[0].style.position = 'relative';
          element[0].style.overflow = 'hidden';
          element[0].style.userSelect = 'none';

          element[0].style.msUserSelect = 'none';
          element[0].style.mozUserSelect = 'none';
          element[0].style.webkitUserSelect = 'none';


          function createRipple(evt) {
            var ripple = angular.element('<span class="ripple-effect animate">'),
              rect = element[0].getBoundingClientRect(),
              radius = Math.max(rect.height, rect.width),
              left = evt.pageX - rect.left - radius / 2 - document.body.scrollLeft,
              top = evt.pageY - rect.top - radius / 2 - document.body.scrollTop;

            ripple[0].style.width = ripple[0].style.height = radius + 'px';
            ripple[0].style.left = left + 'px';
            ripple[0].style.top = top + 'px';
            ripple.on('animationend webkitAnimationEnd', function() {
              angular.element(this).remove();
            });

            element.append(ripple);
          }

          element.on('click', createRipple);
        }
      };
    });
})();

来源:https://github.com/pioug/angular-whimsical-ripple/blob/master/whimsicalRipple.js

有几种方法可以做到这一点。其中之一是使用组件,因为它允许在组件装饰器的 styles 属性 中设置样式。

这样angular1的配置部分就完全移到了组件的styles 属性:

@Component({
  selector: '.ripple',
  styles: [`
      :host {
        display: inline-block;
        position: relative;
        overflow: hidden;
        -webkit-user-select: none; 
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        cursor: pointer;
      }


      @-webkit-keyframes ripple {
          to {
            -webkit-transform: scale(2);
            opacity: 0;
          }
      }

      @keyframes ripple {
          to {
            transform: scale(2);
            opacity: 0;
          }
      }

      :host /deep/ .ripple-effect {
        -webkit-animation: ripple 800ms ease-out;
        animation: ripple 800ms ease-out;
        background-color: rgba(0, 0, 0, 0.16);
        border-radius: 100%;
        height: 10px;
        pointer-events: none;
        position: absolute;
        transform: scale(0);
        width: 10px;
      }

      :host /deep/ .ripple-light .ripple-effect {
        background-color: rgba(255, 255, 255, 0.32);
      }  
  `],
  template: '<ng-content></ng-content>',
})
export class RippleComponent {
  ...
}

另见

第二步处理点击事件。为此,angular2 中有 HostListener 装饰器。没有 angular.element 的等价物,所以我只使用 DOM API (document.createElement, element.addEventListener):

export class RippleComponent {
  @HostListener('click', ['$event', '$event.currentTarget'])
  click(event, element) {
    var ripple = document.createElement('span'),
      rect = element.getBoundingClientRect(),
      radius = Math.max(rect.height, rect.width),
      left = event.pageX - rect.left - radius / 2 - document.body.scrollLeft,
      top = event.pageY - rect.top - radius / 2 - document.body.scrollTop;

    ripple.className = 'ripple-effect animate';
    ripple.style.width = ripple.style.height = radius + 'px';
    ripple.style.left = left + 'px';
    ripple.style.top = top + 'px';
    ripple.addEventListener('animationend', () => {
      element.removeChild(ripple);
    });

    element.appendChild(ripple);
  }
}

Plunker Example

Plunker Example with image