mcasimir / mobile-angular-ui

Angular.js Mobile UI Framework with Bootstrap 3
http://mobileangularui.com/
MIT License
2.87k stars 709 forks source link

Suggestion: Tinder style swipe #11

Closed AnthonyButcher closed 10 years ago

AnthonyButcher commented 10 years ago

I am not sure if you are open to suggestions or even whether this would fit in, but a Tinder-style swipe, with a nice bit of animation, for swiping left/right and up/down would be very nice.

This interaction works very well on mobile devices allowing users to sift through multiple visual 'cards' quickly.

Example CSS here: http://smotko.si/tinder-css/

mcasimir commented 10 years ago

Sure i'm interested in any kind of feedback. There is a dedicated issue for this also: #4

Speaking of Tinder i didn't know it. Are a sort of touch-enabled carousel? Maybe i can re-embed bootstrap carousel with ng-swipe-left/right directives. Would it be enough?

AnthonyButcher commented 10 years ago

Sorry, will add future suggestions to the roadmap.

Tinder allows people to flick through small profile pages very quickly. Each profile card has a photo, name, age and some other info. Elements of it are clickable.

When dragged with a swipe, images follow your finger, but also 'curve' off. Swiping is also linked to triggering an image within the card saying Nope or Liked.

Hopefully these images will illustrate this:

2014-03-10 13 48 42 2014-03-10 13 49 40 2014-03-10 13 50 46

2014-03-10 13 47 22

mcasimir commented 10 years ago

Ok I've re-added carousel component. To create such interaction you can use the $swipe service that is available through mobile-angular-ui.touch.

Taking a cue from your suggestion i created an example on how you can build a touch friendly carousel with a customized interaction. With a small effort you could adjust it to make it behave like tinder.

Obviously you need to permit the movement on Y axis as well and check wether the item overlaps one of the two buttons and trigger proper actions accordingly.

This is the basic code for the demo: http://mobileangularui.com/demo/#/carousel.

<div id="carousel-example" class="carousel">
      <div class="carousel-inner">
        <div href="" class="item active" carousel-example-item>
          <div class="carousel-example-content">1
            <br/><small>Swipe me!</small>
          </div>
        </div>
        <div href="" class="item" carousel-example-item>
          <div class="carousel-example-content">2
            <br/><small>Swipe me!</small>
          </div>
        </div>
        <div href="" class="item" carousel-example-item>
          <div class="carousel-example-content">3
            <br/><small>Swipe me!</small>
          </div>
        </div>
      </div>
</div>

Lets define the carouselExampleItem directive.

app.directive( "carouselExampleItem", function($rootScope, $swipe){
  return function(scope, element, attrs){
      var startX = null;
      var startY = null;
      var endAction = "cancel";
      var carouselId = element.parent().parent().attr("id");

      var translateAndRotate = function(x, y, z, deg){
        element[0].style["-webkit-transform"] =
           "translate3d("+x+"px,"+ y +"px," + z + "px) rotate("+ deg +"deg)";
        element[0].style["-moz-transform"] =
           "translate3d("+x+"px," + y +"px," + z + "px) rotate("+ deg +"deg)";
        element[0].style["-ms-transform"] =
           "translate3d("+x+"px," + y + "px," + z + "px) rotate("+ deg +"deg)";
        element[0].style["-o-transform"] =
           "translate3d("+x+"px," + y  + "px," + z + "px) rotate("+ deg +"deg)";
        element[0].style["transform"] =
           "translate3d("+x+"px," + y + "px," + z + "px) rotate("+ deg +"deg)";
      }

      $swipe.bind(element, {
        start: function(coords) {
          startX = coords.x;
          startY = coords.y;
        },

        cancel: function(e) {
          translateAndRotate(0, 0, 0, 0);
          e.stopPropagation();
        },

        end: function(coords, e) {
          if (endAction == "prev") {
            $rootScope.carouselPrev(carouselId);
          } else if (endAction == "next") {
            $rootScope.carouselNext(carouselId);
          }
          translateAndRotate(0, 0, 0, 0);
          e.stopPropagation();
        },

        move: function(coords) {
          if( startX != null) {
            var deltaX = coords.x - startX;
            var deltaXRatio = deltaX / element[0].clientWidth;
            if (deltaXRatio > 0.3) {
              endAction = "next";
            } else if (deltaXRatio < -0.3){
              endAction = "prev";
            }
            translateAndRotate(deltaXRatio * 200, 0, 0, deltaXRatio * 15);
          }
        }
      });
    }
});