今天和大家分享一个纯用web(html+css+js)实现的一个优美的个人信息菜单资料展示。
项目结构很简单只有一个html文件+一个css样式文件以及一个js脚本文件。
项目实现的功能:
1:进入首页是一个天气样式页面(显示当前时间+天气状态+进入主题的跳跃按钮)
2:点击按钮进入主题内容页面时需要输入密码(自定义1234)
3:主题内容是一个多样式菜单,展示自定义的一些内容(城市天气,个人美食,热爱影视等等)
代码如下:
html:
<!DOCTYPE html>
<html lang="en" >
<head><meta charset="UTF-8"><title>CodePen - App Menu With Lock Screen</title><meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500&display=swap" rel="stylesheet"><link rel="stylesheet" href="./style.css"></head>
<body>
<!-- partial:index.partial.html -->
<div id="root"></div>
<!-- partial --><script src='https://unpkg.com/react@17/umd/react.development.js'></script>
<script src='https://unpkg.com/react-dom@17/umd/react-dom.development.js'></script>
<script src='https://unpkg.com/browse/@types/react@16.4.14/index.d.ts'></script>
<script src='https://unpkg.com/browse/@types/react-dom@17.0.2/index.d.ts'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.1/index.min.js'></script>
<script src='https://codepen.io/Hyperplexed/pen/xxYJYjM/54407644e24173ad6019b766443bf2a6.js'></script><script src="./script.js"></script></body>
</html>
css
/* -- */
@keyframes blink {from, 25%, to {opacity: 1;}50% {opacity: 0;}
}
@keyframes spin {from {transform: rotate(0deg);}50% {transform: rotate(720deg);}to {transform: rotate(1440deg);}
}
@keyframes bounce {from, 6.66%, 17.66%, 33.33% {animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);transform: translate3d(0, 0, 0);}13.33%, 14.33% {animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);transform: translate3d(0, -30px, 0) scaleY(1.1);}23.33% {animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);transform: translate3d(0, -15px, 0) scaleY(1.05);}26.66% {transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);transform: translate3d(0, 0, 0) scaleY(0.95);}30% {transform: translate3d(0, -4px, 0) scaleY(1.02);}
}
body {margin: 0px;overflow-x: hidden;padding: 0px;
}
body::-webkit-scrollbar-track {background-color: #1e1e1e;
}
body::-webkit-scrollbar-thumb {background-color: rgba(255, 255, 255, 0.2);border-radius: 100px;
}
body::-webkit-scrollbar {height: 4px;width: 4px;
}
body input, body h1, body h3, body a, body span {color: #5a5a5a;font-family: "Rubik", sans-serif;font-weight: 400;margin: 0px;padding: 0px;
}.background-image {background-position: center;background-repeat: no-repeat;background-size: cover;
}.clear-button {backdrop-filter: blur(3px);background-color: rgba(255, 255, 255, 0.1);border: 1px solid rgba(255, 255, 255, 0.1);border-radius: 100px;box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);cursor: pointer;
}
.clear-button:hover {background-color: rgba(255, 255, 255, 0.2);border: 1px solid rgba(255, 255, 255, 0.3);
}#app {background-color: #1e1e1e;
}
#app.logged-out #app-info {margin-left: 40px;opacity: 1;transform: translateX(0%);
}
#app.logged-out #app-background {cursor: pointer;
}
#app.logged-out #sign-in-button-wrapper {opacity: 1;pointer-events: all;transform: translate(-50%, -40px);
}
#app.logging-in #app-background #app-background-image, #app.verifying-log-in #app-background #app-background-image, #app.log-in-error #app-background #app-background-image {filter: blur(8px);transform: scale(1.2);
}
#app.logging-in #app-pin-wrapper, #app.log-in-error #app-pin-wrapper {opacity: 1;pointer-events: all;transform: translate(-50%, -50%) scale(1);
}
#app.verifying-log-in #app-loading-icon {opacity: 1;transform: translate(-50%, -50%);
}
#app.log-in-error #app-pin-wrapper #app-pin .app-pin-digit {background-color: rgba(239, 83, 80, 0.05);border-color: rgba(239, 83, 80, 0.5);
}
#app.logged-in #app-menu {height: auto;overflow: initial;opacity: 1;pointer-events: all;transform: translateY(0%);
}
#app .scrollable-component {cursor: grab;overflow: auto;user-select: none;width: 100%;
}
#app .scrollable-component:active {cursor: grabbing;
}
#app .scrollable-component::-webkit-scrollbar {height: 0px;width: 0px;
}
#app #app-loading-icon {left: 50%;opacity: 0;pointer-events: none;position: absolute;top: 50%;transform: translate(-50%, 0%);transition: opacity 250ms, transform 250ms;z-index: 2;
}
#app #app-loading-icon i {animation: 2s spin ease-in-out infinite;color: white;font-size: 2em;
}
#app #app-background {height: 100%;left: 0px;overflow: hidden;position: fixed;top: 0px;width: 100%;z-index: 1;
}
#app #app-background #app-background-image {background-image: url("https://images.unsplash.com/photo-1483728642387-6c3bdd6c93e5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2076&q=80");height: 100%;transition: filter 250ms, transform 250ms;width: 100%;
}
#app #app-info {bottom: 0px;left: 0px;margin: 40px;margin-left: 0px;opacity: 0;position: absolute;transform: translateX(-100%);transition: margin 250ms, opacity 250ms, transform 250ms;z-index: 2;
}
#app .user-status-button {cursor: pointer;margin-top: 10px;outline: none;padding: 10px;width: 100px;
}
#app .user-status-button i {color: whitesmoke;font-size: 1.25em;
}
#app #sign-in-button-wrapper {bottom: 0px;left: 50%;opacity: 0;pointer-events: none;position: absolute;transform: translate(-50%, 40px);transition: opacity 250ms, transform 250ms;z-index: 2;
}
#app #sign-in-button-wrapper #sign-in-button:not(:hover) {animation: bounce 3s infinite;animation-delay: 3s;
}
#app .info {align-items: flex-end;display: flex;
}
#app .info .time {color: whitesmoke;font-size: 6em;height: 80px;line-height: 80px;text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#app .info .weather {display: inline-flex;height: 20px;margin-bottom: 6px;margin-left: 20px;
}
#app .info .weather i, #app .info .weather span {align-items: center;display: inline-flex;
}
#app .info .weather i {color: #fdd835;font-size: 0.9em;
}
#app .info .weather span {color: white;
}
#app .info .weather .weather-type {height: 20px;
}
#app .info .weather .weather-temperature-value {font-size: 1.5em;height: 20px;margin-left: 5px;
}
#app .info .weather .weather-temperature-unit {align-items: flex-start;font-size: 0.8em;margin-left: 3px;
}
#app .reminder {display: flex;gap: 6px;margin-top: 10px;
}
#app .reminder i, #app .reminder div {display: inline-flex;
}
#app .reminder i {color: whitesmoke;font-size: 0.8em;height: 12px;line-height: 12px;
}
#app .reminder span {color: rgba(255, 255, 255, 0.8);font-size: 1.1em;
}
#app .reminder .reminder-icon {align-items: center;height: 20px;
}
#app .reminder .reminder-time {align-items: flex-end;color: #1e1e1e;font-size: 0.8em;height: 20px;
}
#app #quick-nav {display: flex;gap: 10px;margin-top: 20px;overflow: auto;padding-bottom: 5px;width: 100%;z-index: 3;
}
#app #quick-nav .quick-nav-item {padding: 10px 20px;
}
#app #quick-nav .quick-nav-item:last-of-type {margin-right: 10px;
}
#app #quick-nav .quick-nav-item .quick-nav-item-label {color: whitesmoke;text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.1);
}
#app #youtube-link {align-items: center;display: inline-flex;gap: 5px;margin-top: 10px;padding: 10px 20px;text-decoration: none;
}
#app #youtube-link i, #app #youtube-link span {height: 20px;line-height: 20px;
}
#app #youtube-link i {color: #ef5350;
}
#app #youtube-link span {color: white;
}
#app .menu-section {margin-top: 60px;
}
#app .menu-section .menu-section-title {align-items: center;display: flex;gap: 6px;
}
#app .menu-section .menu-section-title i, #app .menu-section .menu-section-title span {color: whitesmoke;
}
#app .menu-section .menu-section-title i {font-size: 1em;
}
#app .menu-section .menu-section-title .menu-section-title-text {color: rgba(255, 255, 255, 0.8);font-size: 1.25em;
}
#app .menu-section .menu-section-content {margin-top: 15px;padding-top: 5px;
}
#app #restaurants-section .menu-section-content {display: flex;gap: 1em;
}
#app #restaurants-section .menu-section-content .restaurant-card {border-radius: 10px;box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);cursor: pointer;height: 14vw;max-height: 240px;position: relative;transition: transform 250ms;width: 25%;
}
#app #restaurants-section .menu-section-content .restaurant-card:hover {transform: translateY(-5px);
}
#app #restaurants-section .menu-section-content .restaurant-card:hover .restaurant-card-content .restaurant-card-content-items {margin-bottom: 30px;
}
#app #restaurants-section .menu-section-content .restaurant-card .restaurant-card-content {background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);border-radius: 10px;height: 100%;width: 100%;
}
#app #restaurants-section .menu-section-content .restaurant-card .restaurant-card-content .restaurant-card-content-items {bottom: 0px;display: flex;flex-direction: column;margin: 20px;position: absolute;right: 0px;text-align: right;transition: margin 250ms;
}
#app #restaurants-section .menu-section-content .restaurant-card .restaurant-card-content .restaurant-card-content-items .restaurant-card-title {color: whitesmoke;font-size: 1.5em;
}
#app #restaurants-section .menu-section-content .restaurant-card .restaurant-card-content .restaurant-card-content-items .restaurant-card-desc {color: #42a5f5;font-size: 0.9em;
}
#app #weather-section .menu-section-content {display: flex;gap: 1em;padding: 5px 0px;width: 100%;
}
#app #weather-section .menu-section-content .day-card {backdrop-filter: blur(3px);background-color: rgba(255, 255, 255, 0.1);border: 1px solid rgba(255, 255, 255, 0.2);border-radius: 10px;box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);height: 8vw;max-height: 160px;min-height: 140px;min-width: 180px;position: relative;transition: transform 250ms;width: calc(100% / 7);
}
#app #weather-section .menu-section-content .day-card:last-of-type {margin-right: 5px;
}
#app #weather-section .menu-section-content .day-card .day-card-content {display: flex;flex-direction: column;height: calc(100% - 20px);justify-content: space-evenly;padding: 10px;
}
#app #weather-section .menu-section-content .day-card .day-card-content i, #app #weather-section .menu-section-content .day-card .day-card-content span {color: whitesmoke;text-align: center;
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-weather-temperature {align-items: flex-start;display: flex;font-size: 0.9em;justify-content: center;
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-weather-temperature .day-weather-temperature-unit {font-size: 0.8em;margin-left: 3px;
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-weather-icon {font-size: 3.5em;text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-weather-icon.sunny {color: #fdd835;
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-weather-icon.rainy, #app #weather-section .menu-section-content .day-card .day-card-content .day-weather-icon.stormy {color: #42a5f5;
}
#app #weather-section .menu-section-content .day-card .day-card-content .day-name {font-size: 0.9em;text-transform: uppercase;
}
#app #tools-section .menu-section-content {display: flex;gap: 1em;
}
#app #tools-section .menu-section-content .tool-card {background-color: #1e1e1e;border-radius: 10px;box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);cursor: pointer;height: 8vw;max-height: 160px;min-height: 140px;overflow: hidden;position: relative;transition: transform 250ms;width: calc(100% / 6);
}
#app #tools-section .menu-section-content .tool-card:hover {transform: translateY(-5px);
}
#app #tools-section .menu-section-content .tool-card:hover .tool-card-background {filter: grayscale(25%);
}
#app #tools-section .menu-section-content .tool-card .tool-card-background {border-radius: 10px;filter: grayscale(100%);height: 100%;left: 0px;opacity: 0.5;position: absolute;top: 0px;transition: filter 250ms;width: 100%;
}
#app #tools-section .menu-section-content .tool-card .tool-card-content {background: linear-gradient(to right, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.1));border-radius: 10px;display: flex;flex-direction: column;height: calc(100% - 40px);justify-content: space-between;padding: 20px;position: relative;width: calc(100% - 40px);z-index: 2;
}
#app #tools-section .menu-section-content .tool-card .tool-card-content .tool-card-content-header {display: flex;flex-direction: column;gap: 2px;
}
#app #tools-section .menu-section-content .tool-card .tool-card-content .tool-card-content-header .tool-card-label {color: #42a5f5;font-size: 0.8em;text-transform: uppercase;
}
#app #tools-section .menu-section-content .tool-card .tool-card-content .tool-card-content-header .tool-card-name {color: whitesmoke;font-size: 1.25em;
}
#app #tools-section .menu-section-content .tool-card .tool-card-content .tool-card-icon {color: whitesmoke;font-size: 2em;
}
#app #movies-section .menu-section-content {display: flex;gap: 1em;
}
#app #movies-section .menu-section-content #movie-card-1 .movie-card-content {background: linear-gradient(to top, rgba(57, 73, 171, 0.4), transparent, rgba(0, 0, 0, 0.4));
}
#app #movies-section .menu-section-content #movie-card-2 .movie-card-content {background: linear-gradient(to top, rgba(103, 58, 183, 0.4), transparent, rgba(0, 0, 0, 0.4));
}
#app #movies-section .menu-section-content #movie-card-3 .movie-card-content {background: linear-gradient(to top, rgba(239, 83, 80, 0.4), transparent, rgba(0, 0, 0, 0.4));
}
#app #movies-section .menu-section-content #movie-card-4 .movie-card-content {background: linear-gradient(to top, rgba(42, 252, 152, 0.4), rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4));
}
#app #movies-section .menu-section-content .movie-card {background-color: #1e1e1e;border-radius: 10px;box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);cursor: pointer;height: 40vw;max-height: 600px;min-height: 460px;min-width: 260px;overflow: hidden;position: relative;transition: transform 250ms;width: calc(100% / 4);
}
#app #movies-section .menu-section-content .movie-card:hover {transform: translateY(-5px);
}
#app #movies-section .menu-section-content .movie-card:hover .movie-card-background {transform: scale(1.05);
}
#app #movies-section .menu-section-content .movie-card:hover .movie-card-content i {transform: translate(-20%, -20%) scale(1.2);
}
#app #movies-section .menu-section-content .movie-card .movie-card-background {border-radius: 10px;height: 100%;left: 0px;position: absolute;top: 0px;transition: transform 250ms;width: 100%;z-index: 1;
}
#app #movies-section .menu-section-content .movie-card .movie-card-content {background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4));border-radius: 10px;height: 100%;position: relative;z-index: 2;
}
#app #movies-section .menu-section-content .movie-card .movie-card-content .movie-card-info {display: flex;flex-direction: column;gap: 5px;padding: 30px;
}
#app #movies-section .menu-section-content .movie-card .movie-card-content .movie-card-info span {text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#app #movies-section .menu-section-content .movie-card .movie-card-content .movie-card-info .movie-card-title {color: whitesmoke;font-size: 2em;
}
#app #movies-section .menu-section-content .movie-card .movie-card-content .movie-card-info .movie-card-desc {color: #c8c8c8;font-size: 0.9em;
}
#app #movies-section .menu-section-content .movie-card .movie-card-content i {bottom: 0px;color: whitesmoke;font-size: 5em;padding: 30px;position: absolute;right: 0px;text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);transition: transform 250ms;
}
#app #app-pin-wrapper {left: 50%;opacity: 0;pointer-events: none;position: absolute;top: 50%;transform: translate(-50%, -30%) scale(0.8);transition: opacity 250ms, transform 250ms;z-index: 2;
}
#app #app-pin-wrapper #app-pin-label {color: whitesmoke;font-size: 0.9em;margin: 10px;text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
}
#app #app-pin-wrapper #app-pin-label #app-pin-cancel-text {cursor: pointer;margin-left: 2px;
}
#app #app-pin-wrapper #app-pin-label #app-pin-cancel-text:hover {text-decoration: underline;
}
#app #app-pin-wrapper #app-pin-label #app-pin-error-text {color: #ef5350;
}
#app #app-pin-wrapper #app-pin-hidden-input {background-color: transparent;border: none;height: 0px;outline: none;pointer-events: none;position: absolute;width: 0px;
}
#app #app-pin-wrapper #app-pin {display: flex;gap: 10px;
}
#app #app-pin-wrapper #app-pin .app-pin-digit {align-items: center;background-color: rgba(255, 255, 255, 0.05);border: 1px solid rgba(255, 255, 255, 0.2);border-radius: 10px;box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.06);display: inline-flex;font-size: 3em;height: 80px;justify-content: center;position: relative;transition: background-color 250ms, border-color 250ms;width: 60px;
}
#app #app-pin-wrapper #app-pin .app-pin-digit:after, #app #app-pin-wrapper #app-pin .app-pin-digit:before {box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.06);content: "";position: absolute;transition: opacity 250ms, transform 250ms;z-index: 2;
}
#app #app-pin-wrapper #app-pin .app-pin-digit:before {background-color: whitesmoke;border-radius: 10px;bottom: 0px;height: 3px;left: 15%;opacity: 0;transform: translateY(0px);width: 70%;
}
#app #app-pin-wrapper #app-pin .app-pin-digit:after {background-color: whitesmoke;border-radius: 20px;height: 20px;opacity: 0;transform: scale(0.25);width: 20px;
}
#app #app-pin-wrapper #app-pin .app-pin-digit.focused:before {animation: blink 2s ease-in-out infinite;opacity: 1;transform: translateY(-10px);
}
#app #app-pin-wrapper #app-pin .app-pin-digit.hidden:after {opacity: 1;transform: scale(1);
}
#app #app-pin-wrapper #app-pin .app-pin-digit.hidden .app-pin-digit-value {opacity: 0;transform: scale(0.25);
}
#app #app-pin-wrapper #app-pin .app-pin-digit .app-pin-digit-value {color: whitesmoke;transition: opacity 250ms, transform 250ms;
}
#app #app-menu {height: 100vh;overflow: hidden;opacity: 0;pointer-events: none;position: relative;transform: translateY(-10%);transition: opacity 250ms, transform 250ms;z-index: 2;
}
#app #app-menu #app-menu-content-wrapper {background: linear-gradient(to bottom, transparent, #1e1e1e);margin-top: 30vh;min-height: 80vh;padding: 80px;padding-top: 0px;
}
#app #app-menu #app-menu-content-wrapper #app-menu-content {margin: auto;max-width: 1600px;position: relative;
}
#app #app-menu #app-menu-content-wrapper #app-menu-content #app-menu-content-header {display: flex;justify-content: space-between;
}@media (max-width: 1300px) {#app.logged-out #sign-in-button-wrapper {transform: translate(-40px, 0px);}#app #sign-in-button-wrapper {bottom: 40px;left: auto;right: 0px;transform: translate(40px, 0px);}#app #app-menu #app-menu-content-wrapper {padding: 30px;}#app #app-menu #app-menu-content-wrapper #app-menu-content #restaurants-section .menu-section-content {flex-wrap: wrap;}#app #app-menu #app-menu-content-wrapper #app-menu-content #restaurants-section .menu-section-content .restaurant-card {height: 30vw;max-height: 300px;position: relative;width: calc(50% - 0.5em);}#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content {flex-wrap: wrap;}#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content .tool-card {width: calc(33.33% - 0.69em);}
}
@media (max-width: 600px) {#app .info .time {font-size: 4em;height: 60px;line-height: 60px;}#app .user-status-button {width: 60px;}#app #app-menu #app-menu-content-wrapper #app-menu-content #restaurants-section .menu-section-content {flex-direction: column;}#app #app-menu #app-menu-content-wrapper #app-menu-content #restaurants-section .menu-section-content .restaurant-card {height: 40vw;position: relative;width: 100%;}#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content {flex-wrap: wrap;}#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content .tool-card {width: calc(50% - 0.5em);}
}
@media (max-width: 400px) {#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content {flex-wrap: wrap;}#app #app-menu #app-menu-content-wrapper #app-menu-content #tools-section .menu-section-content .tool-card {width: 100%;}
}
js
"use strict";
var UserStatus;
(function (UserStatus) {UserStatus["LoggedIn"] = "Logged In";UserStatus["LoggingIn"] = "Logging In";UserStatus["LoggedOut"] = "Logged Out";UserStatus["LogInError"] = "Log In Error";UserStatus["VerifyingLogIn"] = "Verifying Log In";
})(UserStatus || (UserStatus = {}));
var Default;
(function (Default) {Default["PIN"] = "1234";
})(Default || (Default = {}));
var WeatherType;
(function (WeatherType) {WeatherType["Cloudy"] = "Cloudy";WeatherType["Rainy"] = "Rainy";WeatherType["Stormy"] = "Stormy";WeatherType["Sunny"] = "Sunny";
})(WeatherType || (WeatherType = {}));
const defaultPosition = () => ({left: 0,x: 0
});
const N = {clamp: (min, value, max) => Math.min(Math.max(min, value), max),rand: (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
};
const T = {format: (date) => {const hours = T.formatHours(date.getHours()), minutes = date.getMinutes(), seconds = date.getSeconds();return `${hours}:${T.formatSegment(minutes)}`;},formatHours: (hours) => {return hours % 12 === 0 ? 12 : hours % 12;},formatSegment: (segment) => {return segment < 10 ? `0${segment}` : segment;}
};
const LogInUtility = {verify: async (pin) => {return new Promise((resolve, reject) => {setTimeout(() => {if (pin === Default.PIN) {resolve(true);}else {reject(`Invalid pin: ${pin}`);}}, N.rand(300, 700));});}
};
const useCurrentDateEffect = () => {const [date, setDate] = React.useState(new Date());React.useEffect(() => {const interval = setInterval(() => {const update = new Date();if (update.getSeconds() !== date.getSeconds()) {setDate(update);}}, 100);return () => clearInterval(interval);}, [date]);return date;
};
const ScrollableComponent = (props) => {const ref = React.useRef(null);const [state, setStateTo] = React.useState({grabbing: false,position: defaultPosition()});const handleOnMouseDown = (e) => {setStateTo(Object.assign(Object.assign({}, state), { grabbing: true, position: {x: e.clientX,left: ref.current.scrollLeft} }));};const handleOnMouseMove = (e) => {if (state.grabbing) {const left = Math.max(0, state.position.left + (state.position.x - e.clientX));ref.current.scrollLeft = left;}};const handleOnMouseUp = () => {if (state.grabbing) {setStateTo(Object.assign(Object.assign({}, state), { grabbing: false }));}};return (React.createElement("div", { ref: ref, className: classNames("scrollable-component", props.className), id: props.id, onMouseDown: handleOnMouseDown, onMouseMove: handleOnMouseMove, onMouseUp: handleOnMouseUp, onMouseLeave: handleOnMouseUp }, props.children));
};
const WeatherSnap = () => {const [temperature] = React.useState(N.rand(65, 85));return (React.createElement("span", { className: "weather" },React.createElement("i", { className: "weather-type", className: "fa-duotone fa-sun" }),React.createElement("span", { className: "weather-temperature-value" }, temperature),React.createElement("span", { className: "weather-temperature-unit" }, "\u00B0F")));
};
const Reminder = () => {return (React.createElement("div", { className: "reminder" },React.createElement("div", { className: "reminder-icon" },React.createElement("i", { className: "fa-regular fa-bell" })),React.createElement("span", { className: "reminder-text" },"Extra cool people meeting ",React.createElement("span", { className: "reminder-time" }, "10AM"))));
};
const Time = () => {const date = useCurrentDateEffect();return (React.createElement("span", { className: "time" }, T.format(date)));
};
const Info = (props) => {return (React.createElement("div", { id: props.id, className: "info" },React.createElement(Time, null),React.createElement(WeatherSnap, null)));
};
const PinDigit = (props) => {const [hidden, setHiddenTo] = React.useState(false);React.useEffect(() => {if (props.value) {const timeout = setTimeout(() => {setHiddenTo(true);}, 500);return () => {setHiddenTo(false);clearTimeout(timeout);};}}, [props.value]);return (React.createElement("div", { className: classNames("app-pin-digit", { focused: props.focused, hidden }) },React.createElement("span", { className: "app-pin-digit-value" }, props.value || "")));
};
const Pin = () => {const { userStatus, setUserStatusTo } = React.useContext(AppContext);const [pin, setPinTo] = React.useState("");const ref = React.useRef(null);React.useEffect(() => {if (userStatus === UserStatus.LoggingIn || userStatus === UserStatus.LogInError) {ref.current.focus();}else {setPinTo("");}}, [userStatus]);React.useEffect(() => {if (pin.length === 4) {const verify = async () => {try {setUserStatusTo(UserStatus.VerifyingLogIn);if (await LogInUtility.verify(pin)) {setUserStatusTo(UserStatus.LoggedIn);}}catch (err) {console.error(err);setUserStatusTo(UserStatus.LogInError);}};verify();}if (userStatus === UserStatus.LogInError) {setUserStatusTo(UserStatus.LoggingIn);}}, [pin]);const handleOnClick = () => {ref.current.focus();};const handleOnCancel = () => {setUserStatusTo(UserStatus.LoggedOut);};const handleOnChange = (e) => {if (e.target.value.length <= 4) {setPinTo(e.target.value.toString());}};const getCancelText = () => {return (React.createElement("span", { id: "app-pin-cancel-text", onClick: handleOnCancel }, "Cancel"));};const getErrorText = () => {if (userStatus === UserStatus.LogInError) {return (React.createElement("span", { id: "app-pin-error-text" }, "Invalid"));}};return (React.createElement("div", { id: "app-pin-wrapper" },React.createElement("input", { disabled: userStatus !== UserStatus.LoggingIn && userStatus !== UserStatus.LogInError, id: "app-pin-hidden-input", maxLength: 4, ref: ref, type: "number", value: pin, onChange: handleOnChange }),React.createElement("div", { id: "app-pin", onClick: handleOnClick },React.createElement(PinDigit, { focused: pin.length === 0, value: pin[0] }),React.createElement(PinDigit, { focused: pin.length === 1, value: pin[1] }),React.createElement(PinDigit, { focused: pin.length === 2, value: pin[2] }),React.createElement(PinDigit, { focused: pin.length === 3, value: pin[3] })),React.createElement("h3", { id: "app-pin-label" },"Enter PIN (1234) ",getErrorText()," ",getCancelText())));
};
const MenuSection = (props) => {const getContent = () => {if (props.scrollable) {return (React.createElement(ScrollableComponent, { className: "menu-section-content" }, props.children));}return (React.createElement("div", { className: "menu-section-content" }, props.children));};return (React.createElement("div", { id: props.id, className: "menu-section" },React.createElement("div", { className: "menu-section-title" },React.createElement("i", { className: props.icon }),React.createElement("span", { className: "menu-section-title-text" }, props.title)),getContent()));
};
const QuickNav = () => {const getItems = () => {return [{id: 1,label: "Weather"}, {id: 2,label: "Food"}, {id: 3,label: "Apps"}, {id: 4,label: "Movies"}].map((item) => {return (React.createElement("div", { key: item.id, className: "quick-nav-item clear-button" },React.createElement("span", { className: "quick-nav-item-label" }, item.label)));});};return (React.createElement(ScrollableComponent, { id: "quick-nav" }, getItems()));
};
const Weather = () => {const getDays = () => {return [{id: 1,name: "Mon",temperature: N.rand(60, 80),weather: WeatherType.Sunny}, {id: 2,name: "Tues",temperature: N.rand(60, 80),weather: WeatherType.Sunny}, {id: 3,name: "Wed",temperature: N.rand(60, 80),weather: WeatherType.Cloudy}, {id: 4,name: "Thurs",temperature: N.rand(60, 80),weather: WeatherType.Rainy}, {id: 5,name: "Fri",temperature: N.rand(60, 80),weather: WeatherType.Stormy}, {id: 6,name: "Sat",temperature: N.rand(60, 80),weather: WeatherType.Sunny}, {id: 7,name: "Sun",temperature: N.rand(60, 80),weather: WeatherType.Cloudy}].map((day) => {const getIcon = () => {switch (day.weather) {case WeatherType.Cloudy:return "fa-duotone fa-clouds";case WeatherType.Rainy:return "fa-duotone fa-cloud-drizzle";case WeatherType.Stormy:return "fa-duotone fa-cloud-bolt";case WeatherType.Sunny:return "fa-duotone fa-sun";}};return (React.createElement("div", { key: day.id, className: "day-card" },React.createElement("div", { className: "day-card-content" },React.createElement("span", { className: "day-weather-temperature" },day.temperature,React.createElement("span", { className: "day-weather-temperature-unit" }, "\u00B0F")),React.createElement("i", { className: classNames("day-weather-icon", getIcon(), day.weather.toLowerCase()) }),React.createElement("span", { className: "day-name" }, day.name))));});};return (React.createElement(MenuSection, { icon: "fa-solid fa-sun", id: "weather-section", scrollable: true, title: "How's it look out there?" }, getDays()));
};
const Tools = () => {const getTools = () => {return [{icon: "fa-solid fa-cloud-sun",id: 1,image: "https://images.unsplash.com/photo-1492011221367-f47e3ccd77a0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTV8fHdlYXRoZXJ8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",label: "Weather",name: "Cloudly"}, {icon: "fa-solid fa-calculator-simple",id: 2,image: "https://images.unsplash.com/photo-1587145820266-a5951ee6f620?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Y2FsY3VsYXRvcnxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60",label: "Calc",name: "Mathio"}, {icon: "fa-solid fa-piggy-bank",id: 3,image: "https://images.unsplash.com/photo-1579621970588-a35d0e7ab9b6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8OHx8YmFua3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60",label: "Bank",name: "Cashy"}, {icon: "fa-solid fa-plane",id: 4,image: "https://images.unsplash.com/photo-1436491865332-7a61a109cc05?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8YWlycGxhbmV8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",label: "Travel",name: "Fly-er-io-ly"}, {icon: "fa-solid fa-gamepad-modern",id: 5,image: "https://images.unsplash.com/photo-1612287230202-1ff1d85d1bdf?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8dmlkZW8lMjBnYW1lc3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60",label: "Games",name: "Gamey"}, {icon: "fa-solid fa-video",id: 6,image: "https://images.unsplash.com/photo-1578022761797-b8636ac1773c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTJ8fHZpZGVvJTIwY2hhdHxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60",label: "Video Chat",name: "Chatty"}].map((tool) => {const styles = {backgroundImage: `url(${tool.image})`};return (React.createElement("div", { key: tool.id, className: "tool-card" },React.createElement("div", { className: "tool-card-background background-image", style: styles }),React.createElement("div", { className: "tool-card-content" },React.createElement("div", { className: "tool-card-content-header" },React.createElement("span", { className: "tool-card-label" }, tool.label),React.createElement("span", { className: "tool-card-name" }, tool.name)),React.createElement("i", { className: classNames(tool.icon, "tool-card-icon") }))));});};return (React.createElement(MenuSection, { icon: "fa-solid fa-toolbox", id: "tools-section", title: "What's Appening?" }, getTools()));
};
const Restaurants = () => {const getRestaurants = () => {return [{desc: "The best burgers in town",id: 1,image: "https://images.unsplash.com/photo-1606131731446-5568d87113aa?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8YnVyZ2Vyc3xlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60",title: "Burgers"}, {desc: "The worst ice-cream around",id: 2,image: "https://images.unsplash.com/photo-1576506295286-5cda18df43e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8aWNlJTIwY3JlYW18ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",title: "Ice Cream"}, {desc: "This 'Za be gettin down",id: 3,image: "https://images.unsplash.com/photo-1590947132387-155cc02f3212?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Nnx8cGl6emF8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",title: "Pizza"}, {desc: "BBQ ain't need no rhyme",id: 4,image: "https://images.unsplash.com/photo-1529193591184-b1d58069ecdd?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8OXx8YmFyYmVxdWV8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",title: "BBQ"}].map((restaurant) => {const styles = {backgroundImage: `url(${restaurant.image})`};return (React.createElement("div", { key: restaurant.id, className: "restaurant-card background-image", style: styles },React.createElement("div", { className: "restaurant-card-content" },React.createElement("div", { className: "restaurant-card-content-items" },React.createElement("span", { className: "restaurant-card-title" }, restaurant.title),React.createElement("span", { className: "restaurant-card-desc" }, restaurant.desc)))));});};return (React.createElement(MenuSection, { icon: "fa-regular fa-pot-food", id: "restaurants-section", title: "Get it delivered!" }, getRestaurants()));
};
const Movies = () => {const getMovies = () => {return [{desc: "A tale of some people watching over a large portion of space.",id: 1,icon: "fa-solid fa-galaxy",image: "https://images.unsplash.com/photo-1596727147705-61a532a659bd?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bWFydmVsfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60",title: "Protectors of the Milky Way"}, {desc: "Some people leave their holes to disrupt some things.",id: 2,icon: "fa-solid fa-hat-wizard",image: "https://images.unsplash.com/photo-1535666669445-e8c15cd2e7d9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8bG9yZCUyMG9mJTIwdGhlJTIwcmluZ3N8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",title: "Hole People"}, {desc: "A boy with a dent in his head tries to stop a bad guy. And by bad I mean bad at winning.",id: 3,icon: "fa-solid fa-broom-ball",image: "https://images.unsplash.com/photo-1632266484284-a11d9e3a460a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTZ8fGhhcnJ5JTIwcG90dGVyfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60",title: "Pot of Hair"}, {desc: "A long drawn out story of some people fighting over some space. Cuz there isn't enough of it.",id: 4,icon: "fa-solid fa-starship-freighter",image: "https://images.unsplash.com/photo-1533613220915-609f661a6fe1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c3RhciUyMHdhcnN8ZW58MHx8MHx8&auto=format&fit=crop&w=500&q=60",title: "Area Fights"}].map((movie) => {const styles = {backgroundImage: `url(${movie.image})`};const id = `movie-card-${movie.id}`;return (React.createElement("div", { key: movie.id, id: id, className: "movie-card" },React.createElement("div", { className: "movie-card-background background-image", style: styles }),React.createElement("div", { className: "movie-card-content" },React.createElement("div", { className: "movie-card-info" },React.createElement("span", { className: "movie-card-title" }, movie.title),React.createElement("span", { className: "movie-card-desc" }, movie.desc)),React.createElement("i", { className: movie.icon }))));});};return (React.createElement(MenuSection, { icon: "fa-solid fa-camera-movie", id: "movies-section", scrollable: true, title: "Popcorn time!" }, getMovies()));
};
const UserStatusButton = (props) => {const { userStatus, setUserStatusTo } = React.useContext(AppContext);const handleOnClick = () => {setUserStatusTo(props.userStatus);};return (React.createElement("button", { id: props.id, className: "user-status-button clear-button", disabled: userStatus === props.userStatus, type: "button", onClick: handleOnClick },React.createElement("i", { className: props.icon })));
};
const Menu = () => {return (React.createElement("div", { id: "app-menu" },React.createElement("div", { id: "app-menu-content-wrapper" },React.createElement("div", { id: "app-menu-content" },React.createElement("div", { id: "app-menu-content-header" },React.createElement("div", { className: "app-menu-content-header-section" },React.createElement(Info, { id: "app-menu-info" }),React.createElement(Reminder, null)),React.createElement("div", { className: "app-menu-content-header-section" },React.createElement(UserStatusButton, { icon: "fa-solid fa-arrow-right-from-arc", id: "sign-out-button", userStatus: UserStatus.LoggedOut }))),React.createElement(QuickNav, null),React.createElement("a", { id: "youtube-link", className: "clear-button", href: "https://www.youtube.com/c/Hyperplexed", target: "_blank" },React.createElement("i", { className: "fa-brands fa-youtube" }),React.createElement("span", null, "Hyperplexed")),React.createElement(Weather, null),React.createElement(Restaurants, null),React.createElement(Tools, null),React.createElement(Movies, null)))));
};
const Background = () => {const { userStatus, setUserStatusTo } = React.useContext(AppContext);const handleOnClick = () => {if (userStatus === UserStatus.LoggedOut) {setUserStatusTo(UserStatus.LoggingIn);}};return (React.createElement("div", { id: "app-background", onClick: handleOnClick },React.createElement("div", { id: "app-background-image", className: "background-image" })));
};
const Loading = () => {return (React.createElement("div", { id: "app-loading-icon" },React.createElement("i", { className: "fa-solid fa-spinner-third" })));
};
const AppContext = React.createContext(null);
const App = () => {const [userStatus, setUserStatusTo] = React.useState(UserStatus.LoggedOut);const getStatusClass = () => {return userStatus.replace(/\s+/g, "-").toLowerCase();};return (React.createElement(AppContext.Provider, { value: { userStatus, setUserStatusTo } },React.createElement("div", { id: "app", className: getStatusClass() },React.createElement(Info, { id: "app-info" }),React.createElement(Pin, null),React.createElement(Menu, null),React.createElement(Background, null),React.createElement("div", { id: "sign-in-button-wrapper" },React.createElement(UserStatusButton, { icon: "fa-solid fa-arrow-right-to-arc", id: "sign-in-button", userStatus: UserStatus.LoggingIn })),React.createElement(Loading, null))));
};
ReactDOM.render(React.createElement(App, null), document.getElementById("root"));
部分运行截图:
需要源码文件可以后台私信我。