diff --git a/src/App.jsx b/src/App.jsx index 0b3384d..bb43abd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import {AddRecipe} from "./pages/recipe/AddRecipe" import Join from "./pages/user/Join" import Login from "./pages/user/Login" import RecipeRecommend from "./pages/recipe/RecipeRecommend" +import RecipeRandom from "./pages/recipe/RecipeRandom" function App() { return ( @@ -22,6 +23,7 @@ function App() { } /> } /> }/> + }/> } /> } /> diff --git a/src/assets/css/recipe/RecipeRandom.css b/src/assets/css/recipe/RecipeRandom.css new file mode 100644 index 0000000..b5dc4d0 --- /dev/null +++ b/src/assets/css/recipe/RecipeRandom.css @@ -0,0 +1,224 @@ +.random-recipe-container { + min-height: 100vh; + padding: 2rem; + background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%); +} + +.page-title { + text-align: center; + color: #3FA2F6; + font-size: 2.5rem; + font-weight: bold; + margin-bottom: 3rem; + text-shadow: 2px 2px 4px rgba(0,0,0,0.1); +} + +.draw-section { + max-width: 1000px; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + min-height: 60vh; +} + +.mystery-box { + position: relative; + width: 300px; + height: 300px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.box-svg { + filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1)); +} + +.box-lid { + transform-origin: center; + transition: transform 0.5s ease; +} + +.mystery-box.opening .box-lid { + animation: openLid 2.5s forwards; +} + +.box-body { + transform-origin: bottom center; +} + +.mystery-box.opening .box-body { + animation: shakeBox 0.5s ease infinite; +} + +.draw-button { + margin-top: 2rem; + padding: 1rem 3rem; + font-size: 1.2rem; + background: linear-gradient(45deg, #3FA2F6, #96C9F4); + color: white; + border: none; + border-radius: 50px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(63, 162, 246, 0.3); +} + +.draw-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(63, 162, 246, 0.4); +} + +.draw-button:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.recipe-reveal { + animation: fadeIn 0.5s ease-out; +} + +.recipe-card { + background: white; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0,0,0,0.1); + overflow: hidden; + max-width: 1000px; + width: 90%; + margin: 0 auto; +} + +.recipe-image-container { + height: 400px; + overflow: hidden; +} + +.recipe-image { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.recipe-image:hover { + transform: scale(1.05); +} + +.recipe-details { + padding: 2rem; +} + +.recipe-title { + font-size: 1.8rem; + color: #333; + margin-bottom: 1rem; +} + +.recipe-description { + color: #666; + line-height: 1.6; + margin-bottom: 1.5rem; +} + +.ingredients-section { + background: #f8f9fa; + padding: 1.5rem; + border-radius: 12px; + margin-bottom: 1.5rem; +} + +.ingredients-list { + display: flex; + flex-wrap: wrap; + gap: 0.8rem; + margin-top: 1rem; +} + +.ingredient-tag { + padding: 0.5rem 1rem; + border-radius: 20px; + font-size: 0.9rem; + transition: all 0.3s ease; +} + +.ingredient-tag.necessary { + background: #3FA2F6; + color: white; +} + +.ingredient-tag.optional { + background: #96C9F4; + color: white; + opacity: 0.8; +} + +.action-buttons { + display: flex; + gap: 1rem; + margin-top: 2rem; +} + +.view-recipe, .draw-again { + flex: 1; + padding: 1rem; + border-radius: 10px; + font-weight: bold; + transition: all 0.3s ease; +} + +.view-recipe { + background: #3FA2F6; + color: white; + border: none; +} + +.draw-again { + background: white; + color: #3FA2F6; + border: 2px solid #3FA2F6; +} + +@keyframes openLid { + 0% { transform: rotateX(0deg); } + 30% { transform: rotateX(-60deg) translateY(-20px); } + 100% { + transform: rotateX(-90deg) translateY(-40px); + opacity: 0; + } +} + +@keyframes shakeBox { + 0% { transform: rotate(0deg); } + 25% { transform: rotate(-5deg); } + 75% { transform: rotate(5deg); } + 100% { transform: rotate(0deg); } +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +/* 반짝이 효과 */ +.mystery-box::after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: radial-gradient(circle, white 10%, transparent 60%); + opacity: 0; + pointer-events: none; +} + +.mystery-box.opening::after { + animation: shine 2s ease-out forwards; +} + +@keyframes shine { + 0% { opacity: 0; transform: scale(0.5); } + 50% { opacity: 0.5; transform: scale(1.2); } + 100% { opacity: 0; transform: scale(2); } +} \ No newline at end of file diff --git a/src/pages/recipe/RecipeRandom.jsx b/src/pages/recipe/RecipeRandom.jsx new file mode 100644 index 0000000..7d03d6c --- /dev/null +++ b/src/pages/recipe/RecipeRandom.jsx @@ -0,0 +1,161 @@ +import React, { useState } from 'react'; +import { getRandomRecipe } from '../../sources/api/recipeAPI'; +import defaultImage from "../../assets/image/default.gif"; +import '../../assets/css/recipe/RecipeRandom.css'; +import { API_URL_HOST } from '../../sources/api/recipeAPI'; +import { useNavigate } from 'react-router-dom'; + +function RecipeRandom() { + const navigate = useNavigate(); + const [recipe, setRecipe] = useState(null); + const [isDrawing, setIsDrawing] = useState(false); + const [showBox, setShowBox] = useState(true); + const [isOpening, setIsOpening] = useState(false); + + const drawRecipe = async () => { + if (isDrawing) return; + + setIsDrawing(true); + setIsOpening(true); + + try { + setTimeout(async () => { + const data = await getRandomRecipe(); + console.log('받아온 레시피 데이터:', data); + setRecipe(data); + setShowBox(false); + setIsDrawing(false); + }, 2500); + } catch (err) { + console.error("랜덤 레시피 로드 실패:", err); + setIsDrawing(false); + } + }; + + const resetDrawing = () => { + setShowBox(true); + setIsOpening(false); + setRecipe(null); + }; + + const handleViewRecipe = () => { + navigate(`/recipe/${recipe.recipePk}`); +}; + + return ( +
+

오늘의 레시피 뽑기

+ +
+ {showBox ? ( +
+ + {/* 상자 뚜껑 */} + + + + + + {/* 상자 본체 */} + + + {/* 상자 무늬 */} + + + + + + +
+ ) : recipe && ( +
+
+
+ 0 + ? `${API_URL_HOST}/${recipe.mainImages[0].filePath}` + : defaultImage} + alt={recipe.recipeName} + className="recipe-image" + /> +
+ +
+

{recipe.recipeName}

+

{recipe.recipeContent}

+ +
+
+ 조리 시간 + {recipe.recipeCookingTime}분 +
+
+ +
+ + +
+
+
+
+ )} +
+
+ ); +} + +export default RecipeRandom; \ No newline at end of file diff --git a/src/sources/api/recipeAPI.jsx b/src/sources/api/recipeAPI.jsx index 37bb4b8..f3579b4 100644 --- a/src/sources/api/recipeAPI.jsx +++ b/src/sources/api/recipeAPI.jsx @@ -31,4 +31,10 @@ export const getRecommendedRecipes = async (userPk) => { const url = `${prefix}/recommend?userPk=${userPk}`; const res = await axios.get(url); return res.data; +} + +export const getRandomRecipe = async () => { + const url = `${prefix}/random`; + const res = await axios.get(url); + return res.data; } \ No newline at end of file