paper-spinner-styles.html 12.1 KB
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<dom-module id="paper-spinner-styles">
  <template>
    <style>
      /*
      /**************************/
      /* STYLES FOR THE SPINNER */
      /**************************/

      /*
       * Constants:
       *      STROKEWIDTH = 3px
       *      ARCSIZE     = 270 degrees (amount of circle the arc takes up)
       *      ARCTIME     = 1333ms (time it takes to expand and contract arc)
       *      ARCSTARTROT = 216 degrees (how much the start location of the arc
       *                                should rotate each time, 216 gives us a
       *                                5 pointed star shape (it's 360/5 * 3).
       *                                For a 7 pointed star, we might do
       *                                360/7 * 3 = 154.286)
       *      CONTAINERWIDTH = 28px
       *      SHRINK_TIME = 400ms
       */

      :host {
        display: inline-block;
        position: relative;
        width: 28px; /* CONTAINERWIDTH */
        height: 28px; /* CONTAINERWIDTH */
      }

      #spinnerContainer {
        width: 100%;
        height: 100%;

        /* The spinner does not have any contents that would have to be
         * flipped if the direction changes. Always use ltr so that the
         * style works out correctly in both cases. */
        direction: ltr;
      }

      #spinnerContainer.active {
        /* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
        -webkit-animation: container-rotate 1568ms linear infinite;
        animation: container-rotate 1568ms linear infinite;
      }

      @-webkit-keyframes container-rotate {
        to { -webkit-transform: rotate(360deg) }
      }

      @keyframes container-rotate {
        to { transform: rotate(360deg) }
      }

      .spinner-layer {
        position: absolute;
        width: 100%;
        height: 100%;
        opacity: 0;
        white-space: nowrap;
        border-color: var(--paper-spinner-color, --google-blue-500);
      }

      .layer-1 {
        border-color: var(--paper-spinner-layer-1-color, --google-blue-500);
      }

      .layer-2 {
        border-color: var(--paper-spinner-layer-2-color, --google-red-500);
      }

      .layer-3 {
        border-color: var(--paper-spinner-layer-3-color, --google-yellow-500);
      }

      .layer-4 {
        border-color: var(--paper-spinner-layer-4-color, --google-green-500);
      }

      /**
       * IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
       *
       * iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
       * guarantee that the animation will start _exactly_ after that value. So we avoid using
       * animation-delay and instead set custom keyframes for each color (as layer-2undant as it
       * seems).
       *
       * We write out each animation in full (instead of separating animation-name,
       * animation-duration, etc.) because under the polyfill, Safari does not recognize those
       * specific properties properly, treats them as -webkit-animation, and overrides the
       * other animation rules. See https://github.com/Polymer/platform/issues/53.
       */
      .active .spinner-layer {
        /* durations: 4 * ARCTIME */
        -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        opacity: 1;
      }

      .active .spinner-layer.layer-1 {
        /* durations: 4 * ARCTIME */
        -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-1-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      .active .spinner-layer.layer-2 {
        /* durations: 4 * ARCTIME */
        -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-2-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      .active .spinner-layer.layer-3 {
        /* durations: 4 * ARCTIME */
        -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-3-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      .active .spinner-layer.layer-4 {
        /* durations: 4 * ARCTIME */
        -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both, layer-4-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      @-webkit-keyframes fill-unfill-rotate {
        12.5% { -webkit-transform: rotate(135deg);  } /* 0.5 * ARCSIZE */
        25%   { -webkit-transform: rotate(270deg);  } /* 1   * ARCSIZE */
        37.5% { -webkit-transform: rotate(405deg);  } /* 1.5 * ARCSIZE */
        50%   { -webkit-transform: rotate(540deg);  } /* 2   * ARCSIZE */
        62.5% { -webkit-transform: rotate(675deg);  } /* 2.5 * ARCSIZE */
        75%   { -webkit-transform: rotate(810deg);  } /* 3   * ARCSIZE */
        87.5% { -webkit-transform: rotate(945deg);  } /* 3.5 * ARCSIZE */
        to    { -webkit-transform: rotate(1080deg); } /* 4   * ARCSIZE */
      }

      @keyframes fill-unfill-rotate {
        12.5% { transform: rotate(135deg);  } /* 0.5 * ARCSIZE */
        25%   { transform: rotate(270deg);  } /* 1   * ARCSIZE */
        37.5% { transform: rotate(405deg);  } /* 1.5 * ARCSIZE */
        50%   { transform: rotate(540deg);  } /* 2   * ARCSIZE */
        62.5% { transform: rotate(675deg);  } /* 2.5 * ARCSIZE */
        75%   { transform: rotate(810deg);  } /* 3   * ARCSIZE */
        87.5% { transform: rotate(945deg);  } /* 3.5 * ARCSIZE */
        to    { transform: rotate(1080deg); } /* 4   * ARCSIZE */
      }

      /**
       * HACK: Even though the intention is to have the current .spinner-layer at
       * `opacity: 1`, we set it to `opacity: 0.99` instead since this forces Chrome
       * to do proper subpixel rendering for the elements being animated. This is
       * especially visible in Chrome 39 on Ubuntu 14.04. See:
       *
       * - https://github.com/Polymer/paper-spinner/issues/9
       * - https://code.google.com/p/chromium/issues/detail?id=436255
       */
      @-webkit-keyframes layer-1-fade-in-out {
        from { opacity: 0.99; }
        25% { opacity: 0.99; }
        26% { opacity: 0; }
        89% { opacity: 0; }
        90% { opacity: 0.99; }
        100% { opacity: 0.99; }
      }

      @keyframes layer-1-fade-in-out {
        from { opacity: 0.99; }
        25% { opacity: 0.99; }
        26% { opacity: 0; }
        89% { opacity: 0; }
        90% { opacity: 0.99; }
        100% { opacity: 0.99; }
      }

      @-webkit-keyframes layer-2-fade-in-out {
        from { opacity: 0; }
        15% { opacity: 0; }
        25% { opacity: 0.99; }
        50% { opacity: 0.99; }
        51% { opacity: 0; }
      }

      @keyframes layer-2-fade-in-out {
        from { opacity: 0; }
        15% { opacity: 0; }
        25% { opacity: 0.99; }
        50% { opacity: 0.99; }
        51% { opacity: 0; }
      }

      @-webkit-keyframes layer-3-fade-in-out {
        from { opacity: 0; }
        40% { opacity: 0; }
        50% { opacity: 0.99; }
        75% { opacity: 0.99; }
        76% { opacity: 0; }
      }

      @keyframes layer-3-fade-in-out {
        from { opacity: 0; }
        40% { opacity: 0; }
        50% { opacity: 0.99; }
        75% { opacity: 0.99; }
        76% { opacity: 0; }
      }

      @-webkit-keyframes layer-4-fade-in-out {
        from { opacity: 0; }
        65% { opacity: 0; }
        75% { opacity: 0.99; }
        90% { opacity: 0.99; }
        100% { opacity: 0; }
      }

      @keyframes layer-4-fade-in-out {
        from { opacity: 0; }
        65% { opacity: 0; }
        75% { opacity: 0.99; }
        90% { opacity: 0.99; }
        100% { opacity: 0; }
      }

      /**
       * Patch the gap that appear between the two adjacent div.circle-clipper while the
       * spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
       *
       * Update: the gap no longer appears on Chrome when .spinner-layer's opacity is 0.99,
       * but still does on Safari and IE.
       */
      .gap-patch {
        position: absolute;
        box-sizing: border-box;
        top: 0;
        left: 45%;
        width: 10%;
        height: 100%;
        overflow: hidden;
        border-color: inherit;
      }

      .gap-patch .circle {
        width: 1000%;
        left: -450%;
      }

      .circle-clipper {
        display: inline-block;
        position: relative;
        width: 50%;
        height: 100%;
        overflow: hidden;
        border-color: inherit;
      }

      .circle-clipper .circle {
        width: 200%;
      }

      .circle {
        box-sizing: border-box;
        height: 100%;
        border-width: 3px; /* STROKEWIDTH */
        border-style: solid;
        border-color: inherit;
        border-bottom-color: transparent !important;
        border-radius: 50%;
        -webkit-animation: none;
        animation: none;

        @apply(--layout-fit);
      }

      .circle-clipper.left .circle {
        border-right-color: transparent !important;
        -webkit-transform: rotate(129deg);
        transform: rotate(129deg);
      }

      .circle-clipper.right .circle {
        left: -100%;
        border-left-color: transparent !important;
        -webkit-transform: rotate(-129deg);
        transform: rotate(-129deg);
      }

      .active .circle-clipper.left .circle {
        /* duration: ARCTIME */
        -webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: left-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      .active .circle-clipper.right .circle {
        /* duration: ARCTIME */
        -webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
        animation: right-spin 1333ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
      }

      @-webkit-keyframes left-spin {
        from { -webkit-transform: rotate(130deg); }
        50% { -webkit-transform: rotate(-5deg); }
        to { -webkit-transform: rotate(130deg); }
      }

      @keyframes left-spin {
        from { transform: rotate(130deg); }
        50% { transform: rotate(-5deg); }
        to { transform: rotate(130deg); }
      }

      @-webkit-keyframes right-spin {
        from { -webkit-transform: rotate(-130deg); }
        50% { -webkit-transform: rotate(5deg); }
        to { -webkit-transform: rotate(-130deg); }
      }

      @keyframes right-spin {
        from { transform: rotate(-130deg); }
        50% { transform: rotate(5deg); }
        to { transform: rotate(-130deg); }
      }

      #spinnerContainer.cooldown {
        /* duration: SHRINK_TIME */
        -webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
        animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0.0, 0.2, 1);
      }

      @-webkit-keyframes fade-out {
        from { opacity: 0.99; }
        to { opacity: 0; }
      }

      @keyframes fade-out {
        from { opacity: 0.99; }
        to { opacity: 0; }
      }
    </style>
  </template>
</dom-module>