Skip to content

Instantly share code, notes, and snippets.

@shshaw
Created July 11, 2018 20:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shshaw/ffbacaa0916487f9ee87f0d1dadfd147 to your computer and use it in GitHub Desktop.
Save shshaw/ffbacaa0916487f9ee87f0d1dadfd147 to your computer and use it in GitHub Desktop.
💳 Payment Picker, Part 2 | @keyframers 1.14.1
<div class="debug" style="display: none;"></div>
<a href="https://youtu.be/_rKtTIJJaJY" target="_blank" data-keyframers-credit style="color: #000"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
<div id="app" data-state="wallet" data-panning="0">
<header class="ui-header">
</header>
<div class="ui-wallet">
<h2 class="ui-wallet-heading">
Wallet
</h2>
<div class="ui-cards">
<div class="ui-card" data-card="AnimEx">
<div class="ui-card-title">AnimEx</div>
<div class="ui-card-type">Credit</div>
<div class="ui-card-number">
.... 8675
</div>
</div>
<div class="ui-card" data-card="Easea">
<div class="ui-card-title">Easea</div>
<div class="ui-card-type">Prepaid</div>
<div class="ui-card-number">
.... 3099
</div>
</div>
<div class="ui-card" data-card="Keyframers">
<div class="ui-card-title">KeyFramers</div>
<div class="ui-card-type">Debit</div>
<div class="ui-card-number">
.... 8571
</div>
</div>
</div>
</div>
<div class="ui-expenses">
<div class="ui-expense">
<div class="ui-expense-timeframe">This month</div>
<div class="ui-expense-price">$1245</div>
</div>
</div>
<div class="ui-bottom">
<!-- Layer -->
<div class="ui-transactions">
<table class="ui-table" style="--total: 5">
<thead>
<tr class="state-wallet-hidden" style="--i: 0">
<th class="ui-icon"></th>
<th class="ui-day">Today</th>
<th class="ui-price">-$124</th>
</tr>
</thead>
<tbody>
<tr style="--i: 1">
<td class="ui-icon">
<div class="ui-item-icon">📍</div>
</td>
<td class="ui-day">
<div class="ui-item-title">Animation Services</div>
<small class="ui-item-time">18:20</small>
</td>
<td class="ui-price">
-$60.00
</td>
</tr>
<tr style="--i: 2">
<td class="ui-icon">
<div class="ui-item-icon">📍</div>
</td>
<td class="ui-day">
<div class="ui-item-title">Animation Services</div>
<small class="ui-item-time">18:20</small>
</td>
<td class="ui-price">
-$60.00
</td>
</tr>
<tr style="--i: 3">
<td class="ui-icon">
<div class="ui-item-icon">📍</div>
</td>
<td class="ui-day">
<div class="ui-item-title">Animation Services</div>
<small class="ui-item-time">18:20</small>
</td>
<td class="ui-price">
-$60.00
</td>
</tr>
<tr style="--i: 4">
<td class="ui-icon">
<div class="ui-item-icon">📍</div>
</td>
<td class="ui-day">
<div class="ui-item-title">Animation Services</div>
<small class="ui-item-time">18:20</small>
</td>
<td class="ui-price">
-$60.00
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

💳 Payment Picker, Part 2 | @keyframers 1.14.1

Building upon the Payment Picker from part one, David Khourshid and Stephen Shaw deep dive into dynamic dragging reactive animations.

Part 2 of 2. Check out Part 1: https://youtu.be/XWL60VQeR4o

Part 1:

Part 2:

Like what we're doing? There are many ways you can support @keyframers so we can keep live coding awesome animations!

Topics covered:

  • CSS Variables
  • HammerJS
  • RxJS
  • data attributes
  • Simple state machines
  • transitions

A Pen by Shaw on CodePen.

License.

console.clear();
const { fromEventPattern, fromEvent } = rxjs;
const { map, withLatestFrom, scan } = rxjs.operators;
const elApp = document.querySelector('#app');
const debug = document.querySelector('.debug');
const hAppy = new Hammer(elApp, {
threshold: 0
});
hAppy
.get('pan')
.set({ direction: Hammer.DIRECTION_ALL });
const clamp = (val, min, max) => {
return Math.min(max, Math.max(min, val));
}
const pan$ = fromEventPattern(handler => hAppy.on('panup pandown panstart panend', handler));
const height$ = rxjs.of(elApp.offsetHeight);
const event$ = pan$.pipe(
withLatestFrom(height$),
map(([pan, height]) => {
return {
type: pan.type,
rdy: clamp((pan.deltaY / (height * .25) ), -1.6, 1.6)
};
}),
scan((currentState, e) => {
// console.log(currentState, e)
if (e.rdy <= -.5 && currentState.state === 'wallet') {
return { ...e, state: 'card' };
} else if (e.rdy >= .5 && currentState.state === 'card') {
return { ...e, state: 'wallet' };
}
return {...currentState, ...e};
}, {
state: 'wallet',
rdy: 1,
type: undefined
})
);
// event$.subscribe(e => console.log(e));
event$.subscribe(
(e) => {
debug.innerText = '--rdy: ' + e.rdy;
elApp.dataset.state = e.state;
if (e.type === 'panend') {
elApp.style.setProperty('--not-panning', 1);
elApp.style.setProperty('--rdy', null);
elApp.dataset.panning = 0;
return;
}
elApp.dataset.panning = 1;
elApp.style.setProperty('--not-panning', 0);
elApp.style.setProperty('--rdy', e.rdy);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.1/rxjs.umd.js"></script>
@import url('https://fonts.googleapis.com/css?family=Poppins:400:600');
// * { transition: all .3s ease-in-out; }
html {
font-size: 56.5%;
}
#app {
overflow: hidden;
background: #FFF;
height: 85vh;
width: 46vh;
display: grid;
grid-template-columns: 5rem 1fr 2rem;
grid-template-rows: 3rem 1fr 4rem 1.2fr;
grid-row-gap: 2rem;
font-family: 'Poppins', sans-serif;
font-size: 1.4rem;
border: 1px solid #CCC;
border-radius: 3vh;
box-shadow: 0 2rem 4rem rgba(black, 0.1);
> .ui-header {
grid-row: 1 / 2;
}
> .ui-wallet {
grid-column: 2 / -1;
grid-row: 2 / -1;
z-index: 1;
}
> .ui-expenses {
grid-column: 2 / -1;
grid-row: 3 / 4;
}
> .ui-bottom {
grid-column: 2 / -2;
grid-row: 4 / 5;
overflow-y: auto;
}
}
.ui-expenses {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.ui-expense-price {
font-size: 2.8rem;
}
/* ---------------------------------- */
.ui-cards {
// 3d setup
transform-style: preserve-3d;
perspective: 1000px;
perspective-origin: 100% 0%;
}
.ui-card {
position: absolute;
padding: 3rem;
background: #EFCDCD;
--light: #EFCDCD;
--dark: #E5A9BB;
background: linear-gradient(to bottom left, var(--light), var(--dark));
border-radius: 2rem;
color: #FFF;
width: 120%;
padding-bottom: 44%;
}
.ui-card[data-card="AnimEx"] {
--light: #838385;
--dark: #454545;
}
.ui-card[data-card="Easea"] {
--light: #6A9EF0;
--dark: #{ darken(#567EDA, 15%) };
}
.ui-card-title {
font-size: 2.8rem;
}
.ui-card-type {
opacity: 0.8;
}
.ui-card-number {
position: absolute;
bottom: 3rem;
left: 3rem;
}
/* ---------------------------------- */
.ui-table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
thead { color: #aaa; font-size: 1.2rem; }
th,
td {
padding: .5em .25em;
}
.ui-item-title {
font-weight: 600;
}
.ui-item-time {
font-size: 1rem;
}
}
th.ui-price {
text-align: right;
}
th.ui-day {
text-align-last: left;
}
td.ui-price {
text-align: right;
vertical-align: top;
}
.ui-wallet-heading {
position: absolute;
top: 0;
left: 0;
margin: 0;
font-size: 3rem;
}
/* ---------------------------------- */
$duration: .6s;
.ui-wallet-heading,
.ui-cards,
.ui-card,
.ui-expenses,
.ui-bottom,
tr {
transition: transform $duration ease, opacity .5s ease;
transition-duration: calc(var(--not-panning, 0) * #{$duration});
transform: translateY(0%);
}
#app {
cursor: grab;
&[data-panning="1"] {
cursor: grabbing;
}
// --rdy: 0;
--pos-rdy: calc(( var(--rdy, 0) + 1 ) / 2 );
--neg-rdy: calc(( var(--rdy, 0) - 1 ) / 2 );
--card-end: -1;
--wallet-end: 1;
--wallet-reveal: var(--pos-rdy); // Reveal on Wallet State;
--card-reveal: var(--neg-rdy); // Reveal on Card State;
}
[data-state="card"] {
--rdy: -1; // Final state
}
[data-state="wallet"] {
--rdy: 1;
}
.ui-card {
--z: 0;
@for $i from 1 through 3 {
&:nth-child(#{$i}) {
--z: #{$i};
z-index: #{3 - $i};
}
}
transform:
translateY(calc(12rem + 12rem * var(--rdy)))
rotateX(calc(-40deg * var(--wallet-reveal) ))
translateZ(calc( var(--z) * -10rem));
}
.ui-wallet-heading {
opacity: calc( 1 + var(--card-reveal) );
transform: translateY( calc( -10vh * var(--card-reveal) * -1 ) );
}
.ui-expenses,
.ui-bottom {
opacity: calc( 1 - var(--wallet-reveal) );
transform: translateY(calc( 20vh * var(--wallet-reveal) ) );
}
tr {
transform: translateY(calc( 100% * (var(--i)/var(--total)) * var(--wallet-reveal) ) );
// transition-delay: calc( (var(--total) - var(--i)) * 50ms);
}
/* ---------------------------------- */
body {
display: flex;
justify-content: center;
align-items: center;
background: #ddd;
color: #000;
background: linear-gradient(to bottom left, #FEFEFE, #ddd);
&:before, &:after {
content: '';
height: 70vmin;
width: 70vmin;
border-radius: 50%;
position: absolute;
background-image: linear-gradient(to right, #FBE4E7, #FBBECF);
z-index: -1;
opacity: 0.6;
}
&:before {
left: 15%;
bottom: -15vw;
}
&:after {
right: 15%;
bottom: -5vw;
z-index: -3;
opacity: 0.5;
//background-image: linear-gradient(to left, #FBE4E7, #FBBECF);
}
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
*,
*:before,
*:after {
box-sizing: border-box;
position: relative;
}
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.debug {
position: fixed;
top: 0;
left: 0;
font-family: monospace;
font-size: 2rem;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment