Merge branch 'supporters_page' into develop
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
.scroll_container {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import styles from "./Supporters.module.scss";
|
||||
import { SupportUsContainer } from "./support_us_container/SupportUsContainer";
|
||||
import { SupportersContainer } from "./supporters_container/SupportersContainer";
|
||||
import { useSupporters } from "@logics_configs";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const Supporters = () => {
|
||||
const { asyncFetchSupportersData } = useSupporters();
|
||||
|
||||
useEffect(() => {
|
||||
asyncFetchSupportersData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<SupportUsContainer />
|
||||
<SupportersContainer />
|
||||
<div className={styles.supportersWrapper}>
|
||||
<SupportersContainer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -5,4 +5,17 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.supportersWrapper {
|
||||
opacity: 0;
|
||||
animation: fadeIn 0.8s ease-in-out 1.6s forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,44 @@
|
||||
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||
|
||||
@keyframes revealTopImg {
|
||||
0% {
|
||||
clip-path: inset(0 50% 0 50%);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
clip-path: inset(0 0 0 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceIn {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
60% {
|
||||
transform: translateY(-10%);
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
transform: translateY(10%);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes expandWidth {
|
||||
0% {
|
||||
transform: scaleX(0);
|
||||
}
|
||||
100% {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
.support_us_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -6,39 +47,56 @@
|
||||
gap: 1.4rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.support_buttons_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
gap: 1.2rem;
|
||||
}
|
||||
|
||||
.top_img {
|
||||
width: 100%;
|
||||
animation: revealTopImg 0.8s ease forwards;
|
||||
}
|
||||
|
||||
.lines_container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 3.6rem;
|
||||
}
|
||||
|
||||
.line_basic, .line_fuwa, .line_mochi, .line_mogu {
|
||||
.line_basic,
|
||||
.line_fuwa,
|
||||
.line_mochi,
|
||||
.line_mogu {
|
||||
width: 8.6rem;
|
||||
height: 0.2rem;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
.line_basic {
|
||||
background-color: var(--dark_800_color);
|
||||
animation: expandWidth 1s $progress_ease 0.4s forwards;
|
||||
}
|
||||
|
||||
.line_fuwa {
|
||||
background-color: #5788a2;
|
||||
animation: expandWidth 1s $progress_ease 0.6s forwards;
|
||||
}
|
||||
|
||||
.line_mochi {
|
||||
background-color: var(--received_300_color);
|
||||
animation: expandWidth 1s $progress_ease 0.8s forwards;
|
||||
}
|
||||
|
||||
.line_mogu {
|
||||
background-color: var(--dark_basic_text_color);
|
||||
animation: expandWidth 1s $progress_ease 1s forwards;
|
||||
}
|
||||
|
||||
.support_us_button_wrapper {
|
||||
@@ -52,8 +110,21 @@
|
||||
.support_button {
|
||||
position: relative;
|
||||
padding: 1.2rem 1.6rem;
|
||||
opacity: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.support_button:nth-child(1) {
|
||||
animation: bounceIn 0.4s ease-out 1.2s forwards;
|
||||
}
|
||||
.support_button:nth-child(2) {
|
||||
animation: bounceIn 0.4s ease-out 1.4s forwards;
|
||||
}
|
||||
.support_button:nth-child(3) {
|
||||
animation: bounceIn 0.4s ease-out 1.6s forwards;
|
||||
}
|
||||
|
||||
|
||||
.support_img {
|
||||
&.fanbox_logo {
|
||||
height: 1.8rem;
|
||||
@@ -64,8 +135,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.spiral_top::before,
|
||||
.spiral_top::after,
|
||||
.spiral_bottom::before,
|
||||
@@ -74,6 +143,7 @@
|
||||
position: absolute;
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.spiral_top::before {
|
||||
background: var(--dark_800_color);
|
||||
box-shadow: 0 0 0.4rem 0 var(--dark_800_color);
|
||||
@@ -116,20 +186,17 @@
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.support_button:hover
|
||||
.spiral_top::before {
|
||||
|
||||
.support_button:hover .spiral_top::before {
|
||||
height: 100%;
|
||||
}
|
||||
.support_button:hover
|
||||
.spiral_top::after {
|
||||
.support_button:hover .spiral_top::after {
|
||||
width: 100%;
|
||||
}
|
||||
.support_button:hover
|
||||
.spiral_bottom::before {
|
||||
.support_button:hover .spiral_bottom::before {
|
||||
width: 100%;
|
||||
}
|
||||
.support_button:hover
|
||||
.spiral_bottom::after {
|
||||
.support_button:hover .spiral_bottom::after {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -141,10 +208,12 @@
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.vrct_supporters_title {
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.vrct_supporters_desc {
|
||||
font-size: 1.4rem;
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,26 @@
|
||||
import styles from "./SupportersContainer.module.scss";
|
||||
import { useState, useEffect } from "react";
|
||||
import vrct_supporters_title from "@images/supporters/vrct_supporters_title.png";
|
||||
import { SupportersWrapper } from "./supporters_wrapper/SupportersWrapper";
|
||||
import { clsx } from "clsx";
|
||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
||||
import { useSupporters } from "@logics_configs";
|
||||
import { supporters_images_url } from "@ui_configs";
|
||||
import vrct_supporters_title from "@images/supporters/vrct_supporters_title.png";
|
||||
|
||||
export const SupportersContainer = () => {
|
||||
const { currentSupportersData } = useSupporters();
|
||||
|
||||
if (currentSupportersData.state === "error")
|
||||
return <div>Failed to retrieve data.</div>;
|
||||
|
||||
if (currentSupportersData.state === "pending" || currentSupportersData.data === null)
|
||||
return <div>Loading...</div>;
|
||||
|
||||
return (
|
||||
<div className={styles.supporters_container}>
|
||||
<img className={styles.vrct_supporters_title} src={vrct_supporters_title} />
|
||||
<ProgressBar />
|
||||
<div className={styles.vrct_supporters_title_wrapper}>
|
||||
<img className={styles.vrct_supporters_title} src={vrct_supporters_title}/>
|
||||
<img className={styles.calc_period} src={`${supporters_images_url}/calc_period_label.png`}/>
|
||||
</div>
|
||||
<SupportersWrapper />
|
||||
<ProgressBar />
|
||||
<p className={styles.vrct_supporters_desc_end}>{`みなさんのおかげで、みしゃ社長は布団で寝ることを許され(in開発室) しいなは喜び庭駆け回っています!!!ふわもちもぐもぐです!ありがとうございます。これからもまだまだ進化するVRCTをどうかよろしくお願いします!\nThanks to everyone, Misha has been granted the privilege of sleeping in a proper bed (in the development room), and Shiina is so happy, running around the yard! Fuwa-mochi-mogu-mogu! Thank you so much! We hope you'll continue to support the ever-evolving VRCT!`}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ProgressBar = () => {
|
||||
const [is_active, setIsActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsActive(true);
|
||||
const interval = setInterval(() => {
|
||||
setIsActive(false);
|
||||
setTimeout(() => setIsActive(true), 50);
|
||||
}, SHUFFLE_INTERVAL_TIME);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.progress_bar, {
|
||||
[styles.progress_bar_active]: is_active,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -5,22 +5,23 @@
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.vrct_supporters_title_wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
.vrct_supporters_title {
|
||||
height: 6rem;
|
||||
height: 4.2rem;
|
||||
}
|
||||
.calc_period {
|
||||
height: 1.6rem;
|
||||
}
|
||||
|
||||
.vrct_supporters_desc_end {
|
||||
font-size: 1.4rem;
|
||||
margin-top: 2rem;
|
||||
color: var(--dark_300_color);
|
||||
}
|
||||
|
||||
.progress_bar {
|
||||
height: 0.2rem;
|
||||
width: 0%;
|
||||
&.progress_bar_active {
|
||||
transition: width 20000ms linear;
|
||||
background-color: var(--primary_400_color);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -6,222 +6,162 @@ import { shuffleArray, randomIntMinMax, randomMinMax } from "@utils";
|
||||
|
||||
import {
|
||||
useSettingBoxScrollPosition,
|
||||
} from "@logics_configs"
|
||||
useSupporters,
|
||||
} from "@logics_configs";
|
||||
|
||||
import json_data from "./data.json";
|
||||
|
||||
const target_supporting_month = "2025-01";
|
||||
const calc_support_period = ["2024-10", "2024-11", "2024-12", "2025-01"];
|
||||
import { supporters_images_url } from "@ui_configs";
|
||||
|
||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
||||
|
||||
|
||||
|
||||
const and_you_data = {
|
||||
supporter_id: "and_you",
|
||||
};
|
||||
|
||||
|
||||
const getImagePath = (images, file_name) => {
|
||||
const image_path = Object.keys(images).find((path) => path.endsWith(`${file_name}.png`));
|
||||
return image_path ? images[image_path]?.default : null;
|
||||
};
|
||||
|
||||
const image_sets = {
|
||||
supporter_cards: import.meta.glob("@images/supporters/supporter_cards/*.png", { eager: true }),
|
||||
chato_expressions: import.meta.glob("@images/supporters/chato_expressions/*.png", { eager: true }),
|
||||
supporters_labels: import.meta.glob("@images/supporters/supporters_labels/*.png", { eager: true }),
|
||||
supporters_icons: import.meta.glob("@images/supporters/supporters_icons/*.png", { eager: true }),
|
||||
supporter_cards: `${supporters_images_url}/supporter_cards/`,
|
||||
chato_expressions: `${supporters_images_url}/chato_expressions/`,
|
||||
supporters_labels: `${supporters_images_url}/supporters_labels/`,
|
||||
supporters_icons: `${supporters_images_url}/supporters_icons/`,
|
||||
};
|
||||
|
||||
const getSupporterCard = (plan_name) => {
|
||||
const card_map = {
|
||||
"もぐもぐ_2000": "mogu_card",
|
||||
"もちもち_1000": "mochi_card",
|
||||
"ふわふわ_500": "fuwa_card",
|
||||
"Basic_300": "basic_card",
|
||||
"mogu_2000": "mogu_card",
|
||||
"mochi_1000": "mochi_card",
|
||||
"fuwa_500": "fuwa_card",
|
||||
"basic_300": "basic_card",
|
||||
};
|
||||
return getImagePath(image_sets.supporter_cards, card_map[plan_name] || "basic_card");
|
||||
if (!card_map[plan_name]) return `${image_sets.supporter_cards}basic_card.png`;
|
||||
|
||||
return `${image_sets.supporter_cards}${card_map[plan_name]}.png`;
|
||||
};
|
||||
|
||||
const getChatoExpressionsPath = (file_name) =>
|
||||
getImagePath(image_sets.chato_expressions, file_name);
|
||||
const getChatoExpressionsPath = (file_name) => `${image_sets.chato_expressions}${file_name}.png`;
|
||||
const getSupportersLabelsPath = (file_name) => `${image_sets.supporters_labels}${file_name}.png`;
|
||||
const getSupportersIconsPath = (file_name) => `${image_sets.supporters_icons}${file_name}.png`;
|
||||
|
||||
const getSupportersLabelsPath = (file_name) =>
|
||||
getImagePath(image_sets.supporters_labels, file_name);
|
||||
|
||||
const getSupportersIconsPath = (file_name) =>
|
||||
getImagePath(image_sets.supporters_icons, file_name);
|
||||
|
||||
const chato_ex_count = Object.keys(image_sets.chato_expressions).length;
|
||||
|
||||
export const SupportersWrapper = () => {
|
||||
const { saveScrollPosition, restoreScrollPosition } = useSettingBoxScrollPosition();
|
||||
const { currentSupportersData } = useSupporters();
|
||||
|
||||
let credit_pending_count = 0;
|
||||
const filtered_data = json_data.filter((supporter) => {
|
||||
if (!supporter.supporter_id) return false;
|
||||
const [json_data, setJsonData] = useState();
|
||||
const [supportersData, setSupportersData] = useState([]);
|
||||
const [chatoExpressions, setChatoExpressions] = useState([]);
|
||||
|
||||
const months = Object.keys(supporter).filter((key) => key.match(/^\d{4}-\d{2}$/));
|
||||
const has_valid_month = months.some((month) => supporter[month]);
|
||||
if (!has_valid_month) return false;
|
||||
|
||||
const basic_300_months = months.filter((month) => supporter[month] === "Basic_300");
|
||||
const has_special_plan = months.some((month) => ["ふわふわ_500", "もちもち_1000", "もぐもぐ_2000"].includes(supporter[month]));
|
||||
|
||||
if (basic_300_months.length === 1 && !has_special_plan) {
|
||||
credit_pending_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const grouped_data = {
|
||||
もぐもぐ_2000: [],
|
||||
もちもち_1000: [],
|
||||
ふわふわ_500: [],
|
||||
Basic_300: [],
|
||||
empty: [],
|
||||
and_you: [],
|
||||
};
|
||||
|
||||
filtered_data.forEach((supporter) => {
|
||||
const value = supporter[target_supporting_month] || "empty";
|
||||
if (grouped_data[value]) {
|
||||
grouped_data[value].push(supporter);
|
||||
} else {
|
||||
grouped_data["empty"].push(supporter);
|
||||
}
|
||||
});
|
||||
|
||||
const [supportersData, setSupportersData] = useState(() => [
|
||||
...grouped_data["もぐもぐ_2000"],
|
||||
...grouped_data["もちもち_1000"],
|
||||
...grouped_data["ふわふわ_500"],
|
||||
...grouped_data["Basic_300"],
|
||||
...grouped_data["empty"],
|
||||
and_you_data,
|
||||
]);
|
||||
useEffect(() => {
|
||||
setJsonData(currentSupportersData.data);
|
||||
}, [currentSupportersData.data]);
|
||||
|
||||
|
||||
const [chatoExpressions, setChatoExpressions] = useState(() =>
|
||||
supportersData.map(() =>
|
||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
||||
)
|
||||
);
|
||||
const supporters_settings = currentSupportersData.data.supporters_settings;
|
||||
|
||||
const calc_support_period = supporters_settings.calc_support_period;
|
||||
const target_supporting_month = calc_support_period.at(-1);
|
||||
const chato_ex_count = supporters_settings.chato_ex_count;
|
||||
const last_updated_local_date = new Date(supporters_settings.last_updated_utc_date)?.toString();
|
||||
|
||||
const recalcAndUpdateSupporters = useCallback(() => {
|
||||
if (!json_data) return;
|
||||
|
||||
let credit_pending_count = 0;
|
||||
const newGroupedData = {
|
||||
"mogu_2000": [],
|
||||
"mochi_1000": [],
|
||||
"fuwa_500": [],
|
||||
"basic_300": [],
|
||||
"empty": [],
|
||||
"and_you": [],
|
||||
};
|
||||
|
||||
const filtered_data = json_data.supporters_data.filter((supporter) => {
|
||||
if (!supporter.supporter_id) return false;
|
||||
|
||||
const months = Object.keys(supporter).filter((key) => calc_support_period.includes(key));
|
||||
const has_valid_month = months.some((month) => supporter[month]);
|
||||
if (!has_valid_month) return false;
|
||||
|
||||
const basic_300_months = months.filter(
|
||||
(month) => supporter[month] === "basic_300"
|
||||
);
|
||||
const has_special_plan = months.some((month) =>
|
||||
["fuwa_500", "mochi_1000", "mogu_2000"].includes(supporter[month])
|
||||
);
|
||||
|
||||
if (basic_300_months.length === 1 && !has_special_plan) {
|
||||
credit_pending_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
filtered_data.forEach((supporter) => {
|
||||
const value = supporter[target_supporting_month] || "empty";
|
||||
if (newGroupedData[value]) {
|
||||
newGroupedData[value].push(supporter);
|
||||
} else {
|
||||
newGroupedData["empty"].push(supporter);
|
||||
}
|
||||
});
|
||||
|
||||
const shuffleSupporters = useCallback(() => {
|
||||
saveScrollPosition();
|
||||
const newSupportersData = [
|
||||
...shuffleArray(grouped_data["もぐもぐ_2000"]),
|
||||
...shuffleArray(grouped_data["もちもち_1000"]),
|
||||
...shuffleArray(grouped_data["ふわふわ_500"]),
|
||||
...shuffleArray(grouped_data["Basic_300"]),
|
||||
...shuffleArray(grouped_data["empty"]),
|
||||
...shuffleArray(newGroupedData["mogu_2000"]),
|
||||
...shuffleArray(newGroupedData["mochi_1000"]),
|
||||
...shuffleArray(newGroupedData["fuwa_500"]),
|
||||
...shuffleArray(newGroupedData["basic_300"]),
|
||||
...shuffleArray(newGroupedData["empty"]),
|
||||
and_you_data,
|
||||
];
|
||||
setSupportersData(newSupportersData);
|
||||
|
||||
setSupportersData(newSupportersData);
|
||||
|
||||
setChatoExpressions(
|
||||
newSupportersData.map(() =>
|
||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
||||
getChatoExpressionsPath(
|
||||
`chato_expression_${randomIntMinMax(1, chato_ex_count)}`
|
||||
)
|
||||
)
|
||||
);
|
||||
setTimeout(() => restoreScrollPosition(), 0);
|
||||
}, [grouped_data]);
|
||||
|
||||
const renderImages = () => {
|
||||
return supportersData.map((item, index) => {
|
||||
const target_plan = item[target_supporting_month];
|
||||
const img_src = getSupporterCard(target_plan);
|
||||
const is_default_icon = item.supporter_icon_id === "";
|
||||
const is_icon_plan = ["もぐもぐ_2000", "もちもち_1000"].includes(target_plan);
|
||||
const is_and_you = item.supporter_id === "and_you";
|
||||
|
||||
const random_delay = `${randomMinMax(0.1, 6).toFixed(1)}s`;
|
||||
|
||||
|
||||
|
||||
const file_name = is_and_you ? "and_you" : `supporter_${item.supporter_id}`;
|
||||
const label_img_src = getSupportersLabelsPath(file_name);
|
||||
const icon_img_src = getSupportersIconsPath(`supporter_icon_${item.supporter_icon_id}`);
|
||||
|
||||
const supporter_label_component_classname = clsx(styles.supporter_label_component, {
|
||||
[styles.is_icon_plan]: is_icon_plan,
|
||||
});
|
||||
|
||||
const supporterLabelComponent = () => (
|
||||
<div className={supporter_label_component_classname}>
|
||||
{is_icon_plan && (
|
||||
<div className={styles.supporter_icon_wrapper}>
|
||||
{is_default_icon ? (
|
||||
<img
|
||||
className={styles.default_chato_expression_image}
|
||||
src={chatoExpressions[index]}
|
||||
/>
|
||||
) : (
|
||||
<img className={styles.supporter_icon} src={icon_img_src} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<img className={styles.supporter_label_image} src={label_img_src} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
|
||||
const supporter_image_wrapper_classname = clsx(styles.supporter_image_wrapper, {
|
||||
[styles.mogu_image]: target_plan === "もぐもぐ_2000",
|
||||
});
|
||||
|
||||
return is_and_you ? (
|
||||
<a href="#support_us_container" key={item.supporter_id}>
|
||||
<div className={styles.supporter_image_container}>
|
||||
<div
|
||||
className={supporter_image_wrapper_classname}
|
||||
style={{ "--delay": random_delay }}
|
||||
>
|
||||
<img className={styles.supporter_image} src={img_src} />
|
||||
{supporterLabelComponent()}
|
||||
<AndYouIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
): img_src ? (
|
||||
<div key={item.supporter_id} className={styles.supporter_image_container}>
|
||||
<div
|
||||
className={supporter_image_wrapper_classname}
|
||||
style={{ "--delay": random_delay }}
|
||||
>
|
||||
<img className={styles.supporter_image} src={img_src} />
|
||||
{supporterLabelComponent()}
|
||||
</div>
|
||||
<SupporterPeriodContainer settings={item}/>
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
};
|
||||
|
||||
}, [json_data]);
|
||||
|
||||
useEffect(() => {
|
||||
recalcAndUpdateSupporters();
|
||||
}, [json_data, recalcAndUpdateSupporters]);
|
||||
|
||||
const shuffleSupporters = useCallback(() => {
|
||||
if (!json_data) return;
|
||||
saveScrollPosition();
|
||||
recalcAndUpdateSupporters();
|
||||
setTimeout(() => restoreScrollPosition(), 0);
|
||||
}, [json_data, recalcAndUpdateSupporters, saveScrollPosition, restoreScrollPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
shuffleSupporters();
|
||||
const interval = setInterval(() => {
|
||||
shuffleSupporters();
|
||||
}, SHUFFLE_INTERVAL_TIME);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
}, [shuffleSupporters]);
|
||||
|
||||
return (
|
||||
<div className={styles.supporters_wrapper}>{renderImages()}</div>
|
||||
<div className={styles.container}>
|
||||
<ProgressBar />
|
||||
<div className={styles.supporters_wrapper}>
|
||||
<SupporterCardsComponent
|
||||
supportersData={supportersData}
|
||||
chatoExpressions={chatoExpressions}
|
||||
target_supporting_month={target_supporting_month}
|
||||
calc_support_period={calc_support_period}
|
||||
/>
|
||||
</div>
|
||||
<p className={styles.last_updated_local_date}>{`Last updated date:\n${last_updated_local_date}`}</p>
|
||||
<ProgressBar />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const AndYouIcon = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -237,29 +177,136 @@ const AndYouIcon = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SupporterPeriodContainer = ({settings}) => {
|
||||
const SupporterCardsComponent = ({ supportersData, chatoExpressions, target_supporting_month, calc_support_period }) => {
|
||||
return supportersData.map((item, index) => {
|
||||
const target_plan = item[target_supporting_month];
|
||||
|
||||
const img_src = getSupporterCard(target_plan);
|
||||
|
||||
const is_and_you = item.supporter_id === "and_you";
|
||||
|
||||
const random_delay = `${randomMinMax(0.1, 6).toFixed(1)}s`;
|
||||
|
||||
const supporter_image_wrapper_classname = clsx(
|
||||
styles.supporter_image_wrapper,
|
||||
{
|
||||
[styles.mogu_image]: target_plan === "mogu_2000",
|
||||
}
|
||||
);
|
||||
|
||||
return is_and_you ? (
|
||||
<a href="#support_us_container" key={item.supporter_id}>
|
||||
<div className={styles.supporter_image_container}>
|
||||
<div
|
||||
className={supporter_image_wrapper_classname}
|
||||
style={{ "--delay": random_delay }}
|
||||
>
|
||||
<img
|
||||
className={styles.supporter_image}
|
||||
src={img_src}
|
||||
alt="supporter"
|
||||
/>
|
||||
<SupporterLabelComponent
|
||||
target_plan={target_plan}
|
||||
item={item}
|
||||
chatoExpressions={chatoExpressions}
|
||||
/>
|
||||
<AndYouIcon />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
) : img_src ? (
|
||||
<div key={item.supporter_id} className={styles.supporter_image_container}>
|
||||
<div
|
||||
className={supporter_image_wrapper_classname}
|
||||
style={{ "--delay": random_delay }}
|
||||
>
|
||||
<img
|
||||
className={styles.supporter_image}
|
||||
src={img_src}
|
||||
alt="supporter"
|
||||
/>
|
||||
<SupporterLabelComponent
|
||||
target_plan={target_plan}
|
||||
item={item}
|
||||
chato_src={chatoExpressions[index]}
|
||||
index={index}
|
||||
/>
|
||||
</div>
|
||||
<SupporterPeriodContainer settings={item} calc_support_period={calc_support_period}/>
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
};
|
||||
|
||||
const SupporterLabelComponent = ({ item, target_plan, chato_src }) => {
|
||||
const is_icon_plan = ["mogu_2000", "mochi_1000"].includes(
|
||||
target_plan
|
||||
);
|
||||
|
||||
const supporter_label_component_classname = clsx(
|
||||
styles.supporter_label_component,
|
||||
{
|
||||
[styles.is_icon_plan]: is_icon_plan,
|
||||
}
|
||||
);
|
||||
|
||||
const is_and_you = item.supporter_id === "and_you";
|
||||
const is_default_icon = item.supporter_icon_id === "";
|
||||
|
||||
const file_name = is_and_you ? "and_you" : `supporter_${item.supporter_id}`;
|
||||
const label_img_src = getSupportersLabelsPath(file_name);
|
||||
const icon_img_src = getSupportersIconsPath(
|
||||
`supporter_icon_${item.supporter_icon_id}`
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={supporter_label_component_classname}>
|
||||
{is_icon_plan && (
|
||||
<div className={styles.supporter_icon_wrapper}>
|
||||
{is_default_icon ? (
|
||||
<img
|
||||
className={styles.default_chato_expression_image}
|
||||
src={chato_src}
|
||||
alt="chato expression"
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
className={styles.supporter_icon}
|
||||
src={icon_img_src}
|
||||
alt="supporter icon"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
className={styles.supporter_label_image}
|
||||
src={label_img_src}
|
||||
alt="supporter label"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SupporterPeriodContainer = ({ settings, calc_support_period }) => {
|
||||
const period_data = extractKeys(settings, calc_support_period);
|
||||
|
||||
return (
|
||||
<div className={styles.supporter_period_container}>
|
||||
{Object.entries(period_data).map(([key, item], index) => {
|
||||
if (item === "") return null;
|
||||
const class_name = clsx(styles.period_box, {
|
||||
[styles.mogu_bar]: item === "もぐもぐ_2000",
|
||||
[styles.mochi_bar]: item === "もちもち_1000",
|
||||
[styles.fuwa_bar]: item === "ふわふわ_500",
|
||||
[styles.basic_bar]: item === "Basic_300",
|
||||
[styles.mogu_bar]: item === "mogu_2000",
|
||||
[styles.mochi_bar]: item === "mochi_1000",
|
||||
[styles.fuwa_bar]: item === "fuwa_500",
|
||||
[styles.basic_bar]: item === "basic_300",
|
||||
});
|
||||
|
||||
return <div key={index} className={class_name}></div>
|
||||
return <div key={index} className={class_name}></div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const extractKeys = (data, keys_to_extract) => {
|
||||
const result = {};
|
||||
for (const key of keys_to_extract) {
|
||||
@@ -268,4 +315,31 @@ const extractKeys = (data, keys_to_extract) => {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
const ProgressBar = () => {
|
||||
const [is_active, setIsActive] = useState(false);
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
setIsActive(true);
|
||||
});
|
||||
});
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setIsActive(false);
|
||||
setTimeout(() => setIsActive(true), 50);
|
||||
}, SHUFFLE_INTERVAL_TIME);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.progress_bar, {
|
||||
[styles.progress_bar_active]: is_active,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,11 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.supporters_wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -218,4 +226,22 @@
|
||||
&.basic_bar {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.progress_bar {
|
||||
height: 0.2rem;
|
||||
width: 0%;
|
||||
&.progress_bar_active {
|
||||
transition: width 20000ms linear;
|
||||
background-color: var(--primary_400_color);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.last_updated_local_date {
|
||||
font-size: 1rem;
|
||||
color: var(--dark_800_color);
|
||||
width: 100%;
|
||||
text-align: end;
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
[
|
||||
{
|
||||
"supporter_id": 1,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "Basic_300",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 2,
|
||||
"supporter_icon_id": 8,
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 3,
|
||||
"supporter_icon_id": 4,
|
||||
"2024-10": "もちもち_1000",
|
||||
"2024-11": "もちもち_1000",
|
||||
"2024-12": "もちもち_1000",
|
||||
"2025-01": "もちもち_1000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 4,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 5,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "ふわふわ_500",
|
||||
"2024-11": "ふわふわ_500",
|
||||
"2024-12": "ふわふわ_500",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 6,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "ふわふわ_500",
|
||||
"2024-11": "ふわふわ_500",
|
||||
"2024-12": "ふわふわ_500",
|
||||
"2025-01": "ふわふわ_500",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 7,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "ふわふわ_500",
|
||||
"2024-11": "ふわふわ_500",
|
||||
"2024-12": "ふわふわ_500",
|
||||
"2025-01": "ふわふわ_500",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 8,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "Basic_300",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 9,
|
||||
"supporter_icon_id": 6,
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 10,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "Basic_300",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 11,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 12,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 13,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "Basic_300",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 14,
|
||||
"supporter_icon_id": 2,
|
||||
"2024-10": "もちもち_1000",
|
||||
"2024-11": "もちもち_1000",
|
||||
"2024-12": "もちもち_1000",
|
||||
"2025-01": "もちもち_1000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 15,
|
||||
"supporter_icon_id": 5,
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 16,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 17,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "もちもち_1000",
|
||||
"2024-11": "もちもち_1000",
|
||||
"2024-12": "もちもち_1000",
|
||||
"2025-01": "もちもち_1000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 18,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 19,
|
||||
"supporter_icon_id": 1,
|
||||
"2024-10": "もぐもぐ_2000",
|
||||
"2024-11": "もぐもぐ_2000",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 20,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "Basic_300",
|
||||
"2024-11": "Basic_300",
|
||||
"2024-12": "Basic_300",
|
||||
"2025-01": "ふわふわ_500",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 21,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "ふわふわ_500",
|
||||
"2024-12": "ふわふわ_500",
|
||||
"2025-01": "ふわふわ_500",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 22,
|
||||
"supporter_icon_id": 3,
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "もぐもぐ_2000",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 23,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "ふわふわ_500",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 24,
|
||||
"supporter_icon_id": 7,
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 25,
|
||||
"supporter_icon_id": 9,
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "もぐもぐ_2000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 26,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "もちもち_1000",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 27,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 28,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "Basic_300",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 29,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": 30,
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
},
|
||||
{
|
||||
"supporter_id": "",
|
||||
"supporter_icon_id": "",
|
||||
"2024-10": "",
|
||||
"2024-11": "",
|
||||
"2024-12": "",
|
||||
"2025-01": "",
|
||||
"2025-02": "",
|
||||
"2025-03": "",
|
||||
"2025-04": ""
|
||||
}
|
||||
]
|
||||
@@ -1,4 +1,5 @@
|
||||
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||
// Duplicated
|
||||
|
||||
.container {
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user