Skip to content

Instantly share code, notes, and snippets.

@satnami
Created November 12, 2015 09:08
Show Gist options
  • Save satnami/6158a239bf329469eac8 to your computer and use it in GitHub Desktop.
Save satnami/6158a239bf329469eac8 to your computer and use it in GitHub Desktop.
Delivery Card Animation

Delivery Card Animation

Source gif by Ramotion - https://dribbble.com/shots/2121350-Delivery-Card

Looks best in Chrome. Some parts are bugged in FF. Not working in IE below edge (if it's not working in edge, let me know pls). Card dimensions probably broken on mobile, except for iPhone 6 (because I was hardcoding pixels for this model). Don't forget to click request button inside card.

(Cool)story behind the demo:

when I saw this gif I just wanted to recreate flipping animation. I thought that it will be easy-peasy, because I worked with preserve-3d stuff in CSS before. But, when I started doing nested elements below 2nd level, everything turned to a crazy bugfest. Border-radius crazy bugs in various Chrome versions, z-index and translateZ(0) retarded things in FF and other absolutely random stuff. So when I finished flipping part, I thought that I should continue and do the rest. Oh boy... This decision gave another week of suffering and LOTS of shitty code. I will try to refactor it in a few weeks (when my brain will recover), but right now CSS is a one big clusterf*ck (shame on me!). And google maps is an another story, I was working with them for the first time, so it's more like a brainless composition of SO answers and google API docs.

Forked from Nikolay Talanov's Pen Delivery Card Animation.

A Pen by Mohammad Sami AlMouhtaseb on CodePen.

License.

<div class="phone" ng-app="delivcard">
<div class="phone__screen">
<div class="phone__scroll-cont">
<div class="phone__content" ng-controller="DelivCtrl">
<section ng-repeat="card in cards"
class="card theme-{{card.themeColor}}"
data-color="{{card.themeColorHex}}">
<div class="card__map">
<div class="card__map__inner"></div>
</div>
<section class="card__part card__part-1">
<div class="card__part__inner">
<header class="card__header">
<div class="card__header__close-btn"></div>
<span class="card__header__id"># {{card.id}}</span>
<span class="card__header__price">${{card.price}}</span>
</header>
<div class="card__stats" ng-style="{'background-image': 'url({{card.bgImgUrl}})'}">
<div class="card__stats__item card__stats__item--req">
<p class="card__stats__type">Requests</p>
<span class="card__stats__value">{{card.requests}}</span>
</div>
<div class="card__stats__item card__stats__item--pledge">
<p class="card__stats__type">Pledge</p>
<span class="card__stats__value">${{card.pledge}}</span>
</div>
<div class="card__stats__item card__stats__item--weight">
<p class="card__stats__type">Weight</p>
<span class="card__stats__value">{{card.weight}} oz</span>
</div>
</div>
</div>
</section>
<section class="card__part card__part-2">
<div class="card__part__side m--back">
<div class="card__part__inner card__face">
<div class="card__face__colored-side"></div>
<h3 class="card__face__price">${{card.price}}</h3>
<div class="card__face__divider"></div>
<div class="card__face__path"></div>
<div class="card__face__from-to">
<p>{{card.fromStreet}}, {{card.fromCity}}</p>
<p>{{card.toStreet}}, {{card.toCity}}</p>
</div>
<div class="card__face__deliv-date">
{{card.delivDateNoun}}
<p>{{card.delivTime}}</p>
</div>
<div class="card__face__stats card__face__stats--req">
Requests
<p>{{card.requests}}</p>
</div>
<div class="card__face__stats card__face__stats--pledge">
Pledge
<p>${{card.pledge}}</p>
</div>
<div class="card__face__stats card__face__stats--weight">
Weight
<p class="card__face__stats__weight">
<span ng-show="card.weight < 60">Light</span>
<span ng-show="card.weight > 60">Heavy</span>
</p>
</div>
</div>
</div>
<div class="card__part__side m--front">
<div class="card__sender">
<h4 class="card__sender__heading">Sender</h4>
<div class="card__sender__img-cont">
<div class="card__sender__img-cont__inner">
<img ng-src="{{card.senderImg}}" class="card__sender__img" />
</div>
</div>
<div class="card__sender__name-and-rating">
<p class="card__sender__name">{{card.sender}}</p>
<p class="card__sender__rating card__sender__rating-{{card.rating}}">
<span class="card__sender__rating__star">&#9733;</span>
<span class="card__sender__rating__star">&#9733;</span>
<span class="card__sender__rating__star">&#9733;</span>
<span class="card__sender__rating__star">&#9733;</span>
<span class="card__sender__rating__star">&#9733;</span>
<span class="card__sender__rating__count">({{card.ratingCount}})</span>
</p>
<p class="card__sender__address">
{{card.fromStreet}}, {{card.fromCity}}
</p>
</div>
<div class="card__receiver">
<div class="card__receiver__inner">
<div class="card__sender__img-cont">
<div class="card__sender__img-cont__inner">
<img ng-src="{{card.senderImg}}" class="card__sender__img" />
</div>
</div>
<div class="card__sender__name-and-rating">
<p class="card__sender__name">{{card.sender}}</p>
<p class="card__sender__address">
{{card.toStreet}}, {{card.toCity}}
</p>
</div>
</div>
</div>
<div class="card__path-big"></div>
</div>
<div class="card__from-to">
<div class="card__from-to__inner">
<div class="card__text card__text--left">
<p class="card__text__heading">From</p>
<p class="card__text__middle">{{card.fromStreet}}</p>
<p class="card__text__bottom">{{card.fromCity}}</p>
</div>
<div class="card__text card__text--right">
<p class="card__text__heading">To</p>
<p class="card__text__middle">{{card.toStreet}}</p>
<p class="card__text__bottom">{{card.toCity}}</p>
</div>
</div>
</div>
<section class="card__part card__part-3">
<div class="card__part__side m--back"></div>
<div class="card__part__side m--front">
<div class="card__timings">
<div class="card__timings__inner">
<div class="card__text card__text--left">
<p class="card__text__heading">Delivery Date</p>
<p class="card__text__middle">{{card.delivTime}}</p>
<p class="card__text__bottom">{{card.delivTime}}</p>
</div>
<div class="card__text card__text--right">
<p class="card__text__heading">Request Deadline</p>
<p class="card__text__middle">{{card.reqDl}}</p>
</div>
</div>
</div>
<div class="card__timer">60 min 00 sec</div>
<section class="card__part card__part-4">
<div class="card__part__side m--back"></div>
<div class="card__part__side m--front">
<button type="button" class="card__request-btn">
<span class="card__request-btn__text-1">Request</span>
<span class="card__request-btn__text-2">Start</span>
</button>
<p class="card__counter">{{card.requests}} people have sent a request</p>
</div>
</section>
</div>
</section>
</div>
</section>
</section>
</div>
</div>
</div>
</div>
'use strict';
$(document).ready(function() {
var animating = false;
var step1 = 500;
var step2 = 500;
var step3 = 500;
var reqStep1 = 600;
var reqStep2 = 800;
var reqClosingStep1 = 500;
var reqClosingStep2 = 500;
var $scrollCont = $(".phone__scroll-cont");
function initMap($card) {
// my first experience with google maps api, so I have no idea what I'm doing
var latLngFrom = {lat: 40.7878581, lng: -73.9671309};
var latLngTo = {lat: 40.746433, lng: -73.9503613};
var latLngCenter = {
lat: (latLngFrom.lat + latLngTo.lat)/2,
lng: (latLngFrom.lng + latLngTo.lng)/2
};
var themeColor = $card.data("color");
var map = new google.maps.Map($(".card__map__inner", $card)[0], {
zoom: 12,
center: latLngCenter,
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true
});
map.set('styles', [
{
"featureType": "landscape",
"elementType": "geometry",
"stylers": [
{ "hue": "#00ffdd" },
{ "gamma": 1 },
{ "lightness": 100 }
]
},{
"featureType": "road",
"stylers": [
{ "lightness": 60 },
{ "hue": "#006eff" }
]
}
]);
var pinImage = new google.maps.MarkerImage(
"http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + themeColor.slice(1),
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34)
);
var marker = new google.maps.Marker({
position: latLngFrom,
map: map,
title: 'From',
icon: pinImage
});
var marker = new google.maps.Marker({
position: latLngTo,
map: map,
title: 'To',
icon: pinImage
});
var polylineOpts = new google.maps.Polyline({
strokeColor: themeColor,
strokeWeight: 3
});
var rendererOptions = {map: map, polylineOptions: polylineOpts, suppressMarkers: true};
var directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var request = {
origin: latLngFrom,
destination: latLngTo,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
var directionsService = new google.maps.DirectionsService();
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
else {
console.log("wtf")
}
});
};
initMap($(".card"));
$(document).on("click", ".card:not(.active)", function() {
if (animating) return;
animating = true;
var $card = $(this);
var cardTop = $card.position().top;
var scrollTopVal = cardTop - 30;
$card.addClass("flip-step1 active");
$scrollCont.animate({scrollTop: scrollTopVal}, step1);
setTimeout(function() {
$scrollCont.animate({scrollTop: scrollTopVal}, step2);
$card.addClass("flip-step2");
setTimeout(function() {
$scrollCont.animate({scrollTop: scrollTopVal}, step3);
$card.addClass("flip-step3");
setTimeout(function() {
animating = false;
}, step3);
}, step2*0.5);
}, step1*0.65);
});
$(document).on("click", ".card:not(.req-active1) .card__header__close-btn", function() {
if (animating) return;
animating = true;
var $card = $(this).parents(".card");
$card.removeClass("flip-step3 active");
setTimeout(function() {
$card.removeClass("flip-step2");
setTimeout(function() {
$card.removeClass("flip-step1");
setTimeout(function() {
animating = false;
}, step1);
}, step2*0.65);
}, step3/2);
});
$(document).on("click", ".card:not(.req-active1) .card__request-btn", function(e) {
if (animating) return;
animating = true;
var $card = $(this).parents(".card");
var cardTop = $card.position().top;
var scrollTopVal = cardTop - 30;
$card.addClass("req-active1 map-active");
initMap($card);
setTimeout(function() {
$card.addClass("req-active2");
$scrollCont.animate({scrollTop: scrollTopVal}, reqStep2);
setTimeout(function() {
animating = false;
}, reqStep2);
}, reqStep1);
});
$(document).on("click",
".card.req-active1 .card__header__close-btn, .card.req-active1 .card__request-btn",
function() {
if (animating) return;
animating = true;
var $card = $(this).parents(".card");
$card.addClass("req-closing1");
setTimeout(function() {
$card.addClass("req-closing2");
setTimeout(function() {
$card.addClass("no-transition hidden-hack")
$card.css("top");
$card.removeClass("req-closing2 req-closing1 req-active2 req-active1 map-active flip-step3 flip-step2 flip-step1 active");
$card.css("top");
$card.removeClass("no-transition hidden-hack");
animating = false;
}, reqClosingStep2);
}, reqClosingStep1);
});
});
// angular used only for templating, I was too tired to find more lightweight solution
var delivcardDefaultData = [
{id: '2618-3157', price: 25, requests: 5, pledge: 150, weight: 50,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'purple', themeColorHex: '#BA68C8',
bgImgUrl: 'http://i.imgur.com/Ph4WAg8.jpg', rating: 5, ratingCount: 26,
fromStreet: 'W 90th St', fromCity: 'New York, NY 10025',
toStreet: '46th Ave', toCity: 'Woodside, NY 11101',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'},
{id: '2618-3156', price: 37, requests: 7, pledge: 222, weight: 66,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'green', themeColorHex: '#52A43A',
bgImgUrl: 'http://i.imgur.com/wsvrUoO.jpg', rating: 4, ratingCount: 21,
fromStreet: 'W 85th St', fromCity: 'New York, NY 1025',
toStreet: 'E 30th Ave', toCity: 'New York, NY 1001',
delivTime: '07:30 am', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '33 minutes'},
{id: '2618-3155', price: 12, requests: 3, pledge: 80, weight: 20,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'orange', themeColorHex: '#F7AA17',
bgImgUrl: 'http://i.imgur.com/MApIznd.jpg', rating: 5, ratingCount: 15,
fromStreet: 'W 79th St', fromCity: 'New York, NY 1024',
toStreet: 'W 139th Ave', toCity: 'New York, NY 1030',
delivTime: '09:22 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '15 minutes'},
{id: '2618-3154', price: 80, requests: 25, pledge: 550, weight: 250,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'red', themeColorHex: '#EF5350',
bgImgUrl: 'http://i.imgur.com/6LLOF8O.jpg', rating: 5, ratingCount: 66,
fromStreet: 'W 90th St', fromCity: 'New York, NY 125',
toStreet: '46th Ave', toCity: 'Woodside, NY 11',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'},
{id: '2618-3153', price: 49, requests: 17, pledge: 299, weight: 149,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'purple', themeColorHex: '#BA68C8',
bgImgUrl: 'http://i.imgur.com/6Ut42CM.jpg', rating: 5, ratingCount: 26,
fromStreet: 'W 90th St', fromCity: 'New York, NY 1025',
toStreet: '46th Ave', toCity: 'Woodside, NY 11101',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'},
{id: '2618-3152', price: 99, requests: 33, pledge: 611, weight: 432,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'green', themeColorHex: '#52A43A',
bgImgUrl: 'http://i.imgur.com/iW19D5b.jpg', rating: 2, ratingCount: 26,
fromStreet: 'W 90th St', fromCity: 'New York, NY 1025',
toStreet: '46th Ave', toCity: 'Woodside, NY 11101',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'},
{id: '2618-3151', price: 61, requests: 15, pledge: 318, weight: 130,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'orange', themeColorHex: '#F7AA17',
bgImgUrl: 'http://i.imgur.com/VOjVYjg.jpg', rating: 3, ratingCount: 26,
fromStreet: 'W 90th St', fromCity: 'New York, NY 10025',
toStreet: '46th Ave', toCity: 'Woodside, NY 11101',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'},
{id: '2618-3150', price: 13, requests: 9, pledge: 231, weight: 55,
sender: 'Edward Norton', senderImg: 'http://i.imgur.com/6BctsMp.jpg',
themeColor: 'red', themeColorHex: '#EF5350',
bgImgUrl: 'http://i.imgur.com/tj9HJzX.jpg', rating: 4, ratingCount: 26,
fromStreet: 'W 90th St', fromCity: 'New York, NY 10025',
toStreet: '46th Ave', toCity: 'Woodside, NY 11101',
delivTime: '06:30 pm', delivDate: 'May 16, 2015', delivDateNoun: 'Today',
reqDl: '24 minutes'}
];
var app = angular.module("delivcard", []);
app.controller("DelivCtrl", ['$scope', function($scope) {
$scope.cards = delivcardDefaultData;
}]);
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
$openSans: 'Open Sans', Helvetica, Arial, sans-serif;
body {
background: #222;
font-family: $openSans;
}
$screenW: 375px;
$screenH: 667px;
$padSide: 24px;
$padVert: 106px;
$phoneW: $screenW + $padSide*2;
$phoneH: $screenH + $padVert*2;
%smallHeading {
font-size: 10px;
text-transform: uppercase;
color: #B5B5B5;
}
.phone {
position: relative;
width: $phoneW;
height: $phoneH;
margin: 2rem auto 2rem;
padding: $padVert $padSide;
background-color: #f8f8f8;
border-radius: 56px;
@media (max-width: 480px) {
width: 100%;
height: 100vh;
margin: 0;
padding: 0;
background: transparent;
border-radius: 0;
}
&__screen {
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
background-color: #525A75;
}
&__scroll-cont {
overflow-y: auto;
position: absolute;
left: 0;
top: -21px;
width: calc(100% + 15px);
height: calc(100% + 42px);
padding: 21px 0;
@media (max-width: 480px) {
width: 100%;
height: 100%;
}
}
&__content {
position: relative;
padding-top: 30px;
}
}
$cardHeight: 160px;
$cardHeaderHeight: 40px;
$cardStatsHeight: $cardHeight - $cardHeaderHeight;
$cardMapHeight: $cardHeight + 100px;
$cardSenderHeight: 90px;
$cardFromToHeight: $cardHeight - $cardSenderHeight;
$cardLastPartHeight: $cardFromToHeight + 15px;
$cardMrgBtm: 15px;
$cardStep1MrgBtm: $cardHeight + $cardMrgBtm;
$cardStep2MrgBtm: $cardStep1MrgBtm + $cardFromToHeight;
$cardStep3MrgBtm: $cardStep2MrgBtm + $cardLastPartHeight;
$cardReqStep1MrgBtm: $cardHeaderHeight + $cardSenderHeight + $cardLastPartHeight - $cardHeight;
$cardReqStep2MrgBtm: $screenH - $cardHeight - $cardMrgBtm*2;
$cardBorderRadius: 11px;
$cardAnimStep1: 0.5s;
$cardAnimStep2: 0.5s;
$cardAnimStep3: 0.5s;
$cardReqAnimStep1: 0.6s;
$cardReqAnimStep2: 0.6s;
$cardReqClosing1: 0.5s;
$cardReqClosing2: 0.5s;
$cardAnimTotal: $cardAnimStep1 + $cardAnimStep2 + $cardAnimStep3;
$purple: #BA68C8;
$green: #52A43A;
$orange: #F7AA17;
$red: #EF5350;
@mixin colorTheme($color) {
.card__face__colored-side {
background: $color;
}
.card__face__path {
background: repeating-linear-gradient($color, $color 3px, transparent 3px, transparent 6px);
border-color: $color;
}
.card__header {
background: $color;
}
.card__sender__rating__star {
color: $color;
}
.card__path-big {
background: repeating-linear-gradient($color, $color 3px, transparent 3px, transparent 6px);
border-color: $color;
}
}
.card {
z-index: 1;
position: relative;
width: 90%;
height: $cardHeight;
margin-left: 18px;
margin-bottom: $cardMrgBtm;
perspective: 2000px;
transition: margin $cardAnimStep1 * 0.8 $cardAnimStep1 * 0.2;
&.active {
transition: margin $cardAnimStep1 * 0.8;
}
&.flip-step1 {
margin-bottom: $cardStep1MrgBtm;
}
&.flip-step2 {
margin-bottom: $cardStep2MrgBtm;
}
&.flip-step3 {
margin-bottom: $cardStep3MrgBtm;
}
&.req-active1 {
transition: margin $cardReqAnimStep1 cubic-bezier(.77,.03,.83,.67);
margin-bottom: $cardReqStep1MrgBtm;
}
&.req-active2 {
transition: margin $cardReqAnimStep2 cubic-bezier(.31,.14,.48,1.52);
margin-bottom: $cardReqStep2MrgBtm;
}
&.req-closing1 {
transition: margin $cardReqClosing1;
margin-bottom: $cardHeight + $cardMrgBtm;
}
&.req-closing2 {
transition: margin $cardReqClosing2;
margin-bottom: $cardMrgBtm;
}
&.hidden-hack {
.card__part-2 > .m--front{
display: none;
}
}
&.no-transition {
transition: all 0s 0s !important;
* {
transition: all 0s 0s !important;
}
}
&.theme-purple {
@include colorTheme($purple);
}
&.theme-green {
@include colorTheme($green);
}
&.theme-orange {
@include colorTheme($orange);
}
&.theme-red {
@include colorTheme($red);
}
&__part {
z-index: 1;
position: absolute;
left: 0;
width: 100%;
border-radius: $cardBorderRadius;
transform-origin: 50% 0;
transform-style: preserve-3d;
&__side {
z-index: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: inherit;
background: #fff;
backface-visibility: hidden;
transform-style: preserve-3d;
&.m--back {
transform: rotateX(180deg);
}
&.m--front {
background: #fff;
}
}
&-1 {
top: 0;
height: 100%;
transition: all $cardAnimStep1/2;
transform: translateZ(-3px);
.card.flip-step1 & {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
transform: translateZ(0);
}
.card.req-closing2 & {
border-radius: $cardBorderRadius;
}
}
&-2 {
z-index: 6;
top: 100%;
height: 100%;
transform: rotateX(179deg) translateZ(3px);
transition: transform $cardAnimStep1 ease-out, border-radius $cardAnimStep1/2;
> .m--back {
overflow: hidden;
cursor: pointer;
}
.card.flip-step1 & {
transform: translateZ(0);
border-radius: 0;
}
.card.req-active1 & {
transition: transform $cardReqAnimStep1;
transform: translate3d(0,-$cardStatsHeight,0);
}
.card.req-active2 & {
transition: transform $cardReqAnimStep2 cubic-bezier(.61,.14,.18,1.52);
transform: translate3d(0, $cardMapHeight - $cardStatsHeight,0);
}
.card.req-closing1 & {
transition: transform $cardReqClosing1;
transform: translate3d(0,0,0);
}
.card.req-closing2 & {
transition: transform, border-radius;
transition-duration: $cardReqClosing2;
transform: rotateX(179deg) translateZ(3px);
border-radius: $cardBorderRadius;
}
}
&-3 {
top: 100%;
height: $cardFromToHeight;
transform: rotateX(179deg) translateZ(-3px);
transition: transform $cardAnimStep2, border-radius 0s $cardAnimStep3;
> .m--back {
background: #F7EFF7;
}
.card.flip-step2 & {
transition: transform $cardAnimStep2, border-radius 0s 0s;
border-radius: 0;
transform: translateZ(0);
}
.card.req-active1 & {
transition: transform, height;
transition-duration: $cardReqAnimStep1;
transform: translate3d(0,-$cardFromToHeight,0);
height: 0;
}
.card.req-active2 & {
transition: transform, height;
transition-duration: $cardReqAnimStep2;
transform: translate3d(0,0,0);
height: 75px;
}
.card.req-closing1 & {
transition: transform $cardReqClosing1;
transform: rotateX(179deg) translateZ(-3px);
}
.card.req-closing2 & {
transition: border-radius $cardReqClosing1;
border-radius: $cardBorderRadius;
}
}
&-4 {
top: 100%;
height: $cardFromToHeight;
transform: rotateX(179deg) translateZ(0);
transition: transform $cardAnimStep3, border-radius 0s 0s, height $cardAnimStep3;
> .m--back {
background: #F7EFF7;
}
> .m--front {
padding: 10px 20px;
opacity: 0;
transition: opacity 0s $cardAnimStep3;
}
.card.flip-step2 & {
transition: transform $cardAnimStep3, border-radius 0s 0s, height $cardAnimStep3*0.66 $cardAnimStep3*0.33;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.card.flip-step3 & {
transition: transform $cardAnimStep2, border-radius 0s 0s, height $cardAnimStep2*0.66;
height: $cardLastPartHeight;
transform: translateZ(0);
> .m--front {
opacity: 1;
transition: opacity 0s 0s;
}
}
.card.req-closing1 & {
transition: border-radius $cardReqClosing1;
border-radius: 0;
}
.card.req-closing2 & {
transition: border-radius $cardReqClosing2;
border-radius: $cardBorderRadius;
}
}
&__inner {
overflow: hidden;
position: relative;
height: 100%;
border-radius: inherit;
}
}
&__text {
overflow: hidden;
position: absolute;
top: 0;
width: 50%;
height: 100%;
&:after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 20px;
height: 100%;
background: linear-gradient(90deg, rgba(255,255,255,0), #fff);
}
p {
width: 200%;
}
&--left {
left: 0;
}
&--right {
left: 50%;
}
&__heading {
@extend %smallHeading;
line-height: 1;
margin-bottom: 3px;
}
&__middle {
font-size: 18px;
line-height: 1;
font-weight: bold;
color: #4B4D52;
}
&__bottom {
font-size: 14px;
color: #555A5F;
}
}
&__face {
padding: 20px 20px 15px;
&:after {
content: "";
z-index: 5;
position: absolute;
right: 0;
top: 0;
width: 20px;
height: 100%;
background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255, 1) 60%, rgba(255,255,255,1) 100%);
}
&__colored-side {
z-index: -1;
position: fixed; // THE ONLY CASE when top left corner is not bugged in Chrome/FF (it's still bugged in FF, but looks much better)
left: 0;
top: 0;
width: 7px;
height: 100%;
border-top-left-radius: $cardBorderRadius;
border-bottom-left-radius: $cardBorderRadius;
transition: width 0.3s;
.card__face:hover & {
width: 80px;
}
}
&__price {
margin-top: -3px;
font-weight: normal;
font-size: 22px;
color: #555A5F;
transition: color 0.3s;
.card__face:hover & {
color: #fff;
}
}
&__divider {
position: absolute;
left: 80px;
top: 10px;
width: 1px;
height: calc(100% - 20px);
background: #ECECEC;
}
&__path {
position: absolute;
left: 105px;
top: 36px;
width: 2px;
height: 23px;
&:before,
&:after {
content: "";
position: absolute;
left: -3px;
width: 8px;
height: 8px;
border-radius: 50%;
border: 2px solid;
border-color: inherit;
}
&:before {
top: -11px;
}
&:after {
bottom: -9px;
}
}
&__from-to {
position: absolute;
left: 120px;
top: 20px;
width: 300px;
color: #555A5F;
font-size: 13px;
&:before {
content: "";
position: absolute;
left: 0;
top: 50%;
width: 100%;
height: 1px;
background: #ECECEC;
}
p:first-child {
margin-bottom: 15px;
}
}
&__deliv-date {
position: absolute;
left: 20px;
top: 105px;
color: #9A9E9E;
text-transform: uppercase;
font-size: 12px;
transition: color 0.3s;
p {
color: #606568;
transition: color 0.3s;
}
.card__face:hover & {
color: rgba(255,255,255,0.8);
p {
color: #fff;
}
}
}
&__stats {
position: absolute;
top: 105px;
color: #B5B5B5;
text-transform: uppercase;
font-size: 12px;
p {
font-size: 15px;
color: #ADADAD;
font-weight: bold;
}
&--req {
left: 100px;
}
&--pledge {
left: 190px;
}
&--weight {
left: 270px;
p {
text-transform: lowercase;
}
}
}
}
&__header {
position: relative;
height: $cardHeaderHeight;
color: rgba(255,255,255,0.8);
&__close-btn {
z-index: 2;
position: absolute;
left: 12px;
top: 12px;
width: 16px;
height: 16px;
transition: transform 0.3s;
cursor: pointer;
&:hover {
transform: rotate(90deg);
}
&:before,
&:after {
content: "";
position: absolute;
left: -4px;
top: 7px;
width: 23px;
height: 2px;
background: #fff;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
&__id {
position: absolute;
left: 0;
top: 0;
width: 100%;
line-height: $cardHeaderHeight;
text-align: center;
}
&__price {
position: absolute;
right: 10px;
top: 0;
line-height: $cardHeaderHeight;
}
}
&__stats {
position: relative;
height: $cardStatsHeight;
background-size: cover;
background-position: center center;
transition: opacity $cardReqAnimStep1;
&:before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
}
.card.req-active1 & {
opacity: 0;
}
.card.req-active2 & {
display: none;
}
&__item {
position: absolute;
bottom: 10px;
&--req {
z-index: 2;
left: 20px;
}
&--pledge {
z-index: 1;
left: 0;
width: 100%;
text-align: center;
}
&--weight {
z-index: 2;
right: 20px;
text-align: right;
}
}
&__type {
font-size: 12px;
text-transform: uppercase;
color: rgba(255,255,255,0.6);
}
&__value {
font-size: 19px;
font-weight: bold;
color: #fff;
}
}
&__map {
display: none;
overflow: hidden;
z-index: -1;
position: absolute;
left: 0;
top: $cardHeaderHeight;
width: 100%;
height: $cardMapHeight - 150px;
opacity: 0;
.card.map-active & {
display: block;
}
.card.req-active2 & {
transition: height $cardReqAnimStep2*0.8;
height: $cardMapHeight + 50px;
z-index: 5;
opacity: 1;
}
.card.req-closing1 & {
transition: height $cardReqClosing1;
height: $cardStatsHeight;
}
.card.req-closing2 & {
transition: opacity $cardReqClosing2;
opacity: 0;
}
&__inner {
position: absolute;
left: -5%;
top: -5%;
width: 110%;
height: ($cardMapHeight + 50px) * 1.1;
transform: scale(1);
transition: transform $cardReqAnimStep2 cubic-bezier(.8,-1.4,.8,1.4);
.card.req-active2 & {
transform: scale(1.1) !important;
}
}
}
&__sender {
position: relative;
height: $cardSenderHeight;
padding: 10px 20px 0;
.card.req-active2 & {
height: $cardHeight;
}
&:after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #ECECEC;
transition: opacity $cardReqAnimStep2/2;
opacity: 0;
.card.req-active2 & {
opacity: 1;
}
}
&__heading {
@extend %smallHeading;
margin-bottom: 5px;
.card.req-active1 & {
transition: opacity, margin;
transition-duration: $cardReqAnimStep1*0.8;
opacity: 0;
margin-top: -9px;
}
.card.req-active2 & {
pointer-events: none;
}
}
&__img-cont {
overflow: hidden;
display: inline-block;
vertical-align: top;
width: 50px;
height: 50px;
margin-right: 5px;
border-radius: 8px;
&__inner {
height: 100%;
filter: grayscale(100%);
}
}
&__img {
width: 100%;
min-height: 100%;
}
&__name-and-rating {
overflow: hidden;
display: inline-block;
vertical-align: top;
max-width: 180px;
height: 55px;
margin-top: -5px;
margin-bottom: 20px;
}
&__name {
font-size: 18px;
color: #3B424A;
}
&__rating {
font-size: 14px;
.card.req-active1 & {
transition: opacity $cardReqAnimStep1*0.8;
opacity: 0;
}
.card.req-active2 & {
display: none;
}
&__star {
opacity: 0.3;
}
@for $i from 1 through 5 {
&-#{$i} {
@for $j from 1 through $i {
.card__sender__rating__star:nth-child(#{$j}) {
opacity: 1;
}
}
}
}
&__count {
font-size: 12px;
color: #8C9093;
}
}
&__address {
font-size: 12px;
color: #8C9093;
opacity: 0;
pointer-events: none;
.card.req-active2 & {
transition: opacity $cardReqAnimStep2;
opacity: 1;
pointer-events: auto;
}
}
}
&__receiver {
overflow: hidden;
position: relative;
width: 100%;
height: 0;
.card.req-active2 & {
transition: height $cardReqAnimStep2;
height: 65px;
}
&__inner {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 65px;
}
}
&__path-big {
position: absolute;
right: 26px;
top: 55px;
width: 2px;
height: 57px;
opacity: 0;
.card.req-active2 & {
transition: opacity $cardReqAnimStep2/2;
opacity: 1;
}
&:before,
&:after {
content: "";
position: absolute;
left: -4px;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid;
border-color: inherit;
}
&:before {
top: -13px;
}
&:after {
bottom: -11px;
}
}
&__from-to {
position: relative;
height: $cardFromToHeight;
padding: 10px 20px 0;
.card.req-active1 & {
transition: opacity $cardReqAnimStep1*0.8;
opacity: 0;
}
.card.req-active2 & {
display: none;
}
&__inner {
position: relative;
height: 100%;
&:before {
content: "";
position: absolute;
left: 0;
top: -10px;
width: 100%;
height: 1px;
background: #ECECEC;
}
}
}
&__timings {
position: relative;
height: 100%;
padding: 10px 20px 0;
.card.req-active1 & {
transition: opacity $cardReqAnimStep1*0.8;
opacity: 0;
}
.card.req-active2 & {
display: none;
}
&__inner {
position: relative;
height: 100%;
}
}
&__timer {
display: none;
margin-top: 22px;
font-size: 30px;
color: #A4ADAD;
text-align: center;
.card.req-active2 & {
display: block;
}
}
&__request-btn {
position: relative;
width: 100%;
height: 40px;
background-color: #FFBD18;
text-transform: uppercase;
font-size: 18px;
color: #3F2F1F;
outline: none;
border: none;
border-radius: 8px;
cursor: pointer;
letter-spacing: 0; // you need to manually set value, because FireFox can't interpolate from normal to needed value with transition
transition: letter-spacing 0.3s;
&__text-1 {
transition: opacity $cardReqAnimStep1*0.8;
.card.req-active1 & {
opacity: 0;
}
.card.req-active2 & {
display: none;
}
}
&__text-2 {
z-index: -5;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
opacity: 0;
transition: opacity $cardReqAnimStep2;
.card.req-active2 & {
z-index: 1;
opacity: 1;
}
}
&:hover {
letter-spacing: 5px;
}
}
&__counter {
position: absolute;
left: 0;
top: 57px;
width: 100%;
font-size: 14px;
color: #A2A2A5;
text-align: center;
transition: opacity $cardReqAnimStep1*0.8;
.card.req-active1 & {
opacity: 0;
}
.card.req-active2 & {
display: none;
}
}
}
<link href="http://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment