The majority of this last year has been light on programming. That said, I found an immediate need for a quick text generator to recreate the "100 points" emoji with [mostly troll] text.
A handful of the technologies I will use listed below, spoiler alert (it's just what I am most comfortable with):
- React (Vite)
- Bootstrap (for easy responsiveness)
- GO (for any backend needed)
- MySQL (found a excuse to implement a database)
The Foundation
First thing's first, I need a font. After comparing a handful of these "100" emojis, I've now seen many different fonts, and slightly different styles. Google fonts was a first and last stop to find a font with a touch of handwriting style, without being too Comic Sans-y. Shantell Sans SemiBold 600 Italic? Sold!
Import a font, apply that to a canvas, write strings to canvas, draw some lines under that. The following is the logic portion of the React component that produces the emoji.
//Excuse the formatting
const [currentText, setCurrentText] = useState("");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
//background
ctx.canvas.width = ctx.measureText(currentText).width + 20
// ctx.fillStyle = "black";
ctx.globalAlpha = 0;
ctx.fillRect(0, 0, canvas.width, canvas.height);
//text
ctx.globalAlpha = 1;
ctx.font = "50px myFont, myFontCSS";
ctx.fillStyle = "red";
ctx.fillText(currentText, 10, 50);
const firstArcStart = 1.4815;
const secondArcStart = 1.4822;
const radian = ctx.measureText(currentText).width / ((2 * Math.PI) * 3690) * (2 * Math.PI);
//line1
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.lineWidth = 6;
ctx.lineCap = "round";
if (currentText.length > 0) {
ctx.arc(220, 3606, 3550, firstArcStart * Math.PI, (firstArcStart * Math.PI) + radian);
}
ctx.stroke();
//line2
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.lineWidth = 6;
//Below some width, the bottom arc will "wrap". This conditional is to not use an "offset" when text length is below 21
if (ctx.measureText(currentText).width > 21) {
ctx.arc(220, 3618, 3550, secondArcStart * Math.PI, ((secondArcStart - .0014) * Math.PI) + radian);
} else if ((ctx.measureText(currentText).width > 0) && (ctx.measureText(currentText).width <= 21)) {
ctx.arc(220, 3618, 3550, secondArcStart * Math.PI, ((secondArcStart) * Math.PI));
}
ctx.stroke();
This logic is actually in a function of its own, but for the sake of context is shown here with state variable and selectors. Surely the circle-math needs some refactoring, but it works well, as is for now, so it stays.
From here, I did wrap the logic in a conditional to handle when the measure of the string in pixels exceeds a certain length. The has a large enough radius to cover a good span, but short enough to show the "swoop" on single character. A dynamically changing radius is outside the scope of this project, and static/straight lines are not desired - also not interested in creating a font.

Copy-ability
It became apparent very quickly that, while using most browsers on PC, you can just right-click, copy the canvas, and the browser will handle turning this into a PNG. Safari on iPhone was the first indication that this was not going to work.
Easy fix - I use the canvas method canvas.toDataURL(), which returns the canvas as a base64 string and it's respective MIME type. I'm now using this as the src for <img> elements and hiding the canvas that produced it.
Most Recent Emojis Rendered
I needed some excuse to start up a database and backend. What better way than a feature that displays the last 5 strings entered. For this, I am using Gin in Go to serve up the world's smallest API. Of course this could have been done entirely within the standard library, but Gin just has so many GitHub stars and I like learning more about tools that are used at scale.
The strings are stored in a database on the same server. The backend is hosted on a port other than :80 or :443 and uses NGINX's reverse proxy feature to route requests accordingly.
/strings (submits current string)
POST accepts: {string: <string>}
/lastfive (returns last five [unique] strings entered)
GET returns: Array(5) {time: <string>, phrase: <string>}
[
{
"time": "2024-01-17 15:19:02",
"phrase": "Thisisalongstring"
},
....]
//TODO create endpoint to submit a string and return the DataURL representation/png file by API call.
When first visiting the site, there is a useEffect hook with an empty dependency array to fetch from this endpoint. This will be iterated over using our function from earlier, and return a handful of <img> elements and a relative time description (currently using momentJS).

Summary
This is my tentative summary.. It has been a good project so far to get back in the groove. It is still very basic - nearly no styling. Error handling needs some real attention. It has been fun so far.
Although it serves no real utility, I'm happy to have introduced something to the internet that I have not seen yet. I plan to pick at this moving forward in my free time.
A very creative, SEO friendly domain: 100emojigenerator.com
Cache me outside, how bout dat

While this is a very specific search term and target, it is still neat to see one of my sites show up as a first result.