diff --git a/public/cats/spring/sit.gif b/public/cats/spring/sit.gif new file mode 100644 index 00000000..da9ec417 Binary files /dev/null and b/public/cats/spring/sit.gif differ diff --git a/public/cats/spring/sit.webm b/public/cats/spring/sit.webm new file mode 100644 index 00000000..e69de29b diff --git a/public/create/springFlower.svg b/public/create/springFlower.svg new file mode 100644 index 00000000..eb3ca84b --- /dev/null +++ b/public/create/springFlower.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/preview/Spring.png b/public/preview/Spring.png new file mode 100644 index 00000000..9e50be4c Binary files /dev/null and b/public/preview/Spring.png differ diff --git a/src/app/create/page.tsx b/src/app/create/page.tsx index a59d4cac..39ded523 100644 --- a/src/app/create/page.tsx +++ b/src/app/create/page.tsx @@ -22,7 +22,7 @@ export default function Create() { const [isNext, setIsNext] = useState(false); const [preview, setPreview] = useState(1); - const themelist = ['sul', 'night', 'peach', 'lilac', 'chris', 'mas']; + const themelist = ['spring', 'night', 'peach', 'lilac', 'sul']; const handleTitleSwitch = (value: string) => { setIsOff((prevIsOff) => (prevIsOff === value ? '' : value)); @@ -40,6 +40,8 @@ export default function Create() { const data = { theme: themelist[preview - 1], title: `${title}`, + public: false, + // 백엔드와 규격을 맞추기위해 임시로 넣어둔 값 }; axiosPostBoard(data) .then((res) => { diff --git a/src/app/personal/[id]/page.tsx b/src/app/personal/[id]/page.tsx index 0270a1e2..bdb19f8a 100644 --- a/src/app/personal/[id]/page.tsx +++ b/src/app/personal/[id]/page.tsx @@ -16,6 +16,7 @@ import { WriterButtonLayer, } from '@/component/Personal'; import FirstContent from '@/component/Personal/FirstContent'; +import SakuraAnimation from '@/component/Personal/SakuraAnimation'; import { useLogin } from '@/hooks/useLogin'; import useModal from '@/hooks/useModal'; import { useScroll } from '@/hooks/useScroll'; @@ -25,7 +26,7 @@ import { board } from '@/types/boards'; import { History } from '@/types/history'; import { personalPage } from '@/types/mixpanel'; import { noneTheme, themeState } from '@/types/theme'; -import { chris, lilac, mas, night, peach, sul } from '@/types/theme'; +import { chris, lilac, mas, night, peach, spring, sul } from '@/types/theme'; import { axiosGetBoard, axoisDeleteContents } from '@/utils/apiInterface'; import { confirm } from '@/utils/confirm'; import { defaultState } from '@/utils/theme/default'; @@ -43,8 +44,8 @@ export default function Personal({ params }: { params: { id: string } }) { const { isHidden, setIsHidden } = useScroll(); const [checkedSet, setCheckedSet] = useState(new Set()); const [openModal, closeModal] = useModal(); - - const addHistory = ()=>{ + + const addHistory = () => { const timestamp = () => { var now = new Date(); now.setHours(now.getHours() + 9); @@ -70,7 +71,7 @@ export default function Personal({ params }: { params: { id: string } }) { title: title, }); localStorage.setItem('history', JSON.stringify(historyArray)); - } + }; useEffect(() => { const token = localStorage.getItem('strcat_token'); @@ -202,8 +203,9 @@ export default function Personal({ params }: { params: { id: string } }) { return ( <>
-
+
+
{ if (themeName === 'peach') return peach; if (themeName === 'lilac') return lilac; if (themeName === 'sul') return sul; + if (themeName === 'spring') return spring; return night; }; diff --git a/src/component/Common/Icon/PhotoPreview.tsx b/src/component/Common/Icon/PhotoPreview.tsx index 0b42aec1..ba0b932e 100644 --- a/src/component/Common/Icon/PhotoPreview.tsx +++ b/src/component/Common/Icon/PhotoPreview.tsx @@ -30,4 +30,5 @@ const getColor = (color: string) => { if (color === 'bg-strcat-lilac') return '#D7C4FF'; if (color === 'bg-strcat-chris') return '#246F50'; if (color === 'bg-strcat-mas') return '#DE6565'; + if (color === 'bg-strcat-spring') return '#FFBACF'; }; diff --git a/src/component/Create/ThemeSelect.tsx b/src/component/Create/ThemeSelect.tsx index bbec1fc3..50950c19 100644 --- a/src/component/Create/ThemeSelect.tsx +++ b/src/component/Create/ThemeSelect.tsx @@ -18,11 +18,11 @@ export default function ThemeSelect({ defaultState, }: Props) { return ( -
+
{themes.map((theme) => (
setPreview(theme.id)} > -
+
{theme.image && ( {theme.name} )}
diff --git a/src/component/Create/getThemes.tsx b/src/component/Create/getThemes.tsx index 9208087c..2787d454 100644 --- a/src/component/Create/getThemes.tsx +++ b/src/component/Create/getThemes.tsx @@ -9,6 +9,12 @@ interface Theme { export const getThemes = (...themelist: string[]): ThemeArray[] => { const allThemes: Record = { + spring: { + name: '봄', + image: '/create/springFlower.svg', + bgStyle: 'bg-strcat-spring', + preview: '/preview/spring.png', + }, sul: { name: '설날', image: '/create/sulHat.svg', diff --git a/src/component/Personal/BottomAnimationImage.tsx b/src/component/Personal/BottomAnimationImage.tsx index 6eafec78..6331eb53 100644 --- a/src/component/Personal/BottomAnimationImage.tsx +++ b/src/component/Personal/BottomAnimationImage.tsx @@ -13,9 +13,9 @@ export default function BottomAnimationImage({ {`sit${themeName}`}
diff --git a/src/component/Personal/SakuraAnimation.tsx b/src/component/Personal/SakuraAnimation.tsx new file mode 100644 index 00000000..ddc9aa9e --- /dev/null +++ b/src/component/Personal/SakuraAnimation.tsx @@ -0,0 +1,66 @@ +import { CSSProperties, useEffect, useMemo, useState } from 'react'; + +const SakuraFlakes = ({ + style, + animation, +}: { + style: CSSProperties; + animation: string; +}) => { + /* 꽃잎 만들기. */ + return ( + + ); +}; + +const random = (num: number) => { + return Math.floor(Math.random() * num); +}; + +const makeSakuraFlakes = () => { + // 50개의 꽃잎을 만들어서 배열에 담아서 반환 + const arr = Array.from({ length: 100 }); + + return arr.map((_, i) => { + const animationDelay = `${random(16000)}ms`; + // 0초-40초 랜덤 딜레이 + const size = `${random(10) + 5}px`; + // 5-15px 랜덤 사이즈 + const startPosition = `${random(100)}%`; + // 0-100% 랜덤 시작 위치 + const style = { + animationDelay: animationDelay, + left: startPosition, + width: size, + height: size, + }; + const animation = i < 50 ? 'animate-sakura1' : 'animate-sakura2'; + return ; + }); +}; + +export default function SakuraAnimation({ themeName }: { themeName: string }) { + const [loaded, setLoaded] = useState(false); + useEffect(() => { + setLoaded(true); + }, [setLoaded]); + const sakuraFlakes = useMemo(() => makeSakuraFlakes(), []); + + if (themeName !== 'spring') return; + return ( + loaded && ( +
+
+ {sakuraFlakes} +
+
+ ) + ); +} diff --git a/src/types/boards.ts b/src/types/boards.ts index 1ab20b94..d8a5c52f 100644 --- a/src/types/boards.ts +++ b/src/types/boards.ts @@ -3,6 +3,6 @@ import { content } from './content'; export interface board { id: string; title: string; - theme: 'night' | 'peach' | 'lilac' | 'chris' | 'mas' | 'sul'; + theme: 'night' | 'peach' | 'lilac' | 'chris' | 'mas' | 'sul' | 'spring'; contents: content[]; } diff --git a/src/types/theme.ts b/src/types/theme.ts index 01a4659b..b000d8a0 100644 --- a/src/types/theme.ts +++ b/src/types/theme.ts @@ -6,6 +6,7 @@ import { nightBg, noneBg, peachBg, + springBg, sulBg, } from '@/utils/theme/bgTheme'; import { @@ -15,6 +16,7 @@ import { nightText, noneText, peachText, + springText, sulText, textThemeState, } from '@/utils/theme/textTheme'; @@ -61,6 +63,12 @@ export const sul: themeState = { bgTheme: sulBg, }; +export const spring: themeState = { + name: 'spring', + textTheme: springText, + bgTheme: springBg, +}; + export const noneTheme: themeState = { name: '', textTheme: noneText, @@ -74,4 +82,5 @@ export const themeObj = { chris: chris, mas: mas, sul: sul, + spring: spring, }; diff --git a/src/utils/theme/bgTheme.ts b/src/utils/theme/bgTheme.ts index 171fcba4..4e8f7eed 100644 --- a/src/utils/theme/bgTheme.ts +++ b/src/utils/theme/bgTheme.ts @@ -40,6 +40,12 @@ export const sulBg: bgThemeState = { addRightCTA: 'bg-strcat-sul', }; +export const springBg: bgThemeState = { + highlight: 'bg-strcat-spring', + rightCTA: 'bg-strcat-spring', + addRightCTA: 'bg-strcat-spring', +}; + export const noneBg: bgThemeState = { highlight: '', rightCTA: '', diff --git a/src/utils/theme/textTheme.ts b/src/utils/theme/textTheme.ts index c9103f91..4215e5cf 100644 --- a/src/utils/theme/textTheme.ts +++ b/src/utils/theme/textTheme.ts @@ -47,6 +47,13 @@ export const sulText: textThemeState = { addRightCTA: 'text-default-black', }; +export const springText: textThemeState = { + writer: 'text-strcat-spring', + highlight: 'text-default-black', + rightCTA: 'text-default-black', + addRightCTA: 'text-default-black', +}; + export const noneText: textThemeState = { writer: '', highlight: '', diff --git a/tailwind.config.js b/tailwind.config.js index 986ab833..e45167ca 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -39,6 +39,7 @@ module.exports = { 'strcat-chris': '#246F50', 'strcat-mas': '#DE6565', 'strcat-sul': '#82CBFF', + 'strcat-spring': '#FFBACF', }, keyframes: { slide: { @@ -76,8 +77,44 @@ module.exports = { from: { backgroundColor: 'rgba(0,0,0,0.8)' }, to: { backgroundColor: 'rgba(0,0,0,0)' }, }, + sakuraSway1: { + '0%': { transform: 'rotate(-5deg))' }, + '40%': { transform: 'rotate(28deg)' }, + '100%': { transform: 'rotate(3deg)' }, + }, + sakuraSway2: { + '0%': { transform: 'rotate(10deg))' }, + '40%': { transform: 'rotate(43deg)' }, + '100%': { transform: 'rotate(15deg)' }, + }, + sakuraFalling1: { + '0%': { + opacity: 0, + top: '-10%', + transform: 'translateX(0) rotate(0deg)', + }, + '10%': { opacity: 1 }, + '100%': { + opacity: 1, + top: '100%', + transform: 'translateX(210px) rotateX(180deg) rotateY(360deg)', + }, + }, + sakuraFalling2: { + '0%': { opacity: 0, top: '-10%', transform: 'translateX(0)' }, + '10%': { opacity: 1 }, + '100%': { + opacity: 1, + top: '100%', + transform: 'translateX(-210px) rotateX(180deg) rotateY(360deg)', + }, + }, }, animation: { + sakura1: + 'sakuraSway1 2s linear 0s infinite, sakuraFalling1 10s 0s linear infinite', + sakura2: + 'sakuraSway2 2s linear 0s infinite, sakuraFalling2 10s 0s linear infinite', slide: 'slide 0.7s linear', fadeIn: 'fadeIn 2s ease-in-out', textFadeIn: 'textFadeIn 0.35s ease-in-out',