From 361f63b8d41db841ffbecce7484c2e3e3d1425f9 Mon Sep 17 00:00:00 2001 From: danbiilee Date: Tue, 2 Feb 2021 20:39:07 +0900 Subject: [PATCH] =?UTF-8?q?[FEAT]=20#1=20:=20=ED=8C=A8=ED=84=B4=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 설정 컴포넌트들 분리 - 캔버스 사이즈: 최소값, 전체화면 리사이즈 시 처리 적용 - 패턴 영역: 캔버스 사이즈에 맞게 클립 적용 - #2 : 설정 컴포넌트들, 스타일 분리 --- src/components/container/CanvasContainer.js | 6 +- src/components/container/ImagesContainer.js | 35 +- src/components/container/PatternsContainer.js | 302 ++++-------------- .../layout/pattern/ToggleContent.js | 13 + src/components/layout/pattern/ToggleTitle.js | 32 ++ .../layout/pattern/ToggleWrapper.js | 12 + src/components/parts/Canvas.js | 161 +++++----- src/components/parts/Images.js | 37 ++- src/components/parts/pattern/SetCVSize.js | 80 +++++ src/components/parts/pattern/SetColor.js | 42 +++ src/components/parts/pattern/SetGapSize.js | 26 ++ src/components/parts/pattern/SetImage.js | 47 +++ src/components/parts/pattern/SetImgSize.js | 40 +++ src/components/parts/pattern/SetType.js | 27 ++ src/components/parts/pattern/Setting.js | 10 - src/lib/myCanvas.js | 74 +++-- src/redux/pictures.js | 14 +- 17 files changed, 565 insertions(+), 393 deletions(-) create mode 100644 src/components/layout/pattern/ToggleContent.js create mode 100644 src/components/layout/pattern/ToggleTitle.js create mode 100644 src/components/layout/pattern/ToggleWrapper.js create mode 100644 src/components/parts/pattern/SetCVSize.js create mode 100644 src/components/parts/pattern/SetColor.js create mode 100644 src/components/parts/pattern/SetGapSize.js create mode 100644 src/components/parts/pattern/SetImage.js create mode 100644 src/components/parts/pattern/SetImgSize.js create mode 100644 src/components/parts/pattern/SetType.js delete mode 100644 src/components/parts/pattern/Setting.js diff --git a/src/components/container/CanvasContainer.js b/src/components/container/CanvasContainer.js index c3cad22..cc35cab 100644 --- a/src/components/container/CanvasContainer.js +++ b/src/components/container/CanvasContainer.js @@ -28,9 +28,9 @@ const CanvasContainer = () => { type: canvasMode, src: myCanvas.drawnImgSrc, }; - if (myCanvas.drawnImgTitle) { - payload.title = myCanvas.drawnImgTitle; - } + // if (myCanvas.drawnImgTitle) { + // payload.title = myCanvas.drawnImgTitle; + // } dispatch(addPicture(payload)); }; diff --git a/src/components/container/ImagesContainer.js b/src/components/container/ImagesContainer.js index a75fff5..b4c9eb0 100644 --- a/src/components/container/ImagesContainer.js +++ b/src/components/container/ImagesContainer.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { getPictures, @@ -31,7 +31,7 @@ const addTypeList = (pictures, typeList) => { } for (let pic of pictures) { - // canvasMode: origin, crop, pattern + // type: origin, crop, pattern let optionName = ''; if (pic.type === 'crop') optionName = '크롭'; else if (pic.type === 'pattern') optionName = '패턴'; @@ -45,6 +45,7 @@ const addTypeList = (pictures, typeList) => { }; const ImagesContainer = () => { + //console.log('ImagesContainer'); const dispatch = useDispatch(); const { pictures, drawnPicture } = useSelector(state => state.pictures); @@ -58,8 +59,6 @@ const ImagesContainer = () => { // pictures가 바뀌었을 때만 실행 typeList = useMemo(() => addTypeList(pictures, typeList), [pictures]); - //console.log('ImagesContainer', pictures, typeList); - // 첫 마운트 후 indexedDB 값 리덕스 스토어에 셋팅 useEffect(() => { if (!pictures || !pictures.length) { @@ -73,17 +72,18 @@ const ImagesContainer = () => { // 선택 이미지 리스트(삭제용) 관리 const [selectedList, setSelectedList] = useState([]); - const onToggle = id => { - if (selectedList.includes(id)) { - setSelectedList(selectedList.filter(item => item !== id)); - dispatch(deleteSelectedPictures(id)); - } else { - setSelectedList(selectedList.concat(id)); - dispatch(addSelectedPictures(id)); - } - dispatch(changeDrawnPicture(id)); - dispatch(changeCanvasMode(null)); - }; + const onToggle = useCallback( + id => { + if (selectedList.includes(id)) { + setSelectedList(selectedList.filter(item => item !== id)); + dispatch(deleteSelectedPictures(id)); + } else { + setSelectedList(selectedList.concat(id)); + dispatch(addSelectedPictures(id)); + } + }, + [selectedList, dispatch], + ); const onDelete = () => { dispatch(deletePicture(selectedList)); @@ -98,10 +98,11 @@ const ImagesContainer = () => { }; const onCrop = () => { - if (!drawnPicture) { - alert('⚠ 이미지를 선택해주세요 ⚠'); + if (selectedList.length > 1) { + alert('⚠ 하나의 이미지만 크롭할 수 있습니다 ⚠'); return; } + dispatch(changeDrawnPicture(selectedList[0])); dispatch(changeCanvasMode('crop')); }; diff --git a/src/components/container/PatternsContainer.js b/src/components/container/PatternsContainer.js index 1ab19bc..4acbb97 100644 --- a/src/components/container/PatternsContainer.js +++ b/src/components/container/PatternsContainer.js @@ -1,11 +1,7 @@ import React, { useState, useCallback, useEffect, useRef } from 'react'; import styled from 'styled-components'; import { useSelector, useDispatch } from 'react-redux'; -import { - changeCanvasMode, - changeProperties, - changeDrawnPicture, -} from '../../redux/pictures'; +import { changeProperties, initialData } from '../../redux/pictures'; import Images from '../parts/Images'; import TopButtons from '../layout/tabPanel/TopButtons'; @@ -15,67 +11,29 @@ import SVG from '../common/SVG'; import Input from '../common/Input'; import Select from '../common/Select'; +import SetImage from '../parts/pattern/SetImage'; +import SetColor from '../parts/pattern/SetColor'; +import SetCVSize from '../parts/pattern/SetCVSize'; +import SetImgSize from '../parts/pattern/SetImgSize'; +import SetGapSize from '../parts/pattern/SetGapSize'; +import SetType from '../parts/pattern/SetType'; + const ButtonInner = styled.div` display: flex; align-items: center; `; -const ContentsInner = styled.div` - padding: 5px; -`; - -const Settings = styled.div` - padding: 5px; - border-bottom: 2px double var(--border-dark-gray); -`; -const InnerBlock = styled.div` - // display: flex; - // align-items: center; - // justify-content: space-around; - &:not(:first-of-typ) { - margin-top: 5px; - } -`; -const ToggleTitle = styled.button` - justify-content: flex-start; - width: 100%; - padding: 5px; - margin-bottom: 5px; - background-color: ${props => - props.selected ? 'var(--dark-gray)' : 'var(--middle-gray)'}; - &:hover { - background-color: var(--dark-gray); - transition: 0.2s; - } - color: var(--lightest-gray); - font-size: 0.8rem; - svg { - margin-right: 3px; - fill: var(--lightest-gray); - } - cursor: pointer; -`; -const ToggleContents = styled.div` - display: ${props => (props.selected ? 'flex' : 'none')}; - flex-direction: column; -`; - -const ColorInput = styled.input.attrs(props => ({ - type: props.type, -}))` - width: 50px; - height: 55px; - cursor: pointer; -`; - const SettingsContainer = () => { const dispatch = useDispatch(); const { properties: prop, canvasMode, drawnPicture } = useSelector( state => state.pictures, ); - // 속성값 설정 + // 속성 설정 + const defaultProps = initialData.properties; const [properties, setProperties] = useState(prop); + //console.log('SettingsContainer', defaultProps, properties); + const onChange = useCallback(e => { const { name, value } = e.target; setProperties(properties => ({ @@ -83,83 +41,28 @@ const SettingsContainer = () => { [name]: value, })); }, []); - const onReset = useCallback( - () => - setProperties({ - color: '#ffffff', - canvasWidth: 0, - canvasHeight: 0, - imgWidth: 0, - imgHeight: 0, - gap: 50, - type: 'vanilla', - }), - [], - ); + const onReset = useCallback(() => setProperties(defaultProps), []); - const keys = useRef([]); // 설정값 변경되면 패턴 다시 그림 useEffect(() => { - for (let key in properties) { - if (properties[key] !== prop[key]) keys.current.push(key); - } - if (!keys.current.length) { - console.log('keys.length 0'); - return; - } - // 배경사이즈 적용은 width, height 둘 다 입력했을 때만 - const canvasSize = - keys.current.includes('canvasWidth') && - keys.current.includes('canvasWidth'); - const imgSize = - keys.current.includes('imgWidth') && keys.current.includes('imgWidth'); - console.log('properties changed', keys.current, properties); + console.log('changed properties', properties); dispatch(changeProperties(properties)); }, [properties, dispatch]); - const handleSizeProp = () => { - dispatch(changeProperties(properties)); - }; - // 설정영역 토글 // img, canvasSize, color, imgSize, gap, patternType - const [selectedArea, setSelectedArea] = useState('img'); - const handleSelectedArea = useCallback(prop => setSelectedArea(prop), []); - - // 선택 크롭 이미지 관리 - const [selectedCrop, setSelectedCrop] = useState([]); - const onToggle = useCallback( - id => { - if (!selectedCrop.includes(id)) { - setSelectedCrop(selectedCrop => [id]); - dispatch(changeDrawnPicture(id)); + const [selected, setSelected] = useState('img'); + const handleSelected = useCallback( + prop => { + if (selected === prop) { + setSelected(null); + } else { + setSelected(prop); } - // 이미지 선택되면 패턴 그림 - dispatch(changeCanvasMode('pattern')); }, - [dispatch], + [selected], ); - // 그림 선택되면 캔버스에 그림 - // useEffect(() => { - // if (drawnPicture) { - // console.log('drawnPicture changed', drawnPicture); - // dispatch(changeCanvasMode('pattern')); - // } - // }, [drawnPicture, dispatch]); - - const patternType = [ - { - type: '', - optionName: '전체', - }, - ]; - - const makePattern = useCallback(() => { - dispatch(changeCanvasMode('pattern')); - dispatch(changeProperties(properties)); - }, [properties, dispatch]); - return ( <> @@ -183,135 +86,38 @@ const SettingsContainer = () => { - - - - handleSelectedArea('img')} - > - Select - Image - - - - - - - handleSelectedArea('color')} - > - {' '} - Background Color - - - {' '} - {properties.color} - - - - handleSelectedArea('canvasSize')} - > - {' '} - Background Size - - - W:{' '} - - H:{' '} - - {/* 사이즈 적용 버튼 */} - - - - - handleSelectedArea('imgSize')} - > - Set - Cropped Image Size - - - W:{' '} - - H:{' '} - - - - - handleSelectedArea('gap')} - > - Set Gap - Size - - - - - - - handleSelectedArea('patternType')} - > - Select - Pattern Type - - - {prop.type} - - - - + + + + + + ); diff --git a/src/components/layout/pattern/ToggleContent.js b/src/components/layout/pattern/ToggleContent.js new file mode 100644 index 0000000..4fdbb50 --- /dev/null +++ b/src/components/layout/pattern/ToggleContent.js @@ -0,0 +1,13 @@ +import React from 'react'; +import styled from 'styled-components'; + +const Wrapper = styled.div` + display: ${props => (props.selected ? 'flex' : 'none')}; + flex-direction: column; +`; + +const ToggleContent = ({ selected, children }) => { + return {children}; +}; + +export default ToggleContent; diff --git a/src/components/layout/pattern/ToggleTitle.js b/src/components/layout/pattern/ToggleTitle.js new file mode 100644 index 0000000..573a837 --- /dev/null +++ b/src/components/layout/pattern/ToggleTitle.js @@ -0,0 +1,32 @@ +import React from 'react'; +import styled from 'styled-components'; + +const Wrapper = styled.button` + justify-content: flex-start; + width: 100%; + padding: 5px; + margin-bottom: 5px; + background-color: ${props => + props.selected ? 'var(--dark-gray)' : 'var(--middle-gray)'}; + &:hover { + background-color: var(--dark-gray); + transition: 0.2s; + } + color: var(--lightest-gray); + font-size: 0.8rem; + svg { + margin-right: 3px; + fill: var(--lightest-gray); + } + cursor: pointer; +`; + +const ToggleTitle = ({ selected, type, handleSelected, children }) => { + return ( + handleSelected(type)}> + {children} + + ); +}; + +export default ToggleTitle; diff --git a/src/components/layout/pattern/ToggleWrapper.js b/src/components/layout/pattern/ToggleWrapper.js new file mode 100644 index 0000000..302fe72 --- /dev/null +++ b/src/components/layout/pattern/ToggleWrapper.js @@ -0,0 +1,12 @@ +import React from 'react'; +import styled from 'styled-components'; + +const Wrapper = styled.div` + margin: 5px; +`; + +const ToggleWrapper = ({ children }) => { + return {children}; +}; + +export default ToggleWrapper; diff --git a/src/components/parts/Canvas.js b/src/components/parts/Canvas.js index 35e8a94..2273048 100644 --- a/src/components/parts/Canvas.js +++ b/src/components/parts/Canvas.js @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; +import { changeProperties } from '../../redux/pictures'; import styled from 'styled-components'; import { debounce } from '../../utils/commonUtils'; @@ -10,13 +11,14 @@ const Wrapper = styled.div` height: 100%; `; const CanvasBlock = styled.canvas` - background-color: #fff; + background-color: #eee; `; const Canvas = ({ setMyCanvas }) => { const { pictures, drawnPicture, canvasMode, properties } = useSelector( state => state.pictures, ); + const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0, @@ -25,7 +27,7 @@ const Canvas = ({ setMyCanvas }) => { const canvasRef = useRef(); const my = useRef(); // 캔버스 객체 const ctx = useRef(); - //console.log('Canvas', drawnPicture, canvasMode, properties); + console.log('Canvas', drawnPicture, properties); // 캔버스 사이즈 변경 const handleResize = () => @@ -34,29 +36,6 @@ const Canvas = ({ setMyCanvas }) => { height: wrapperRef.current.offsetHeight - 1, }); - // 캔버스 (재)설정 및 생성 - const resetMyCanvas = drawnPicture => { - if (drawnPicture) { - // myCanvas 생성 - const cropImgSrc = pictures.find(pic => pic.id === drawnPicture).src; - my.current = new MyCanvas( - wrapperRef.current, - canvasRef.current, - ctx.current, - cropImgSrc, - ); - setMyCanvas(my.current); - - // 이미지 초기화 - my.current.initCropImage(); - - const that = my.current; - my.current.drawnImg.onload = function () { - that.drawImage(1.0); - }; - } - }; - // 컴포넌트 마운트/언마운트 시 처리 useEffect(() => { if (wrapperRef.current && canvasRef.current) { @@ -76,63 +55,97 @@ const Canvas = ({ setMyCanvas }) => { // 리사이즈된 부모영역에 맞게 캔버스 사이즈 재조정 useEffect(() => { const { width, height } = canvasSize; + console.log('canvasSize changed', width, height); + + // 그려뒀던 이미지 있는 경우, 다시 그림 + if (drawnPicture && my.current) { + my.current.resizeCanvas(width, height, canvasMode, properties); + return; + } + + // 아닌 경우 바로 캔버스 리사이즈 if (width && height) { canvasRef.current.width = width; canvasRef.current.height = height; } - - // 캔버스 리사이즈된 경우 캔버스랑 context 설정들 전부 리셋됨 - if (drawnPicture) { - resetMyCanvas(drawnPicture); - } - }, [canvasSize]); - - // 선택한 이미지 바뀐 경우 캔버스 재생성 + // properties는 deps에 넣지 않음. makePattern 중복해서 발생됨 + }, [canvasSize, drawnPicture, canvasMode]); + + // const handleCanvasMode = () => { + // switch (canvasMode) { + // case 'crop': + // console.log('crop!!!!'); + // // 이미지 다시 그리기 + // my.current.drawnImg.onload = function () { + // my.current.drawImage(1.0); + // }; + // my.current.canvas.addEventListener('mousedown', function (e) { + // my.current.handleMouseDown(e); + // }); + // break; + // case 'pattern': + // console.log('pattern!!!!'); + // my.current.makePattern(properties); + // break; + // case 'clear': + // console.log('clear!!!!'); + // // clear canvas + // my.current.clear(); + // break; + // } + // }; + + // 선택이미지 변경 시 캔버스 재생성 + // 크롭 모드: 마우스 이벤트, 패턴 모드: 패턴만들기 useEffect(() => { if (drawnPicture) { - resetMyCanvas(drawnPicture); - } - }, [drawnPicture, setMyCanvas, pictures]); - - useEffect(() => { - if (canvasMode === 'crop') { - console.log('crop!!!!'); - - const that = my.current; - my.current.drawImage(0.7); - - // 그리기 이벤트 감지 - my.current.canvas.addEventListener('mousedown', function (e) { - that.handleMouseDown(e); - }); - } - - if (canvasMode === 'pattern') { - console.log('pattern!!!!'); - my.current.makePattern(properties); - } - - if (canvasMode === 'clear') { - console.log('clear!!!!'); - // clear canvas - ctx.current.clearRect( - 0, - 0, - canvasRef.current.width, - canvasRef.current.height, + console.log('drawnPicture!!!'); + // myCanvas 생성 + const cropImgSrc = pictures.find(pic => pic.id === drawnPicture).src; + my.current = new MyCanvas( + wrapperRef.current, + canvasRef.current, + ctx.current, + cropImgSrc, ); + setMyCanvas(my.current); + my.current.initCropImage(); // 이미지 초기화 + + switch (canvasMode) { + case 'crop': + console.log('crop!!!!'); + // 이미지 다시 그리기 + my.current.drawnImg.onload = function () { + my.current.drawImage(1.0); + }; + my.current.canvas.addEventListener('mousedown', function (e) { + my.current.handleMouseDown(e); + }); + break; + case 'pattern': + console.log('pattern!!!!'); + my.current.makePattern(properties); + break; + case 'clear': + console.log('clear!!!!'); + // clear canvas + my.current.clear(); + break; + } + // if (my.current) { + // my.current.canvas.removeEventListener( + // 'mousedown', + // my.current.handleMouseDown, + // ); + // } } + }, [drawnPicture, pictures, canvasMode, setMyCanvas, properties]); - return () => { - //console.log('unmout'); - if (my.current) { - my.current.canvas.removeEventListener( - 'mousedown', - my.current.handleMouseDown, - ); - } - }; - }, [canvasMode, properties]); + // useEffect(() => { + // if (canvasMode === 'pattern') { + // my.current.makePattern(properties); + // } + // }, [canvasMode, properties]); return ( diff --git a/src/components/parts/Images.js b/src/components/parts/Images.js index 59b54bd..a64309b 100644 --- a/src/components/parts/Images.js +++ b/src/components/parts/Images.js @@ -2,9 +2,6 @@ import React, { useEffect, useRef, useMemo } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import styled from 'styled-components'; -import Button from '../common/Button'; -import SVG from '../common/SVG'; - const Ul = styled.ul` overflow-y: auto; flex: 1 0 500px; @@ -47,8 +44,9 @@ const filterListByType = (pictures, type) => { return result; }; -const Images = ({ selectedType, selectedList, onToggle }) => { - const dispatch = useDispatch(); +const Images = React.memo(({ selectedType, selectedList, onToggle }) => { + //console.log('Images', selectedType, selectedList); + const { pictures } = useSelector(state => state.pictures); // 렌더링될 이미지 필터링 @@ -58,7 +56,8 @@ const Images = ({ selectedType, selectedList, onToggle }) => { ]); // 선택여부 속성 추가 - if (filteredImages && filteredImages.length) { + const bool = filteredImages && filteredImages.length; + if (bool) { filteredImages = filteredImages.map(img => { const check = selectedList.includes(img.id); return check @@ -66,25 +65,29 @@ const Images = ({ selectedType, selectedList, onToggle }) => { : { ...img, isSelected: false }; }); } - //console.log('ImageList', selectedType, pictures, filteredImages); return ( ); -}; +}); + +const ImageItem = React.memo(({ img, onToggle }) => { + const { id, isSelected, src } = img; + //console.log('ImageItem'); + + return ( +
  • onToggle(id)}> + + uploaded image +
  • + ); +}); export default Images; diff --git a/src/components/parts/pattern/SetCVSize.js b/src/components/parts/pattern/SetCVSize.js new file mode 100644 index 0000000..a87f80e --- /dev/null +++ b/src/components/parts/pattern/SetCVSize.js @@ -0,0 +1,80 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { changeProperties } from '../../../redux/pictures'; + +import SVG from '../../common/SVG'; +import Input from '../../common/Input'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const SetCVSize = ({ ...props }) => { + const dispatch = useDispatch(); + const { selected, handleSelected, properties, setProperties } = props; + const { canvasWidth, canvasHeight } = properties; + + const canvasRef = useRef(document.querySelector('canvas')); + const [userWidth, setUserWidth] = useState( + canvasWidth ? canvasWidth : canvasRef.current.offsetWidth, + ); + const [userHeight, setUserHeight] = useState( + canvasHeight ? canvasHeight : canvasRef.current.offsetHeight, + ); + + const onChange = e => { + let { name, value } = e.target; + if (value < 100) { + value = 100; // 최소값 100 + alert('⚠ 캔버스 사이즈는 100px 이하로 설정할 수 없습니다 ⚠'); + } + switch (name) { + case 'canvasWidth': + setUserWidth(value); + break; + case 'canvasHeight': + setUserHeight(value); + break; + } + }; + + //console.log('SetCVSize', canvasWidth, canvasHeight, userWidth, userHeight); + + useEffect(() => { + // 둘 다 변경되었을 때만 최종 반영 + setProperties(properties => ({ + ...properties, + canvasWidth: userWidth, + canvasHeight: userHeight, + })); + }, [canvasWidth, canvasHeight, userWidth, userHeight]); + + return ( + + + Background Size + + + W:{' '} + + H:{' '} + + + + ); +}; + +export default SetCVSize; diff --git a/src/components/parts/pattern/SetColor.js b/src/components/parts/pattern/SetColor.js new file mode 100644 index 0000000..d03a45e --- /dev/null +++ b/src/components/parts/pattern/SetColor.js @@ -0,0 +1,42 @@ +import React from 'react'; +import styled from 'styled-components'; + +import SVG from '../../common/SVG'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const ColorInput = styled.input.attrs(props => ({ + type: props.type, +}))` + width: 50px; + height: 55px; + cursor: pointer; +`; + +const SetColor = ({ selected, handleSelected, color, onChange }) => { + //const dispatch = useDispatch(); + + return ( + + + Background Color + + + + {color} + + + ); +}; + +export default SetColor; diff --git a/src/components/parts/pattern/SetGapSize.js b/src/components/parts/pattern/SetGapSize.js new file mode 100644 index 0000000..d6b27bc --- /dev/null +++ b/src/components/parts/pattern/SetGapSize.js @@ -0,0 +1,26 @@ +import React from 'react'; +import SVG from '../../common/SVG'; +import Input from '../../common/Input'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const SetGapSize = ({ ...props }) => { + const { selected, handleSelected, gap, onChange } = props; + return ( + + + Set Gap Size + + + + + + ); +}; + +export default SetGapSize; diff --git a/src/components/parts/pattern/SetImage.js b/src/components/parts/pattern/SetImage.js new file mode 100644 index 0000000..5c8bba2 --- /dev/null +++ b/src/components/parts/pattern/SetImage.js @@ -0,0 +1,47 @@ +import React, { useState, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { changeDrawnPicture, changeCanvasMode } from '../../../redux/pictures'; + +import SVG from '../../common/SVG'; +import Images from '../../parts/Images'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const SetImage = ({ selected, handleSelected }) => { + const dispatch = useDispatch(); + + // 선택 크롭 이미지 관리 + const [selectedCrop, setSelectedCrop] = useState([]); + const onToggle = useCallback( + id => { + if (!selectedCrop.includes(id)) { + setSelectedCrop([id]); + dispatch(changeDrawnPicture(id)); + dispatch(changeCanvasMode('pattern')); + } + }, + [selectedCrop, dispatch], + ); + + return ( + + + Select Image + + + + + + ); +}; + +export default SetImage; diff --git a/src/components/parts/pattern/SetImgSize.js b/src/components/parts/pattern/SetImgSize.js new file mode 100644 index 0000000..31d50c6 --- /dev/null +++ b/src/components/parts/pattern/SetImgSize.js @@ -0,0 +1,40 @@ +import React from 'react'; +import SVG from '../../common/SVG'; +import Input from '../../common/Input'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const SetImgSize = ({ ...props }) => { + const { selected, handleSelected, width, height, onChange } = props; + return ( + + + Set Cropped + Image Size + + + W:{' '} + + H:{' '} + + + + ); +}; + +export default SetImgSize; diff --git a/src/components/parts/pattern/SetType.js b/src/components/parts/pattern/SetType.js new file mode 100644 index 0000000..609d083 --- /dev/null +++ b/src/components/parts/pattern/SetType.js @@ -0,0 +1,27 @@ +import React from 'react'; +import SVG from '../../common/SVG'; +import Input from '../../common/Input'; +import ToggleWrapper from '../../layout/pattern/ToggleWrapper'; +import ToggleTitle from '../../layout/pattern/ToggleTitle'; +import ToggleContent from '../../layout/pattern/ToggleContent'; + +const SetType = ({ ...props }) => { + const { selected, handleSelected, type, onChange } = props; + return ( + + + Select Pattern + Type + + + {type} + + + ); +}; + +export default SetType; diff --git a/src/components/parts/pattern/Setting.js b/src/components/parts/pattern/Setting.js deleted file mode 100644 index 7124bed..0000000 --- a/src/components/parts/pattern/Setting.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -const Wrapper = styled.div``; - -const Setting = ({ children }) => { - return 123; -}; - -export default Setting; diff --git a/src/lib/myCanvas.js b/src/lib/myCanvas.js index 7cdd300..dc1f40d 100644 --- a/src/lib/myCanvas.js +++ b/src/lib/myCanvas.js @@ -1,3 +1,5 @@ +import { initialData } from '../redux/pictures'; + export function MyCanvas(wrapper, canvas, ctx, drawnImgSrc) { this.wrapper = wrapper; this.canvas = canvas; @@ -10,9 +12,39 @@ export function MyCanvas(wrapper, canvas, ctx, drawnImgSrc) { this.isCropped = false; // crop() 함수 실행여부 } +// 캔버스 리사이즈 시 원본 유지 +MyCanvas.prototype.resizeCanvas = function (...args) { + const [width, height, canvasMode, properties] = args; + const c = document.createElement('canvas'); + const cx = c.getContext('2d'); + + // 이전 캔버스 저장 + c.width = this.canvas.width; + c.height = this.canvas.height; + cx.drawImage(this.canvas, 0, 0); + + this.canvas.width = width; + this.canvas.height = height; + + switch (canvasMode) { + case 'crop': + this.ctx.drawImage(c, 0, 0); + break; + case 'pattern': + this.makePattern(properties); + break; + } +}; + +// 캔버스 클리어하기 +MyCanvas.prototype.clear = function () { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + // 크롭할 이미지 로드하기 MyCanvas.prototype.initCropImage = function () { - //const my = this; + //console.log('initCropImage', this.drawnImgSrc.substr(20, 30)); + this.clear(); this.drawnImg = new Image(); this.drawnImg.src = this.drawnImgSrc; }; @@ -32,6 +64,7 @@ const getSizeToKeepImageScale = (img, canvas) => { // 이미지 그리기 (투명도 설정) MyCanvas.prototype.drawImage = function (alpha) { + //console.log('drawImage', this.drawnImg.src.substr(20, 30)); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.globalAlpha = alpha; // 투명도 설정 @@ -65,6 +98,7 @@ MyCanvas.prototype.handleMouseDown = function (e) { } // 세로 스크롤값 적용 this.points.push({ x, y }); + console.log(this.points); // 클리핑 패스 라인 그리기 this.drawOutline(); @@ -158,7 +192,7 @@ MyCanvas.prototype.crop = function () { this.points.length = 0; }; -MyCanvas.prototype.makePattern = function (prop = null) { +MyCanvas.prototype.makePattern = function (prop) { // 패턴 설정값 let { color, @@ -170,13 +204,13 @@ MyCanvas.prototype.makePattern = function (prop = null) { type, } = prop; canvasWidth = parseInt(canvasWidth ? canvasWidth : this.canvas.width, 10); - canvasHeight = parseInt(canvasHeight ? canvasWidth : this.canvas.height, 10); + canvasHeight = parseInt(canvasHeight ? canvasHeight : this.canvas.height, 10); imgWidth = parseInt(imgWidth ? imgWidth : this.drawnImg.width, 10); imgHeight = parseInt(imgHeight ? imgHeight : this.drawnImg.height, 10); gap = parseInt(gap, 10); - this.ctx.save(); - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + // 캔버스 초기화 + this.clear(); // 배경색 설정 this.ctx.beginPath(); @@ -187,8 +221,21 @@ MyCanvas.prototype.makePattern = function (prop = null) { // 이미지 뿌리기 const loopX = Math.ceil(canvasWidth / (gap + imgWidth)); const loopY = Math.ceil(canvasHeight / (gap + imgHeight)); + console.log( + 'makePattern', + canvasWidth, + canvasHeight, + imgWidth, + imgHeight, + loopX, + loopY, + ); - console.log('makePattern', canvasWidth, gap, imgWidth, loopX, loopY); + // props 사이즈만큼 context 크기 클립해두고 그 안에서 패턴 뿌림 + // 그 뒤 다시 이전 context로 restore + this.ctx.save(); + this.ctx.rect(0, 0, canvasWidth, canvasHeight); + this.ctx.clip(); let y = 0; for (let i = 0; i < loopY; i++) { @@ -200,21 +247,10 @@ MyCanvas.prototype.makePattern = function (prop = null) { x += imgWidth + gap; } y += imgHeight + gap; - console.log(x, y); } - this.ctx.restore(); - // 캔버스 생성 - const c = document.createElement('canvas'); - const cx = c.getContext('2d'); - // 사이즈 설정(prop.bgWidth, prop.bgHeight) - c.width = canvasWidth; - c.height = canvasHeight; - - // 새로운 캔버스에 패턴 그리기 - cx.drawImage(this.canvas, 0, 0, canvasWidth, canvasHeight); - this.drawnImgSrc = c.toDataURL(); - //this.drawImage(1.0); + this.ctx.restore(); + this.drawnImgSrc = this.canvas.toDataURL(); // const my = this; // const img = new Image(); diff --git a/src/redux/pictures.js b/src/redux/pictures.js index 740a864..d0a8f70 100644 --- a/src/redux/pictures.js +++ b/src/redux/pictures.js @@ -58,11 +58,7 @@ export function* pictureSaga() { yield takeEvery(DELETE_PICTURE, deletePictureSaga); } -const initialState = { - pictures: [], - selectedPictures: [], // 삭제 - drawnPicture: null, - canvasMode: null, +export const initialData = { properties: { color: '#ffffff', canvasWidth: 0, @@ -74,6 +70,14 @@ const initialState = { }, }; +const initialState = { + pictures: [], + selectedPictures: [], // 삭제 + drawnPicture: null, + canvasMode: null, + properties: initialData.properties, +}; + export default function pictures(state = initialState, action) { //console.log('reducer', action.type); switch (action.type) {