annsonn/fancy-carousel

View on GitHub
fancy-carousel-animations.html

Summary

Maintainability
Test Coverage
<script>
  FancyCarouselAnimations = (superClass) => class extends superClass {
  
    connectedCallback() {
      if(super.connectedCallback) {
        super.connectedCallback();
      }
      this._preloadAnimationSprites();
    }

    ready() {
      this.addEventListener('transitionend', this._resetChildrenStyles);
      this.addEventListener('touchstart', this._touchstart);
      this.addEventListener('touchmove', this._touchmove);
      this.addEventListener('touchend', this._touchend);

      super.ready();
    }

    _getNatureSpriteUrl() {
        return "url('" + this.resolveUrl('images/nature-sprite.png') + "')";
    }

    _getUrbanSpriteUrl() {
        return "url('" + this.resolveUrl('images/urban-sprite.png') + "')";
    }

    _getShiftSpriteUrl() {
        return "url('" + this.resolveUrl('images/building-sprite.png') + "')";
    }

    _getCollaspeSpriteUrl() {
        return "url('" + this.resolveUrl('images/building-sprite-2.png') + "')";
    }

    _preloadAnimationSprites() {
      var spriteElem = document.createElement('div');

      if (this.transitionType === 'spread') {
        spriteElem.style.backgroundImage = this._getNatureSpriteUrl();
      } else if (this.transitionType === 'paint') {
        spriteElem.style.backgroundImage = this._getUrbanSpriteUrl();
      } else if (this.transitionType === 'collapse') {
        spriteElem.style.backgroundImage = this._getShiftSpriteUrl();
      } else if (this.transitionType === 'shift') {
        spriteElem.style.backgroundImage = this._getCollaspeSpriteUrl();
      }
        
      spriteElem.className = "preloaded-sprite";
      spriteElem.style.visibility = "hidden";
      Polymer.dom(this).appendChild(spriteElem);
    }

    _resetZIndex(elements, current) {
        for (var i = 0; i < elements.length; i++) {
          if (current && current.src === elements[i].src) {
            elements[i].style.zIndex = "20";
          } else {
            elements[i].style.zIndex = "10";
          }
        }
    }

    _startTransition(direction, selected, nextElem) {
        if (this.transitionType === 'spread' ||
            this.transitionType === 'paint' ||
            this.transitionType === 'shift' ||
            this.transitionType === 'collapse') {
          this._animateTransition(direction, selected, nextElem);
        } else {
          if (this._isWorking) return;
          this._isWorking = true;

          nextElem.style.zIndex = "50";

          var oldSelected = selected;
          this._translateX(oldSelected, 0);
          this._translateX(nextElem, this.offsetWidth*direction);

          // Start the transition
          this.selected = nextElem;
          this._translateX(oldSelected, -this.offsetWidth*direction, true /* transition */);
          this._translateX(nextElem, 0, true /* transition */);

          this._resetZIndex(Polymer.dom(this).children, selected);
          nextElem.style.zIndex = "100";

          this._isWorking = false;
        }
      }

      _animateTransition(direction, selected, nextElem) {
        var self = this;

        if (self._isWorking) return;
        self._isWorking = true;

        selected.style.zIndex = "30";
        nextElem.style.zIndex = "20";
                
        var spriteUrl;
        if (this.transitionType === 'spread') {
          spriteUrl = this._getNatureSpriteUrl();
        } else if (this.transitionType === 'paint') {
          spriteUrl = this._getUrbanSpriteUrl();
        } else if (this.transitionType === 'collapse') {
          spriteUrl = this._getCollaspeSpriteUrl();
        } else if (this.transitionType === 'shift') {
          spriteUrl = this._getShiftSpriteUrl();
        }

        self._updateSelectedStyles(selected, this.transitionType, spriteUrl, '1400ms', '0ms', 'mask-play');

        setTimeout(function() {
          self._updateSelectedStyles(selected, '', '', '', '', '');
          
          selected.style.zIndex = "10";
          nextElem.style.zIndex = "30"

          self.selected = nextElem;
          self._isWorking = false;
        }, 1400);
      }

      _updateSelectedStyles(element, className, spriteUrl, animationDuration, animationDelay, animationName) {
        element.className = className;
        element.style.mask = spriteUrl;
        element.style.webkitMask = spriteUrl;
        element.style.webkitMaskImage = spriteUrl;
        element.style.animationName = animationName;
      }

      _resetChildrenStyles() {
        var elem = this.firstElementChild;
        while (elem) {
          elem.style.display = '';
          elem.style.transition = '';
          elem.style.transform = '';
          elem = elem.nextElementSibling;
        }
      }

      _translateX(elem, x, transition) {
        elem.style.display = 'block';
        elem.style.transition = transition ? 'transform 0.2s' : '';
        elem.style.transform = 'translate3d(' + x + 'px, 0, 0)';
      }

      _touchstart(event) {
        // No transition if less than two images
        if (this.childElementCount < 2) {
          return;
        }

        // Save start coordinates
        if (!this._touchDir) {
          this._startX = event.changedTouches[0].clientX;
          this._startY = event.changedTouches[0].clientY;
        }
      }

      _touchmove(event) {
        // No transition if less than two images
        if (this.childElementCount < 2) {
          return;
        }

        // Is touchmove mostly horizontal or vertical?
        if (!this._touchDir) {
          var absX = Math.abs(event.changedTouches[0].clientX - this._startX);
          var absY = Math.abs(event.changedTouches[0].clientY - this._startY);
          this._touchDir = absX > absY ? 'x' : 'y';
        }

        if (this._touchDir === 'x') {
          // Prevent vertically scrolling when swiping
          event.preventDefault();

          var dx = Math.round(event.changedTouches[0].clientX - this._startX);
          var prevChild = this.selected.previousElementSibling;
          var nextChild = this.selected.nextElementSibling;

          // Don't translate past the current image if there's no adjacent image in that direction
          if ((!prevChild && dx > 0) || (!nextChild && dx < 0)) {
            dx = 0;
          }

          this._translateX(this.selected, dx);
          if (prevChild) {
            this._translateX(prevChild, dx - this.offsetWidth);
          }
          if (nextChild) {
            this._translateX(nextChild, dx + this.offsetWidth);
          }
        }
      }

      _touchend(event) {
        // No transition if less than two images
        if (this.childElementCount < 2) {
          return;
        }

        // Don't finish swiping if there are still active touches.
        if (event.touches.length) {
          return;
        }

        if (this._touchDir === 'x') {
          var dx = Math.round(event.changedTouches[0].clientX - this._startX);
          var prevChild = this.selected.previousElementSibling;
          var nextChild = this.selected.nextElementSibling;

          // Don't translate past the current image if there's no adjacent image in that direction
          if ((!prevChild && dx > 0) || (!nextChild && dx < 0)) {
            dx = 0;
          }

          if (dx > 0) {
            var prevChild = this.selected.previousElementSibling;
            if (prevChild) {
              if (dx > 100) {
                if (dx === this.offsetWidth) {
                  // No transitionend will fire (since we're already in the final state),
                  // so reset children styles now
                  this._resetChildrenStyles();
                } else {
                  this._translateX(prevChild, 0, true);
                  this._translateX(this.selected, this.offsetWidth, true);
                }
                this.selected = prevChild;
              } else {
                this._translateX(prevChild, -this.offsetWidth, true);
                this._translateX(this.selected, 0, true);
              }
            }
          } else if (dx < 0) {
            var nextChild = this.selected.nextElementSibling;
            if (nextChild) {
              if (dx < -100) {
                if (dx === -this.offsetWidth) {
                  // No transitionend will fire (since we're already in the final state),
                  // so reset children styles now
                  this._resetChildrenStyles();
                } else {
                  this._translateX(this.selected, -this.offsetWidth, true);
                  this._translateX(nextChild, 0, true);
                }
                this.selected = nextChild;
              } else {
                this._translateX(this.selected, 0, true);
                this._translateX(nextChild, this.offsetWidth, true);
              }
            }
          } else {
            // No transitionend will fire (since we're already in the final state),
            // so reset children styles now
            this._resetChildrenStyles();
          }
        }

        // Reset touch direction
        this._touchDir = null;
      }
    
  };
</script>