242 lines
5.9 KiB
HTML
242 lines
5.9 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<title>I AM ERROR.</title>
|
||
|
<link rel="icon" type="image/png" href="zelda2-error.png" />
|
||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
<meta name="description" content="I AM ERROR. image generator"/>
|
||
|
<meta property="og:description" content="I AM ERROR. image generator"/>
|
||
|
<meta property="og:title" content="I AM ERROR."/>
|
||
|
<style>
|
||
|
* {
|
||
|
box-sizing: border-box;
|
||
|
}
|
||
|
body {
|
||
|
margin: 0;
|
||
|
font-family: sans-serif;
|
||
|
font-size: 16px;
|
||
|
background-color: #2a2a2a;
|
||
|
color: white;
|
||
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAAAAABzQ+pjAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAUSURBVAjXY9Rk5GBi+vuPhZ2FEQANXQJDZcY4EgAAAABJRU5ErkJggg==');
|
||
|
background-repeat: repeat;
|
||
|
}
|
||
|
|
||
|
h1 {
|
||
|
text-align: center;
|
||
|
font-size: 1rem;
|
||
|
margin: 1rem 0;
|
||
|
}
|
||
|
canvas {
|
||
|
display: none;
|
||
|
}
|
||
|
|
||
|
.render {
|
||
|
box-shadow:
|
||
|
0 0 1px black,
|
||
|
2px 2px 5px rgba(0, 0, 0, 0.75);
|
||
|
border: 2px solid white;
|
||
|
display: inline-block;
|
||
|
}
|
||
|
|
||
|
.render img {
|
||
|
display: block;
|
||
|
}
|
||
|
|
||
|
.container {
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
@font-face {
|
||
|
font-family: "Zelda II: The Adventure of Link";
|
||
|
src: url("./font/zelda-ii-the-adventure-of-link.ttf");
|
||
|
}
|
||
|
|
||
|
.iamerror-text {
|
||
|
font-family: "Zelda II: The Adventure of Link", sans-serif;
|
||
|
text-shadow: 2px 2px #4240FF;
|
||
|
color: white;
|
||
|
}
|
||
|
|
||
|
label {
|
||
|
display: block;
|
||
|
margin-bottom: 10px;
|
||
|
}
|
||
|
|
||
|
#text {
|
||
|
width: 256px;
|
||
|
height: 150px;
|
||
|
background-color: black;
|
||
|
border: 4px double white;
|
||
|
padding: 2px 10px;
|
||
|
}
|
||
|
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1><span class="iamerror-text">I AM ERROR GENERATOR.</span></h1>
|
||
|
<div class="container">
|
||
|
<div class="render">
|
||
|
<img width="256" height="240" />
|
||
|
</div>
|
||
|
<canvas id="iamerror" width="256" height="240"></canvas>
|
||
|
<p>
|
||
|
<textarea id="text" class="iamerror-text" placeholder="I AM ERROR." autofocus maxlength="40"></textarea>
|
||
|
</p>
|
||
|
</div>
|
||
|
|
||
|
<script>
|
||
|
(function() {
|
||
|
const colors = {
|
||
|
text: '#FFFFFF',
|
||
|
textShadow: '#4240FF',
|
||
|
background: '#000000',
|
||
|
};
|
||
|
|
||
|
const dimensions = {
|
||
|
left: 127,
|
||
|
top: 63,
|
||
|
width: 80,
|
||
|
height: 52,
|
||
|
};
|
||
|
|
||
|
const samples = [
|
||
|
'i am error.',
|
||
|
'can i eat your ham?',
|
||
|
'the crow flies at midnight.',
|
||
|
'pull my finger.',
|
||
|
'say hi to dark link for me.',
|
||
|
'are you my mother?',
|
||
|
'you shall not pass.',
|
||
|
'it smells like updog in here.',
|
||
|
'i like mary poppins.',
|
||
|
'oh my god, becky.',
|
||
|
'there is a bustle in my hedgerow.',
|
||
|
'you are the man now, dog.',
|
||
|
'want to see a magic trick?',
|
||
|
'yarp.',
|
||
|
'narp?',
|
||
|
'love is the last bastion of hope.',
|
||
|
'my beard is quite luxurious.',
|
||
|
'it is the springtime of my loving.',
|
||
|
'would you have any grey poupon?',
|
||
|
'come with me if you want to live.',
|
||
|
'my tummy feels funny.',
|
||
|
'are you still touching me?',
|
||
|
'your sound card works perfectly.',
|
||
|
'life is pain.',
|
||
|
'say hello to my little friend.',
|
||
|
'please do not make fun of my shirt.',
|
||
|
'bagu? what a terrible name.',
|
||
|
'i thought you were left- handed?',
|
||
|
];
|
||
|
|
||
|
const charWidth = 8;
|
||
|
const charHeight = charWidth;
|
||
|
const lineHeight = charHeight;
|
||
|
const charsPerLine = Math.floor(dimensions.width / charWidth);
|
||
|
const maxLines = Math.floor(dimensions.height / (charHeight + lineHeight));
|
||
|
|
||
|
const templateImg = document.createElement('img');
|
||
|
templateImg.src = './iamerror-template.png';
|
||
|
|
||
|
const canvas = document.querySelector('#iamerror');
|
||
|
const ctx = canvas.getContext('2d');
|
||
|
|
||
|
let imageLoaded = false;
|
||
|
|
||
|
const render = () => {
|
||
|
const text = input.value || samples[Math.floor(Math.random() * samples.length)];
|
||
|
const width = canvas.width;
|
||
|
const height = canvas.height;
|
||
|
|
||
|
ctx.clearRect(0, 0, width, height);
|
||
|
ctx.drawImage(templateImg, 0, 0, width, height);
|
||
|
|
||
|
ctx.font = '7px "Zelda II: The Adventure of Link"';
|
||
|
ctx.textBaseline = 'middle';
|
||
|
|
||
|
const words = text.split(' ').map(word => word.trim());
|
||
|
let lineNum = 0;
|
||
|
let charPos = 0;
|
||
|
while (words.length) {
|
||
|
let word = words.shift();
|
||
|
|
||
|
if (charPos > 0 && charPos + word.length + 1 > charsPerLine) {
|
||
|
// word goes on next line
|
||
|
charPos = 0;
|
||
|
lineNum++;
|
||
|
}
|
||
|
|
||
|
if (charPos > 0) {
|
||
|
word = ' ' + word;
|
||
|
}
|
||
|
|
||
|
const fragments = [ '' ];
|
||
|
let index = 0;
|
||
|
for (let i = 0; i < word.length; i++) {
|
||
|
if (fragments[index].length >= charsPerLine) {
|
||
|
index++;
|
||
|
fragments[index] = '';
|
||
|
}
|
||
|
|
||
|
fragments[index] += word[i];
|
||
|
}
|
||
|
|
||
|
for (const fragment of fragments) {
|
||
|
if (lineNum > maxLines) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const x = dimensions.left + (charPos * charWidth);
|
||
|
const y = dimensions.top + (lineNum * (charHeight + lineHeight));
|
||
|
|
||
|
ctx.fillStyle = colors.textShadow;
|
||
|
ctx.fillText(fragment, x + 1, y + 1);
|
||
|
|
||
|
ctx.fillStyle = colors.text;
|
||
|
ctx.fillText(fragment, x, y);
|
||
|
|
||
|
charPos += fragment.length;
|
||
|
if (charPos >= charsPerLine) {
|
||
|
charPos = 0;
|
||
|
lineNum++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.querySelector('.render img').src = canvas.toDataURL('img/png');
|
||
|
};
|
||
|
|
||
|
const input = document.querySelector('#text');
|
||
|
input.addEventListener('input', () => {
|
||
|
if (imageLoaded && fontLoaded) {
|
||
|
render();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
let fontLoaded = false;
|
||
|
|
||
|
templateImg.addEventListener('load', () => {
|
||
|
imageLoaded = true;
|
||
|
if (fontLoaded) {
|
||
|
setTimeout(render, 1);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const font = new FontFace('"Zelda II: The Adventure of Link"', `url(./font/zelda-ii-the-adventure-of-link.ttf)`);
|
||
|
font.load()
|
||
|
.then(() => {
|
||
|
fontLoaded = true;
|
||
|
if (imageLoaded) {
|
||
|
setTimeout(render, 1);
|
||
|
}
|
||
|
})
|
||
|
.catch((err) => {
|
||
|
console.error('failed to load custom font', err);
|
||
|
});
|
||
|
}());
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|