Skip to content

Commit c30bf92

Browse files
committed
feat: textAlign
1 parent 9b25a20 commit c30bf92

File tree

5 files changed

+143
-74
lines changed

5 files changed

+143
-74
lines changed

src/assets/icons.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,12 @@ export const toggleButton =
2424

2525
export const blur =
2626
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAACXBIWXMAAAsTAAALEwEAmpwYAAABQ0lEQVR4nOXWzSsFURjH8S8rikv5Byhs7Sy85S0WVhb+GupuLGxk60bKTvwHFqRElGRzs2LJwms3rLg69Ts1i9u9M885c0ue+tU5Z3rm08ycmQb+WLUB54obN602gKqy3ix0DPhOwG48mTfaCdwnUJ87oCNPeK0G6rOaF9oHfNWB3bHePOCDOqjPXmx0BPhJAbuMx0JbgMuUqMtFLHghA+ozHwM+NcBnMZ5t1ZjhEHg3AN62ogXgMwD+0Jcucy0FoD6LFngnAmy63TcR4GsL/KZmt7NHjfCrBa6ouV3zJwP8boFv1Tyk+aEBLlvgLTWvaD5ngEsWeFbND0CX1pYzwtMY60Qn2AdatTYFXKVAjwioQeAlgXdrvacB+gz0E1gTCfwRKAKbDdBoPwMDwHHK2xt8pbVqRju1rPe8onEpZCPxL+oX1mA2OnIJA6MAAAAASUVORK5CYII=';
27+
28+
export const alignStart =
29+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAk0lEQVR4nO3aQQqDMBQE0Dlek/uvWwjeo8UTVFTiF9+D7DPoYhiSAACV9SSfJN/iZ71jOyPwKBBm61kE3mH9Td4Fvt6/s97xtScgAHfSJzWt05rSnZrWkgLG0wK3SU1LUwJgA5tW5q8ZU4vLeFrgZtMCoI5u00rt4nDUuFtTOqrZtACoo3unlcunHJtWbFoAQIr5AdE+2j1jRhTnAAAAAElFTkSuQmCC';
30+
31+
export const alignCenter =
32+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAfklEQVR4nO3WQQqEMBBFwX887fuvFcR7zJATzCBCWqmC3qdJIC8BADqrJHuST/MZZ1zvWPhosMy/c1r4gvFMtga392vGGZcrCwLwJKW0Mv3PVVq5MS1XpQVAH6W0Mr2olFaUFgDvVEor04tKaUVpAfBOpbQyvaiUVpQWAJBmvtJoWcwlcdsDAAAAAElFTkSuQmCC';
33+
34+
export const alignEnd =
35+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAkElEQVR4nO3YQQrCMBQE0Dmeyf3XCqX3UHICJQ0xmvfg7zO0i2ESAGBlNckjyXPxa28sIwIfC4T59E6BO7Tf5L7A13t37Y23noAA7KROamrDmtYvNbUhTeuq7QKXSU1N0wLYQrVpZfqaMbV4HLsFLjYtAP5HtWll7eJxlU0rNi0Autm08v0px6YVmxYAkMW8AOsU2j1bZwouAAAAAElFTkSuQmCC';

src/components/Canvas/index.tsx

Lines changed: 81 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const Canvas = React.forwardRef(({ canvasState }: CanvasProps, ref: any) => {
3434
handleCanvasOffsetReset,
3535
} = useDragAndDropText(+canvasWidth, +canvasHeight);
3636

37-
const getLineWidthByStrokeType = () => {
37+
const getLineWidthByStrokeType = useCallback(() => {
3838
const strokeObj = {
3939
None: 0,
4040
Thin: 3,
@@ -43,7 +43,7 @@ const Canvas = React.forwardRef(({ canvasState }: CanvasProps, ref: any) => {
4343
} as { [key: string]: number };
4444

4545
return strokeObj[fontStrokeType];
46-
};
46+
}, [fontStrokeType]);
4747

4848
const getCenterX = useCallback(
4949
(lineMaxWidth: number) => {
@@ -77,7 +77,7 @@ const Canvas = React.forwardRef(({ canvasState }: CanvasProps, ref: any) => {
7777

7878
return { x, y };
7979
},
80-
[dragAndDropTextData, canvasHeight]
80+
[dragAndDropTextData, canvasHeight, getCenterX]
8181
);
8282

8383
const setFontStroke = useCallback(
@@ -91,79 +91,95 @@ const Canvas = React.forwardRef(({ canvasState }: CanvasProps, ref: any) => {
9191
[fontStrokeType, strokeColor]
9292
);
9393

94-
const rotateCanvas = (ctx: CanvasRenderingContext2D) => {
95-
const { offsetX, offsetY } = dragAndDropTextData;
96-
const centerX = +canvasWidth / 2;
97-
const centerY = +canvasHeight / 2;
98-
const moveX = offsetX ? offsetX : centerX;
99-
const moveY = offsetY ? offsetY : centerY;
100-
101-
ctx.translate(moveX, moveY);
102-
ctx.rotate((+angle * Math.PI) / 180);
103-
ctx.translate(-moveX, -moveY);
104-
};
105-
106-
const fillCanvasMultiLineText = (
107-
ctx: CanvasRenderingContext2D,
108-
lines: string[]
109-
) => {
110-
const size = +fontSize.replace('px', '');
111-
const fontLineHeight = size + +lineHeight;
112-
let lineMaxWidth = 0;
113-
114-
lines.forEach((line) => {
115-
lineMaxWidth = Math.max(lineMaxWidth, ctx.measureText(line).width);
116-
});
117-
118-
lines.forEach((line, idx) => {
119-
const { x, y } = getMultiLinePosition(
120-
lines.length,
121-
fontLineHeight,
122-
lineMaxWidth,
123-
idx
124-
);
125-
126-
ctx.save();
127-
ctx.translate(x, y);
94+
const rotateCanvas = useCallback(
95+
(ctx: CanvasRenderingContext2D) => {
96+
const { offsetX, offsetY } = dragAndDropTextData;
97+
const centerX = +canvasWidth / 2;
98+
const centerY = +canvasHeight / 2;
99+
const moveX = offsetX ? offsetX : centerX;
100+
const moveY = offsetY ? offsetY : centerY;
128101

129-
setFontStroke(ctx, line);
102+
ctx.translate(moveX, moveY);
103+
ctx.rotate((+angle * Math.PI) / 180);
104+
ctx.translate(-moveX, -moveY);
105+
},
106+
[dragAndDropTextData, canvasWidth, canvasHeight, angle]
107+
);
130108

131-
ctx.fillStyle = fontColor.hex;
132-
ctx.fillText(line, 0, 0);
133-
ctx.restore();
134-
});
135-
};
109+
const fillCanvasMultiLineText = useCallback(
110+
(ctx: CanvasRenderingContext2D, lines: string[]) => {
111+
const size = +fontSize.replace('px', '');
112+
const fontLineHeight = size + +lineHeight;
113+
let lineMaxWidth = 0;
114+
115+
lines.forEach((line) => {
116+
lineMaxWidth = Math.max(lineMaxWidth, ctx.measureText(line).width);
117+
});
118+
119+
lines.forEach((line, idx) => {
120+
const { x, y } = getMultiLinePosition(
121+
lines.length,
122+
fontLineHeight,
123+
lineMaxWidth,
124+
idx
125+
);
126+
127+
ctx.save();
128+
ctx.translate(x, y);
129+
130+
setFontStroke(ctx, line);
131+
132+
ctx.fillStyle = fontColor.hex;
133+
ctx.fillText(line, 0, 0);
134+
ctx.restore();
135+
});
136+
},
137+
[fontColor, fontSize, lineHeight, getMultiLinePosition, setFontStroke]
138+
);
136139

137-
const setCanvasText = (ctx: CanvasRenderingContext2D) => {
138-
const lines = value.split('\n');
139-
const size = +fontSize.replace('px', '');
140+
const setCanvasText = useCallback(
141+
(ctx: CanvasRenderingContext2D) => {
142+
const lines = value.split('\n');
143+
const size = +fontSize.replace('px', '');
140144

141-
ctx.font = `${size}px ${fontFamily}`;
142-
ctx.textAlign = textAlign;
143-
ctx.textBaseline = 'middle';
145+
ctx.font = `${size}px ${fontFamily}`;
146+
ctx.textAlign = textAlign;
147+
ctx.textBaseline = 'middle';
144148

145-
ctx.save();
149+
ctx.save();
146150

147-
rotateCanvas(ctx);
148-
fillCanvasMultiLineText(ctx, lines);
151+
rotateCanvas(ctx);
152+
fillCanvasMultiLineText(ctx, lines);
149153

150-
ctx.restore();
151-
};
154+
ctx.restore();
155+
},
156+
[
157+
value,
158+
fontSize,
159+
fontFamily,
160+
textAlign,
161+
rotateCanvas,
162+
fillCanvasMultiLineText,
163+
]
164+
);
152165

153-
const fillBackground = (canvas: HTMLCanvasElement) => {
154-
const ctx = canvas.getContext('2d');
166+
const fillBackground = useCallback(
167+
(canvas: HTMLCanvasElement) => {
168+
const ctx = canvas.getContext('2d');
155169

156-
if (!ctx) return;
170+
if (!ctx) return;
157171

158-
if (selectedImage) {
159-
if (isBlur) ctx.filter = 'blur(5px)';
160-
ctx.drawImage(selectedImage, 0, 0);
161-
return;
162-
}
172+
if (selectedImage) {
173+
if (isBlur) ctx.filter = 'blur(5px)';
174+
ctx.drawImage(selectedImage, 0, 0);
175+
return;
176+
}
163177

164-
ctx.fillStyle = bgColor.hex;
165-
ctx.fillRect(0, 0, canvas.width, canvas.height);
166-
};
178+
ctx.fillStyle = bgColor.hex;
179+
ctx.fillRect(0, 0, canvas.width, canvas.height);
180+
},
181+
[selectedImage, bgColor, isBlur]
182+
);
167183

168184
useEffect(() => {
169185
if (!ref.current) return;

src/components/ColorPicker/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const ColorPickerPicker = ({
7171
return (
7272
<div style={wrapperStyle}>
7373
<IconButton
74-
isOpenColorPicker={isOpenColorPicker}
74+
isOpen={isOpenColorPicker}
7575
onClick={handleOpenColorPicker}
7676
isBorder={true}>
7777
{children}

src/components/Icon/styled.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import styled from '@emotion/styled';
22

33
export const IconButton = styled.button<{
4-
isOpenColorPicker?: boolean;
4+
isOpen?: boolean;
55
isBorder?: boolean;
66
}>`
77
padding: 4px 5px;
@@ -11,12 +11,10 @@ export const IconButton = styled.button<{
1111
align-items: center;
1212
cursor: pointer;
1313
14-
${({ isBorder, isOpenColorPicker }) => {
14+
${({ isBorder, isOpen }) => {
1515
if (!isBorder) return `border: none;`;
1616
return `
17-
border: ${
18-
isOpenColorPicker ? '1px solid #0e1b30;' : '1px solid #cccccc;'
19-
};
17+
border: ${isOpen ? '1px solid #0e1b30;' : '1px solid #cccccc;'};
2018
`;
2119
}}
2220

src/components/TG.tsx

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ import {
2323
strokeTypes,
2424
} from '@constants/select';
2525
import { CanvasState } from '../types/canvas';
26-
import { fill, font, stroke, blur } from '@assets/icons';
26+
import {
27+
fill,
28+
font,
29+
stroke,
30+
blur,
31+
alignStart,
32+
alignCenter,
33+
alignEnd,
34+
} from '@assets/icons';
2735
import { Color, useColor } from 'react-color-palette';
2836
import { BodyWrapper, ContentWrapper, InnerWrapper } from './Layout/styled';
2937
import * as S from './TG.styled';
@@ -47,13 +55,13 @@ const TG = ({
4755
value: 'Simple Thumbnail\nGenerator 😁',
4856
fontSize: '30px',
4957
fontStrokeType: 'None',
58+
textAlign: 'center',
5059
fontFamily: 'Arial',
5160
canvasWidth: '600',
5261
canvasHeight: '400',
5362
imageType: 'png',
5463
angle: '0',
5564
lineHeight: '0',
56-
textAlign: 'center',
5765
isBlur: false,
5866
selectedImage: null,
5967
isBlockEvent: false,
@@ -83,6 +91,40 @@ const TG = ({
8391
[canvasState.fontSize]
8492
);
8593

94+
const textAlignIcon = useMemo(() => {
95+
const { textAlign } = canvasState;
96+
97+
if (textAlign === 'center') return alignCenter;
98+
if (textAlign === 'end') return alignEnd;
99+
return alignStart;
100+
}, [canvasState.textAlign]);
101+
102+
const onChangeTextAlign = useCallback(() => {
103+
const getNextTextAlign = () => {
104+
const { textAlign } = canvasState;
105+
106+
if (textAlign === 'center') return 'end';
107+
if (textAlign === 'end') return 'start';
108+
return 'center';
109+
};
110+
111+
setCanvasState({
112+
...canvasState,
113+
textAlign: getNextTextAlign(),
114+
});
115+
}, [canvasState]);
116+
117+
const onChangeStrokeColor = useCallback(
118+
(color: Color) => {
119+
setStrokeColor(color);
120+
121+
if (canvasState.fontStrokeType === 'None') {
122+
setCanvasState({ ...canvasState, fontStrokeType: 'Normal' });
123+
}
124+
},
125+
[canvasState, setStrokeColor]
126+
);
127+
86128
const toggleIsBlockEvent = useCallback(() => {
87129
setCanvasState({
88130
...canvasState,
@@ -208,6 +250,10 @@ const TG = ({
208250

209251
<S.TGControllerWrapper>
210252
<FileInput width={20} height={20} onChangeImage={onChangeImage} />
253+
<IconButton isBorder onClick={onChangeTextAlign}>
254+
<Icon src={textAlignIcon} width={20} height={20} />
255+
</IconButton>
256+
211257
<ColorPicker
212258
color={bgColor}
213259
setColor={onChangeBgColor}
@@ -222,7 +268,7 @@ const TG = ({
222268
</ColorPicker>
223269
<ColorPicker
224270
color={strokeColor}
225-
setColor={setStrokeColor}
271+
setColor={onChangeStrokeColor}
226272
toggleIsBlockEvent={toggleIsBlockEvent}>
227273
<Icon src={stroke} width={20} height={20} />
228274
</ColorPicker>

0 commit comments

Comments
 (0)