feat: add 3d card

This commit is contained in:
Akuoko Daniel Jnr 2021-02-23 13:38:20 +00:00
parent 71c2bb0813
commit 52cc5d813f
No known key found for this signature in database
GPG Key ID: 1C95803CACD3E9DC
4 changed files with 334 additions and 0 deletions

View File

@ -172,6 +172,7 @@ export default class SystemPage extends React.Component {
<SidebarLink url={url} href="/_/system/avatar-group" title="Avatar Group" />
<SidebarLink url={url} href="/_/system/buttons" title="Buttons" />
<SidebarLink url={url} href="/_/system/card-tabs" title="Card Tabs" />
<SidebarLink url={url} href="/_/system/card-3d" title="3D Card" />
<SidebarLink url={url} href="/_/system/checkboxes" title="Checkboxes" />
<SidebarLink url={url} href="/_/system/colors" title="Colors" />
<SidebarLink url={url} href="/_/system/dropdowns" title="Dropdowns" />

View File

@ -0,0 +1,258 @@
import * as React from "react";
import { css } from "@emotion/react";
const STYLES_WRAPPER = css`
display: inline-block;
/* height: 100%; */
/* width: 100%; */
height: 200px;
width: 320px;
border-radius: 8px;
transform-style: preserve-3d;
-webkit-tap-highlight-color: rgba(#000, 0);
.container.over .shadow {
box-shadow: 0 45px 100px rgba(14, 21, 47, 0.4), 0 16px 40px rgba(14, 21, 47, 0.4);
}
`;
const STYLES_CONTAINER = css`
position: relative;
width: 100%;
height: 100%;
border-radius: 8px;
transition: all 0.2s ease-out;
`;
const STYLES_SHADOW = css`
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
transition: all 0.2s ease-out;
box-shadow: 0 8px 30px rgba(14, 21, 47, 0.6);
`;
const STYLES_LAYER = css`
position: relative;
width: 100%;
height: 100%;
border-radius: 8px;
overflow: hidden;
transform-style: preserve-3d;
`;
const STYLES_SHINE = css`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 8px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 60%);
`;
const STYLES_RENDERED_LAYER = css`
position: absolute;
width: 100%;
height: 100%;
top: 0%;
left: 0%;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
background-size: cover;
transition: all 0.1s ease-out;
overflow: hidden;
border-radius: 8px;
`;
const Card3D = ({ children }) => {
const wrapper = React.useRef();
const [width, setWidth] = React.useState(0);
React.useEffect(() => {
const width = getWidth(wrapper.current);
setWidth(width);
}, [width]);
const _handleMouseMove = (...args) => processMovement(...args);
const _handleMouseEnter = (...args) => processEnter(...args);
const _handleMouseLeave = (...args) => processExit(...args);
const _handleTouchMove = (e, ...rest) => {
if (window.preventScroll) {
e.preventDefault();
}
processMovement(e, ...rest);
};
const _handleTouchStart = (...args) => {
window.preventScroll = true;
processEnter(...args);
};
const _handleTouchEnd = (...args) => {
window.preventScroll = false;
processExit(...args);
};
React.useEffect(() => {
let layersNode = document.querySelectorAll(".rendered-layer");
let layers = Array.from(layersNode);
let shine = document.querySelectorAll(".shine")[0];
// desktop devices
wrapper.current.addEventListener("mousemove", (e) =>
_handleMouseMove(e, false, wrapper.current, layers, layers.length, shine)
);
wrapper.current.addEventListener("mouseenter", (e) => _handleMouseEnter(e, wrapper.current));
wrapper.current.addEventListener("mouseleave", (e) =>
_handleMouseLeave(e, wrapper.current, layers, layers.length, shine)
);
// mobile devices
wrapper.current.addEventListener("touchmove", (e) =>
_handleTouchMove(e, false, wrapper.current, layers, layers.length, shine)
);
wrapper.current.addEventListener("touchstart", (e) => _handleTouchStart(e, wrapper.current));
wrapper.current.addEventListener("touchend", (e) =>
_handleTouchEnd(e, wrapper.current, layers, layers.length, shine)
);
return () => {
// desktop devices
wrapper.current.removeEventListener("mousemove", (e) =>
_handleMouseMove(e, false, wrapper.current, layers, layers.length, shine)
);
wrapper.current.removeEventListener("mouseenter", (e) =>
_handleMouseEnter(e, wrapper.current)
);
wrapper.current.removeEventListener("mouseleave", (e) =>
_handleMouseLeave(e, wrapper.current, layers, layers.length, shine)
);
// mobile devices
wrapper.current.removeEventListener("touchmove", (e) =>
_handleTouchMove(e, false, wrapper.current, layers, layers.length, shine)
);
wrapper.current.removeEventListener("touchstart", (e) =>
_handleTouchStart(e, wrapper.current)
);
wrapper.current.removeEventListener("touchend", (e) =>
_handleTouchEnd(e, wrapper.current, layers, layers.length, shine)
);
};
});
return (
<div css={STYLES_WRAPPER} ref={wrapper} style={{ transform: `perspective(${width * 3}px)` }}>
<div css={STYLES_CONTAINER} className="container">
<div className="shadow" css={STYLES_SHADOW} />
<div className="layer" css={STYLES_LAYER}>
<div
className="rendered-layer"
css={STYLES_RENDERED_LAYER}
style={{
backgroundImage: "url('http://robindelaporte.fr/codepen/visa-bg.jpg')",
}}
></div>
<div
className="rendered-layer"
css={STYLES_RENDERED_LAYER}
style={{
backgroundImage: "url('http://robindelaporte.fr/codepen/visa.png')",
}}
></div>
</div>
<div className="shine" css={STYLES_SHINE} />
</div>
</div>
);
};
export default Card3D;
function getWidth(elem) {
let width;
if (elem) {
width = elem.clientWidth || elem.offsetWidth || elem.scrollWidth;
}
return width;
}
function processMovement(e, touchEnabled, elem, layers, totalLayers, shine) {
let html = document.getElementsByTagName("html")[0];
let bodyScrollTop = document.body.scrollTop || html.scrollTop,
bodyScrollLeft = document.body.scrollLeft,
pageX = touchEnabled ? e.touches[0].pageX : e.pageX,
pageY = touchEnabled ? e.touches[0].pageY : e.pageY,
offsets = elem.getBoundingClientRect(),
w = elem.clientWidth || elem.offsetWidth || elem.scrollWidth,
h = elem.clientHeight || elem.offsetHeight || elem.scrollHeight,
wMultiple = 320 / w,
offsetX = 0.52 - (pageX - offsets.left - bodyScrollLeft) / w,
offsetY = 0.52 - (pageY - offsets.top - bodyScrollTop) / h,
dy = pageY - offsets.top - bodyScrollTop - h / 2,
dx = pageX - offsets.left - bodyScrollLeft - w / 2,
yRotate = (offsetX - dx) * (0.07 * wMultiple),
xRotate = (dy - offsetY) * (0.1 * wMultiple),
imgCSS = "rotateX(" + xRotate + "deg) rotateY(" + yRotate + "deg)",
arad = Math.atan2(dy, dx),
angle = (arad * 180) / Math.PI - 90;
if (angle < 0) {
angle = angle + 360;
}
if (elem.firstChild.className.indexOf(" over") !== -1) {
imgCSS += " scale3d(1.07,1.07,1.07)";
}
elem.firstChild.style.transform = imgCSS;
shine.style.background =
"linear-gradient(" +
angle +
"deg, rgba(255,255,255," +
((pageY - offsets.top - bodyScrollTop) / h) * 0.4 +
") 0%,rgba(255,255,255,0) 80%)";
shine.style.transform =
"translateX(" +
offsetX * totalLayers -
0.1 +
"px) translateY(" +
offsetY * totalLayers -
0.1 +
"px)";
let revNum = totalLayers;
for (let ly = 0; ly < totalLayers; ly++) {
layers[ly].style.transform =
"translateX(" +
offsetX * revNum * ((ly * 2.5) / wMultiple) +
"px) translateY(" +
offsetY * totalLayers * ((ly * 2.5) / wMultiple) +
"px)";
revNum--;
}
}
function processEnter(e, elem) {
elem.firstChild.className += " over";
}
function processExit(e, elem, layers, totalLayers, shine) {
let container = elem.firstChild;
container.className = container.className.replace(" over", "");
container.style.transform = "";
shine.style.cssText = "";
for (let ly = 0; ly < totalLayers; ly++) {
layers[ly].style.transform = "";
}
}

View File

@ -81,6 +81,8 @@ import { AvatarGroup } from "~/components/system/components/AvatarGroup";
import * as SVG from "~/common/svg";
import Card3D from "~/components/system/components/Card3D";
// NOTE(jim): Export everything.
export {
// NOTE(martina): Actions
@ -113,6 +115,7 @@ export {
ButtonDisabledFull,
ButtonWarning,
CardTabGroup,
Card3D,
CheckBox,
CodeText,
CodeTextarea,

72
pages/_/system/card-3d.js Normal file
View File

@ -0,0 +1,72 @@
import * as React from "react";
import * as System from "~/components/system";
import * as Constants from "~/common/constants";
import { css } from "@emotion/react";
import SystemPage from "~/components/system/SystemPage";
import ViewSourceLink from "~/components/system/ViewSourceLink";
import CodeBlock from "~/components/system/CodeBlock";
export default class SystemPageCard3D extends React.Component {
render() {
return (
<SystemPage title="SDS: 3D Card" description="..." url="https://slate.host/_/system/card-3d">
<System.H1>
3D Card <ViewSourceLink file="system/card-3d.js" />
</System.H1>
<br />
<br />
<System.P>All of the colors the Filecoin Client uses.</System.P>
<br />
<br />
<br />
<System.H2>Imports</System.H2>
<hr />
<br />
<System.P>Import Constants.</System.P>
<br />
<br />
<CodeBlock>{`import { Constants } from 'slate-react-system';`}</CodeBlock>
<br />
<br />
<System.H2>Usage</System.H2>
<hr />
<br />
<System.P>Declare Constants.</System.P>
<br />
<CodeBlock>
{`{Constants.system.white};
{Constants.system.foreground};
{Constants.system.gray};
{Constants.system.border};
{Constants.system.darkGray};
{Constants.system.black};
{Constants.system.pitchBlack};
{Constants.system.brand};
{Constants.system.link};
{Constants.system.green};
{Constants.system.yellow};
{Constants.system.red};`}
</CodeBlock>
<br />
<br />
<System.H2>Output</System.H2>
<hr />
<br />
<System.Card3D />
</SystemPage>
);
}
}