diff --git a/package.json b/package.json index 86c8e93..a2dceda 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "axios": "^1.6.8", + "html2canvas": "^1.4.1", "react": "^18.2.0", "react-cookie": "^7.1.4", "react-dom": "^18.2.0", diff --git a/src/assets/icons/change.svg b/src/assets/icons/change.svg new file mode 100644 index 0000000..8d2343d --- /dev/null +++ b/src/assets/icons/change.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/download.svg b/src/assets/icons/download.svg new file mode 100644 index 0000000..b1c0dee --- /dev/null +++ b/src/assets/icons/download.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/DefineResultPage/CardSection.tsx b/src/components/DefineResultPage/CardSection.tsx new file mode 100644 index 0000000..876e8a0 --- /dev/null +++ b/src/components/DefineResultPage/CardSection.tsx @@ -0,0 +1,182 @@ +import { useRef, useState } from 'react'; + +import html2canvas from 'html2canvas'; +import styled from 'styled-components'; + +import { ReactComponent as ChangeIcon } from '@/assets/icons/change.svg'; +import { ReactComponent as DownloadIcon } from '@/assets/icons/download.svg'; +import { ReactComponent as KakaoIcon } from '@/assets/icons/kakaoIcon.svg'; + +interface CardSectionProps { + piece: string; +} + +export const CardSection = ({ piece }: CardSectionProps) => { + const [isFront, setIsFront] = useState(true); + const captureRef = useRef(null); + + const handleSaveImage = () => { + if (!captureRef.current) return; + + const capture = captureRef.current; + + html2canvas(capture, { scrollY: -window.scrollY }).then((canvas) => { + const image = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream'); + const link = document.createElement('a'); + link.href = image; + link.download = `define-result-${isFront ? 'front' : 'back'}.jpg`; + link.click(); + }); + }; + + return ( + + + card +
+ setIsFront((prev) => !prev)}> + + + + + + +
+
+
카드에 마우스를 가져가 보세요!
+
+ ); +}; + +const StyledCardSection = styled.section` + display: flex; + flex-direction: column; + justify-content: space-between; + + .notice { + text-align: center; + ${({ theme }) => theme.font.desktop.label1m}; + color: ${({ theme }) => theme.color.primary700}; + } +`; + +// TODO: Change 버튼 클릭시 카드 앞뒤 돌아가는 애니메이션 추가 + +const StyledImageContainer = styled.div` + width: 264px; + height: 426px; + + position: relative; + + img { + width: 100%; + height: 100%; + border-radius: 13px; + } + + .hover-view { + visibility: hidden; + + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-end; + + position: absolute; + top: 0; + left: 0; + + width: 100%; + height: 100%; + padding: 20px; + + border-radius: 13px; + background: ${({ theme }) => theme.color.bgModal}; + filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.25)); + backdrop-filter: blur(4px); + + button { + color: white; + } + } + + &:hover .hover-view { + visibility: visible; + } +`; + +const StyledChangeButton = styled.button` + display: flex; + width: 48px; + height: 48px; + justify-content: center; + align-items: center; + + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.3); + background: rgba(255, 255, 255, 0.2); + + &:hover { + background: rgba(255, 255, 255, 0.1); + } +`; + +const StyleButtonContainer = styled.div` + width: 100%; + + display: flex; + flex-direction: column; + gap: 8px; + + button { + display: flex; + justify-content: space-between; + align-items: center; + + height: 48px; + padding: 8px 16px; + border-radius: 8px; + + span { + ${({ theme }) => theme.font.desktop.label1m}; + } + } + + .download-button { + background: ${({ theme }) => theme.color.primary50}; + + span { + color: ${({ theme }) => theme.color.primary700}; + } + } + + .share-button { + background: #fee500; + + span { + color: #191600; + } + } + + div { + width: 24px; + } +`; diff --git a/yarn.lock b/yarn.lock index 18c6d03..f3b5271 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1920,6 +1920,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -2092,6 +2097,13 @@ css-color-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -2962,6 +2974,14 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" @@ -4116,6 +4136,13 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -4285,6 +4312,13 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + vite-plugin-svgr@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz#9f3bf5206b0ec510287e56d16f1915e729bb4e6b"