Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useState, useRef } from 'react';
- import styled from 'styled-components';
- const Container = styled.div`
- padding: 20px;
- display: flex;
- flex-direction: column;
- gap: 20px;
- max-width: 800px;
- margin: 0 auto;
- `;
- const TextArea = styled.textarea`
- width: 100%;
- min-height: 150px;
- padding: 10px;
- border: 1px solid #ccc;
- border-radius: 4px;
- resize: vertical;
- `;
- const InputGroup = styled.div`
- display: flex;
- gap: 10px;
- align-items: center;
- `;
- const NumberInput = styled.input`
- width: 100px;
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- `;
- const TextInput = styled.input`
- width: 200px;
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- `;
- const ColorInput = styled.input`
- width: 50px;
- height: 30px;
- padding: 0;
- border: 1px solid #ccc;
- border-radius: 4px;
- `;
- const Select = styled.select`
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- `;
- const FileInput = styled.input`
- padding: 8px;
- `;
- const Button = styled.button`
- width: 100%;
- padding: 10px 20px;
- background-color: #007bff;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- &:hover {
- background-color: #0056b3;
- }
- `;
- const PreviewContainer = styled.div`
- margin-top: 20px;
- position: relative;
- `;
- const PreviewImage = styled.img`
- max-width: 100%;
- display: block;
- `;
- interface TextOverlayProps {
- top: number;
- left: number;
- fontSize: number;
- color: string;
- fontWeight: number;
- }
- const TextOverlay = styled.div<TextOverlayProps>`
- position: absolute;
- top: ${(props: TextOverlayProps) => props.top}px;
- left: ${(props: TextOverlayProps) => props.left}px;
- color: ${(props: TextOverlayProps) => props.color};
- font-size: ${(props: TextOverlayProps) => props.fontSize}px;
- font-weight: ${(props: TextOverlayProps) => props.fontWeight};
- white-space: nowrap;
- `;
- const Home = () => {
- const [communityName, setCommunityName] = useState('');
- const [image, setImage] = useState<File | null>(null);
- const [top, setTop] = useState(0);
- const [left, setLeft] = useState(0);
- const [fontSize, setFontSize] = useState(16);
- const [textColor, setTextColor] = useState('#000000');
- const [fontWeight, setFontWeight] = useState(400);
- const [additionalText, setAdditionalText] = useState('');
- const [additionalTop, setAdditionalTop] = useState(0);
- const [additionalLeft, setAdditionalLeft] = useState(0);
- const [additionalFontSize, setAdditionalFontSize] = useState(16);
- const [additionalTextColor, setAdditionalTextColor] = useState('#000000');
- const [additionalFontWeight, setAdditionalFontWeight] = useState(400);
- const [previewUrl, setPreviewUrl] = useState<string | null>(null);
- const [textLines, setTextLines] = useState<string[]>([]);
- const canvasRef = useRef<HTMLCanvasElement>(null);
- const fontWeights = [300, 400, 500, 700, 800, 900];
- const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- if (e.target.files && e.target.files[0]) {
- const file = e.target.files[0];
- setImage(file);
- setPreviewUrl(URL.createObjectURL(file));
- }
- };
- const processText = () => {
- const lines = communityName.split('\n').filter(line => line.trim());
- setTextLines(lines);
- };
- const exportImages = async () => {
- if (!image || !previewUrl) return;
- const img = new Image();
- img.src = previewUrl;
- await new Promise((resolve) => {
- img.onload = resolve;
- });
- for (let i = 0; i < textLines.length; i++) {
- const canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- const ctx = canvas.getContext('2d');
- if (!ctx) return;
- // Draw the original image
- ctx.drawImage(img, 0, 0);
- // Draw the additional text
- ctx.font = `${additionalFontWeight} ${additionalFontSize}px GFF Latin`;
- ctx.fillStyle = additionalTextColor;
- ctx.fillText(additionalText, additionalLeft, additionalTop + additionalFontSize + 10);
- // Draw only the current line of text
- ctx.font = `${fontWeight} ${fontSize}px Arial`;
- ctx.fillStyle = textColor;
- ctx.fillText(textLines[i], left, top + fontSize + 10);
- // Create download link
- const link = document.createElement('a');
- link.download = `image_${i + 1}.png`;
- link.href = canvas.toDataURL('image/png');
- link.click();
- }
- };
- return (
- <Container>
- <TextArea
- name="community_name"
- value={communityName}
- onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setCommunityName(e.target.value)}
- placeholder="Enter text (one line per item)"
- />
- <InputGroup>
- <FileInput
- type="file"
- name="image"
- accept="image/*"
- onChange={handleImageChange}
- />
- </InputGroup>
- <div className="row">
- <div className="col-6">
- <InputGroup>
- <label>Font Size:</label>
- <NumberInput
- type="number"
- name="font_size"
- value={fontSize}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFontSize(Number(e.target.value))}
- />
- </InputGroup>
- <InputGroup>
- <label>Text Color:</label>
- <ColorInput
- type="color"
- value={textColor}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setTextColor(e.target.value)}
- />
- <label>Font Weight:</label>
- <Select
- value={fontWeight}
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setFontWeight(Number(e.target.value))}
- >
- {fontWeights.map(weight => (
- <option key={weight} value={weight}>{weight}</option>
- ))}
- </Select>
- </InputGroup>
- <InputGroup>
- <label>Text Position (Top):</label>
- <NumberInput
- type="number"
- name="top"
- value={top}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setTop(Number(e.target.value))}
- />
- <label>Left:</label>
- <NumberInput
- type="number"
- name="left"
- value={left}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLeft(Number(e.target.value))}
- />
- </InputGroup>
- </div>
- <div className="col-6">
- <InputGroup>
- <label>Additional Text:</label>
- <TextInput
- type="text"
- value={additionalText}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAdditionalText(e.target.value)}
- placeholder="Enter additional text"
- />
- </InputGroup>
- <InputGroup>
- <label>Additional Font Size:</label>
- <NumberInput
- type="number"
- name="font_size_text"
- value={additionalFontSize}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAdditionalFontSize(Number(e.target.value))}
- />
- </InputGroup>
- <InputGroup>
- <label>Additional Text Color:</label>
- <ColorInput
- type="color"
- value={additionalTextColor}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAdditionalTextColor(e.target.value)}
- />
- <label>Font Weight:</label>
- <Select
- value={additionalFontWeight}
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setAdditionalFontWeight(Number(e.target.value))}
- >
- {fontWeights.map(weight => (
- <option key={weight} value={weight}>{weight}</option>
- ))}
- </Select>
- </InputGroup>
- <InputGroup>
- <label>Additional Text Position (Top):</label>
- <NumberInput
- type="number"
- name="additional_top"
- value={additionalTop}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAdditionalTop(Number(e.target.value))}
- />
- <label>Left:</label>
- <NumberInput
- type="number"
- name="additional_left"
- value={additionalLeft}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAdditionalLeft(Number(e.target.value))}
- />
- </InputGroup>
- </div>
- </div>
- <div className="row">
- <div className="col-6">
- <Button onClick={processText}>Process Text</Button>
- </div>
- <div className="col-6">
- <Button onClick={exportImages}>Export Images</Button>
- </div>
- </div>
- {previewUrl && (
- <PreviewContainer>
- <PreviewImage src={previewUrl} alt="Preview" />
- {textLines.map((line, index) => (
- <TextOverlay
- key={index}
- top={top}
- left={left}
- fontSize={fontSize}
- color={textColor}
- fontWeight={fontWeight}
- >
- {line}
- </TextOverlay>
- ))}
- {additionalText && (
- <TextOverlay
- top={additionalTop}
- left={additionalLeft}
- fontSize={additionalFontSize}
- color={additionalTextColor}
- fontWeight={additionalFontWeight}
- >
- {additionalText}
- </TextOverlay>
- )}
- </PreviewContainer>
- )}
- </Container>
- );
- };
- export default Home;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement