35 comments

[ 14.8 ms ] story [ 1269 ms ] thread
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASIAAAAeCAYAAACIcRPUAAAMLklEQVR4Xu2aB6wVRRSGDx3pRXrnUZUu0juE3jEQgUAghA6KSJGi9N57AKW3ECABJBQFUbDQCR1CFxClg/RivknmZd++ve/uLvf6gMxJCMrdnZ3555z//OfMxImIiHglxgwCBgGDQCwiEMcQUSyibz5tEDAIKAQMERlHMAgYBGIdAUNEsb4FZgIGAYOAISLjAwYBg0CsI2CIKNa3wEzAIGAQMERkfMAgYBCIdQQMEcX6FpgJGAQMAoaIjA8YBAwCsY6AJyKKHz++TJ06VZYvXy67d+92nHyxYsWkc+fO8tVXX8ndu3dl1qxZMmfOHDl06FDIF5s1a1YZNGiQfPPNN3L9+vWQj9+6dWupUKGCjBo1Si5duhTy8YMNOHnyZFm7dq388ssvAR9t2LChFC5cWEaOHBlsuCi/p06dWkaPHi1HjhyRmTNnenr3XXw4f/78UqdOHcmcObMkTZpUfv75Z1m1apWvpVarVk2aN2+u3n327Jn06NHD1zgxvZQuXTpp0KCB5MmTR1KkSCF//fWXjBgxIuTfcRowYcKEUq9ePSlevLikSpVKbt++Lb///rts3rxZXr3ydz/aExExqa+//lpOnToVcJOYYNWqVeXLL7+UuHHjvtVEVLNmTSlSpIgsWbIkLEQXzGvCSUTJkyeXTp06ydmzZ2XdunXBpvJO/54jRw7p27ev7NixQ/744w95+PChIpB79+75WjeBmjhxYvnoo4+kadOmIScixiYOr169qoIfIsBu3rzpa75eX8JvIGySJHPIli2bkLR//PFH+f77770Op573TETt2rUTsumkSZMcP8gk33vvPZkyZcpbT0S+EA3hS+EkohBO860fqkmTJvLhhx+GXFGUKVNGWrVqFXIiYq6orC+++EKR5v9pKLHhw4fLkCFDlArTBuEWLFjQszLX73smoho1akjdunUVCBiAJEuWTMl8jEkePHhQsaVVET148EAaN24slFOAd/ToUfXM48ePIxcD05cuXVo+/vhjxbgJEiSQW7duyf79++WHH36IBD1fvnyR33fahO7du8vz5899748XaV2iRAmpWLGiZMmSRUl61vPnn3+qcurAgQPy8uVLz/OglA1k06dPl2PHjkX+rEuzsWPHCv8NdiQCnGT9+vVy/PjxaENB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...
> What is this useful for?

I suspect it may be used to enforce custom fonts in an old-fashioned way, but nowadays you would simply deliver the font with you site, or use a font service.

Or, it could serve as some kind of "copy-protection", but it would not protect against OCR.

Thinking more about it, this may be a diabolic way to collect strings from people who want to make them "copy-protectable". So you don't need OCR anymore, but fetch them from the source. ;-)

Either way, it's a nice project with a good execution!

> What is this useful for?

Twitter.

Cmon - don't call this a project. It is a single function. However, I like how OP realized that vanilla js was the correct choice for a simple job.
Haha, I agree, it's pretty small. If you look at my other projects, I <3 vanilla js.
Could also be useful, if extended with webfonts, for something like a custom T-shirt/mug/etc preview generator.
> Either way, it's a nice project with a good execution!

I agree. Very quick too. I tried it with some ascii art and it makes the overall image a bit thinner, though:(

(not handling spaces correctly)

Image: https://sli.mg/ZSLtf6

Ascii Source: http://www.ascii-art.de/ascii/ghi/gary_larson.txt

Yes, I'm working on this.

The problem is that it finds the longest line, by the number of characters in it. Instead of taking the actual width.

This is great news. I can understand wanting to keep this as simple as possible but... if you decide to add any features:

1. Transparent Background (and)

2. Choice of character color (to integrate better with a webpage's color scheme, for example)

Well, I mainly made this after seeing people on Twitter tweet out code perfectly formatted and highlighted, but it's just a screenshot of their text editor.

So basically, the main use-case of Alter is that it lets you tweet code snippets that are a little over 140 characters that are formatted correctly.

It can also be used for more, such as "copy-protection", like you said above.

I looked at the code.

  var lineLengthOrder = lines.slice(0).sort(function(a, b) {
      return b.length - a.length;
  });
  ctx.canvas.width = ctx.measureText(lineLengthOrder[0]).width + 25;
Ok, written by a beginner. A few tips:

1. Sorting all the lines just to get the longest is pretty wasteful. Try an explicit loop that iterates over the array once, keeping track of the longest line seen so far.

2. Using measureText to get the visible length of a line is a good idea, but doesn't mesh with using the .length property to find the longest line. Some characters may be wider than others, so a line containing fewer characters (shorter .length) could actually be longer (using measureText) than the one with the most characters. Try copy-pasting some Chinese from https://zh.wikipedia.org and compare with a line containing the same number of Latin characters.

To also say something positive: At least it looks nice!

How would you have written that?
For point 1 I would use "reduce":

  lines.reduce((longest, line) => (longest.length > line.length) ? longest : line, "")
https://developer.mozilla.org/en/docs/Web/JavaScript/Referen...

EDIT: sorry, initial code returned length, not line itself

This returns the length, not the line itself.

Edit: `lines.reduce((line, line_) => line_.length > line.length ? line_ : line, '')`

It is a good use of reduce (accumulate as I call it), but maybe slightly overkill for this case compared to a simple iteration or a max function.
Regarding point number 2, it should be noted that this only applies for non mono-spaced fonts. If you were using a mono-spaced font, then each character glyph uses the same physical canvas width as all others.
No, you are wrong. Unicode characters can be halfwidth or fullwidth even for monospaced fonts.
Okay - I didn't know about those. I stand corrected.
Would it be a bit better if I go through each line and instead of getting the length, get the actual width of it?
Regarding 1: I think the sorting approach is actually really nice in this case. Clearly it's not as efficient as a simple loop, but it's safer and more readable. Explicit looping and mutation can introduce nasty bugs which this kind of declarative approach can't.
I am of the same opinion, always go for correctness before performance, efficiency can be tuned later if it's even needed.
No need to mutate/slice/sort anything

    Math.max(...lines.map((line) => ctx.measureText(line).width))
Thank you so much! I'm not the best at HTML5 canvas, more of front end. (I'm also 13).

I'll iterate through the array instead. If you have any other suggestions, the repo is at https://github.com/KingPixil/alter

Dude - you're 13? You're awesome! Keep at it!
Thanks :D
Optimisation 1 might be premature. If most cases the text is short then the functional approach is arguably nicer even if a tad slower.
I like it.

It does what it says (although I'm not sure how useful what it does is).

Still, good job.

Thanks, features to change font and color will be added soon, this is just a little concept :)
(comment deleted)
Awesome, another tool to make blind people not able to read messages on Twitter! +1
The best part about this post is that the author is 13 years old. You are going to have an amazing career. I started programming around your age too. Trust me, it helps :)
Thank you so much for the kind words :)