Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* This meme creation tool app will allow users to create memes by selecting an image and adding top and bottom text.
* We'll use HTML5 Canvas to render the meme directly in the browser, with custom positioning for different meme templates.
*/
/** @jsxImportSource https://esm.sh/react */
import React, { useState, useRef, useEffect } from "https://esm.sh/react";
import { createRoot } from "https://esm.sh/react-dom/client";
interface MemeTemplate {
id: string;
name: string;
url: string;
}
interface TextPosition {
x: number;
y: number;
maxWidth: number;
color: string;
}
function App() {
const [templates, setTemplates] = useState<MemeTemplate[]>([]);
const [selectedTemplate, setSelectedTemplate] = useState<string>("");
const [topText, setTopText] = useState<string>("");
const [bottomText, setBottomText] = useState<string>("");
const [memeUrl, setMemeUrl] = useState<string>("");
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
fetch("/templates")
.then(response => response.json())
.then(data => setTemplates(data));
}, []);
const getTextPositions = (imageWidth: number, imageHeight: number, templateId: string): [TextPosition, TextPosition] => {
const defaultColor = "white";
switch (templateId) {
case "1": // Drake
return [
{ x: imageWidth * 0.7, y: imageHeight * 0.25, maxWidth: imageWidth * 0.5, color: "black" },
{ x: imageWidth * 0.7, y: imageHeight * 0.75, maxWidth: imageWidth * 0.5, color: "black" }
];
case "2": // Distracted Boyfriend
return [
{ x: imageWidth * 0.6, y: imageHeight * 0.1, maxWidth: imageWidth * 0.3, color: defaultColor },
{ x: imageWidth * 0.8, y: imageHeight * 0.1, maxWidth: imageWidth * 0.3, color: defaultColor }
];
case "3": // Two Buttons
return [
{ x: imageWidth * 0.5, y: imageHeight * 0.1, maxWidth: imageWidth * 0.8, color: defaultColor },
{ x: imageWidth * 0.5, y: imageHeight * 0.9, maxWidth: imageWidth * 0.8, color: defaultColor }
];
case "4": // Expanding Brain
return [
{ x: imageWidth * 0.5, y: imageHeight * 0.15, maxWidth: imageWidth * 0.8, color: defaultColor },
{ x: imageWidth * 0.5, y: imageHeight * 0.95, maxWidth: imageWidth * 0.8, color: defaultColor }
];
case "5": // Change My Mind
return [
{ x: imageWidth * 0.5, y: imageHeight * 0.7, maxWidth: imageWidth * 0.8, color: defaultColor },
{ x: imageWidth * 0.5, y: imageHeight * 0.9, maxWidth: imageWidth * 0.8, color: defaultColor }
];
default:
return [
{ x: imageWidth * 0.5, y: imageHeight * 0.1, maxWidth: imageWidth * 0.9, color: defaultColor },
{ x: imageWidth * 0.5, y: imageHeight * 0.9, maxWidth: imageWidth * 0.9, color: defaultColor }
];
}
};
const generateMeme = () => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const selectedImage = templates.find(t => t.id === selectedTemplate);
if (!selectedImage) return;
const image = new Image();
image.crossOrigin = "anonymous";
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
const fontSize = Math.floor(image.height / 15);
ctx.font = `bold ${fontSize}px Impact, sans-serif`;
ctx.textAlign = "center";
const [topPosition, bottomPosition] = getTextPositions(image.width, image.height, selectedTemplate);
// Function to draw text with word wrap
const drawText = (text: string, position: TextPosition) => {
ctx.fillStyle = position.color;
ctx.strokeStyle = "black";
ctx.lineWidth = Math.floor(fontSize / 15);
stevekrouse-memegenerator.web.val.run
August 23, 2024