Created
September 16, 2018 11:39
-
-
Save madeinfree/d694ca0270943c1ba3ed4c95c5639dcb to your computer and use it in GitHub Desktop.
Rotate Credit Card for Reactjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.flex { | |
display: flex; | |
} | |
.outer-margin { | |
margin-left: 30px; | |
margin-right: 30px; | |
} | |
.animation { | |
transition: all 0.32s ease; | |
} | |
.unfocus-color { | |
color: rgba(255, 255, 255, 0.3); | |
} | |
.focus-color { | |
color: rgba(255, 255, 255, 1); | |
} | |
.credit-card__wrapper { | |
user-select: none; | |
position: relative; | |
perspective: 1000; | |
transform-style: preserve-3d; | |
} | |
.credit-card__gray-rotate { | |
transform-origin: center center; | |
transform: rotateY(180deg); | |
} | |
.credit-card__full-front-rotate { | |
transform-origin: center center; | |
transform: rotateY(180deg); | |
} | |
.credit-card__full-back-rotate { | |
transform-origin: center center; | |
transform: rotateY(0deg); | |
} | |
.credit-card__gray { | |
background: linear-gradient( | |
45deg, | |
rgba(40, 52, 59, 1) 0%, | |
rgba(130, 140, 149, 1) 63%, | |
rgba(181, 189, 200, 1) 100% | |
); | |
width: 290px; | |
height: 182px; | |
box-shadow: 0 0 20px rgba(#000, 0.2); | |
border-radius: 12px; | |
backface-visibility: hidden; | |
} | |
.credit-card { | |
position: absolute; | |
top: 0; | |
z-index: 1; | |
background: linear-gradient(45deg, #1e5799 0%, #2989d8 63%, #7db9e8 100%); | |
width: 290px; | |
height: 182px; | |
box-shadow: 0 0 20px rgba(#000, 0.2); | |
border-radius: 12px; | |
transform-origin: 0 0; | |
transform: scale(0, 0); | |
backface-visibility: hidden; | |
} | |
.credit-card__full { | |
position: absolute; | |
top: 0; | |
z-index: 1; | |
background: linear-gradient(45deg, #1e5799 0%, #2989d8 63%, #7db9e8 100%); | |
width: 290px; | |
height: 182px; | |
box-shadow: 0 0 20px rgba(#000, 0.2); | |
border-radius: 12px; | |
transform-origin: 0 0; | |
transform: scale(1, 1); | |
backface-visibility: hidden; | |
} | |
.credit-card__full-back { | |
position: absolute; | |
z-index: 1; | |
top: 0px; | |
left: 0px; | |
background: linear-gradient(45deg, #1e5799 0%, #2989d8 63%, #7db9e8 100%); | |
width: 290px; | |
height: 182px; | |
box-shadow: 0 0 20px rgba(#000, 0.2); | |
border-radius: 12px; | |
transform: rotateY(-180deg); | |
backface-visibility: hidden; | |
} | |
.credit-card__full-back-line { | |
width: 290px; | |
height: 40px; | |
background-color: black; | |
margin-top: 20px; | |
} | |
.credit-card__full-back-content { | |
position: relative; | |
width: 200px; | |
height: 40px; | |
margin-top: 10px; | |
margin-left: 15px; | |
font-weight: bold; | |
background-color: white; | |
} | |
.credit-card__full-back-content--cvc { | |
font-family: Consolas, Courier, monospace; | |
position: absolute; | |
bottom: 5px; | |
right: 10px; | |
z-index: 3; | |
} | |
.credit-card__full-back-content--line--1 { | |
position: absolute; | |
top: 10px; | |
width: 200px; | |
height: 2px; | |
z-index: 2; | |
background-color: rgb(255, 224, 166); | |
} | |
.credit-card__full-back-content--line--2 { | |
position: absolute; | |
top: 20px; | |
width: 200px; | |
height: 2px; | |
z-index: 2; | |
background-color: rgb(255, 224, 166); | |
} | |
.credit-card__full-back-content--line--3 { | |
position: absolute; | |
top: 30px; | |
width: 200px; | |
height: 2px; | |
z-index: 2; | |
background-color: rgb(255, 224, 166); | |
} | |
.credit-card__full-back-issue { | |
font-family: Consolas, Courier, monospace; | |
text-align: center; | |
margin-top: 30px; | |
font-size: 30px; | |
color: white; | |
} | |
.credit-card__issue { | |
position: relative; | |
font-family: Consolas, Courier, monospace; | |
justify-content: space-between; | |
z-index: 2; | |
padding-top: 18px; | |
display: flex; | |
align-items: center; | |
} | |
.credit-card__issue img { | |
width: 35px; | |
height: 28px; | |
border-radius: 5px; | |
} | |
.credit-card__issue-title { | |
color: white; | |
font-size: 35px; | |
} | |
.credit-card__number { | |
position: relative; | |
z-index: 2; | |
font-family: Consolas, Courier, monospace; | |
justify-content: space-between; | |
padding-top: 30px; | |
font-size: 17px; | |
display: flex; | |
} | |
.credit-card__name-date { | |
position: relative; | |
z-index: 2; | |
justify-content: space-between; | |
padding-top: 30px; | |
font-size: 17px; | |
} | |
.credit-card__input { | |
outline: none; | |
border-radius: 3px; | |
border: 1px solid #ccc; | |
height: 35px; | |
width: 230px; | |
padding-left: 10px; | |
} | |
.credit-card__input-number:focus, | |
.credit-card__input-name:focus, | |
.credit-card__input-valid-thru:focus, | |
.credit-card__input-cvc:focus { | |
border: 1px solid #000; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import ReactDOM from "react-dom"; | |
import "./styles.css"; | |
function CreditCard(props) { | |
return ( | |
<div className="credit-card__wrapper"> | |
<div | |
className={`animation credit-card__gray ${ | |
props.cvc !== "" ? "credit-card__gray-rotate" : "" | |
}`} | |
> | |
<div className="flex outer-margin credit-card__issue"> | |
<img src="https://upload.cc/i1/2018/09/16/7IaB1M.png" /> | |
<div className="credit-card__issue-title">VISA</div> | |
</div> | |
<div | |
className={`flex outer-margin credit-card__number ${ | |
props.focus !== "number" ? "unfocus-color" : "focus-color" | |
}`} | |
> | |
<div> | |
{props.number[0] | |
? [1, 2, 3, 4] | |
.map( | |
(_, index) => | |
props.number[0].split("")[index] | |
? props.number[0][index] | |
: "·" | |
) | |
.join("") | |
: "····"} | |
</div> | |
<div> | |
{props.number[1] | |
? [1, 2, 3, 4] | |
.map( | |
(_, index) => | |
props.number[1].split("")[index] | |
? props.number[1][index] | |
: "·" | |
) | |
.join("") | |
: "····"} | |
</div> | |
<div> | |
{props.number[2] | |
? [1, 2, 3, 4] | |
.map( | |
(_, index) => | |
props.number[2].split("")[index] | |
? props.number[2][index] | |
: "·" | |
) | |
.join("") | |
: "····"} | |
</div> | |
<div> | |
{props.number[3] | |
? [1, 2, 3, 4] | |
.map( | |
(_, index) => | |
props.number[3].split("")[index] | |
? props.number[3][index] | |
: "·" | |
) | |
.join("") | |
: "····"} | |
</div> | |
</div> | |
<div className={`flex outer-margin credit-card__name-date `}> | |
<div | |
className={`${ | |
props.focus !== "name" ? "unfocus-color" : "focus-color" | |
}`} | |
> | |
{props.name || "NAME HERE"} | |
</div> | |
<div | |
className={`${ | |
props.focus !== "valid" ? "unfocus-color" : "focus-color" | |
}`} | |
> | |
{props.validThru[0] | |
? [1, 2] | |
.map( | |
(_, index) => | |
props.validThru[0].split("")[index] | |
? props.validThru[0][index] | |
: "·" | |
) | |
.join("") | |
: "··"}{" "} | |
/{" "} | |
{props.validThru[1] | |
? [1, 2] | |
.map( | |
(_, index) => | |
props.validThru[1].split("")[index] | |
? props.validThru[1][index] | |
: "·" | |
) | |
.join("") | |
: "··"}{" "} | |
</div> | |
</div> | |
<div | |
className={`animation ${ | |
props.number[0] === "" ? "credit-card" : "credit-card__full" | |
}`} | |
/> | |
</div> | |
<div | |
className={`animation ${ | |
props.cvc !== "" | |
? "credit-card__full redit-card__full-front-rotate" | |
: "credit-card__full-back" | |
}`} | |
> | |
<div className="credit-card__full-back-line" /> | |
<div className="credit-card__full-back-content"> | |
<div className="credit-card__full-back-content--cvc"> | |
<b>{props.cvc}</b> | |
</div> | |
<div className="credit-card__full-back-content--line--1" /> | |
<div className="credit-card__full-back-content--line--2" /> | |
<div className="credit-card__full-back-content--line--3" /> | |
</div> | |
<div className="credit-card__full-back-issue">VISA</div> | |
</div> | |
</div> | |
); | |
} | |
class App extends React.PureComponent { | |
constructor() { | |
super(); | |
this.state = { | |
isTrue: false, | |
number: ["", "", "", ""], | |
name: "", | |
validThru: ["", ""], | |
cvc: "", | |
focus: "date" | |
}; | |
} | |
onChangeNumber = number => { | |
if (number.replace(/ /g, "").length > 16) return; | |
this.setState(state => ({ | |
number: number.split(" ") | |
})); | |
}; | |
onChangeName = name => { | |
this.setState(state => ({ | |
name | |
})); | |
}; | |
onChangeValidThru = validThru => { | |
if (validThru.replace(/( )|\//g, "").length > 4) return; | |
this.setState(state => ({ | |
validThru: validThru.split(" / ") | |
})); | |
}; | |
onChangeCVC = cvc => { | |
if (cvc.length > 3) return; | |
this.setState(state => ({ | |
cvc | |
})); | |
}; | |
onChangeFocus = name => { | |
this.setState(state => ({ | |
focus: name | |
})); | |
}; | |
render() { | |
return ( | |
<div className="App"> | |
<CreditCard | |
number={this.state.number} | |
name={this.state.name} | |
validThru={this.state.validThru} | |
cvc={this.state.cvc} | |
focus={this.state.focus} | |
/> | |
<div> | |
<h2>Number</h2> | |
<div> | |
<input | |
className="animation credit-card__input credit-card__input-number" | |
placeholder="4211 4211 4211 4211" | |
value={this.state.number.reduce((curr, next, index, total) => { | |
if (next !== "" && next.length >= 4 && index !== 3) { | |
if (total[index + 1] === "") return curr + next; | |
return curr + next + " "; | |
} | |
return curr + next; | |
}, "")} | |
autoFocus={true} | |
onChange={e => this.onChangeNumber(e.currentTarget.value)} | |
onFocus={() => this.onChangeFocus("number")} | |
type="text" | |
/> | |
</div> | |
<h2>Name</h2> | |
<input | |
className="animation credit-card__input credit-card__input-name" | |
placeholder="Name here" | |
value={this.state.name} | |
onChange={e => this.onChangeName(e.currentTarget.value)} | |
onFocus={() => this.onChangeFocus("name")} | |
type="text" | |
/> | |
<h2>Valid Thru</h2> | |
<input | |
className="animation credit-card__input credit-card__input-valid-thru" | |
placeholder="Valid Thru" | |
value={this.state.validThru.reduce((curr, next, index, total) => { | |
if (next !== "" && index === 0 && next.length === 2) { | |
if (total[index + 1] === "") | |
return curr + next.replace(/( )|\//g, ""); | |
return curr + next + " / "; | |
} | |
return curr + next; | |
}, "")} | |
onChange={e => this.onChangeValidThru(e.currentTarget.value)} | |
onFocus={() => this.onChangeFocus("valid")} | |
type="text" | |
/> | |
<h2>CVC</h2> | |
<input | |
className="animation credit-card__input credit-card__input-cvc" | |
placeholder="CVC" | |
value={this.state.cvc} | |
onChange={e => this.onChangeCVC(e.currentTarget.value)} | |
onFocus={() => this.onChangeFocus("cvc")} | |
type="text" | |
/> | |
</div> | |
</div> | |
); | |
} | |
} | |
const rootElement = document.getElementById("root"); | |
ReactDOM.render(<App />, rootElement); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment