Bart Chalkboard Generator

в 22:36, , рубрики: canvas, html, html5, html5 canvas, images, javascript, Веб-разработка, метки: , , , ,

Здравствуйте.
Мне бы хотелось рассказать про мой «Bart Chalkboard Generator».image
Я уверен, что большинство из вас знают сериал «The Simpsons», и вы помните что почти в каждой новой заставке Барт писал на школьной доске что-то новое, типа: «They are laughing at me, not with me». А в интернете часто всплывает картинка с текстом: «I will use google before asking bump questions». И вот однажды я подумал, почему бы не создать простой генератор подобных картинок, да ещё и на Html5, что бы попрактиковаться?

Подготовка

Идея состояла в том что бы создать Canvas приложение которое бы отрисовывало заданный текст на шаблоне и давало возможность сохранить результат.
Я начал с создания спрайта в Фотошопе. За основу я взял картинку из интернета, разделил её на доску и на барта.
Bart Chalkboard Generator
Схема отрисовки следующая:

  • нарисовать доску
  • нарисовать тектс
  • нарисовать Барта поверх текста

Bart Chalkboard Generator

Лирическое отступление

К моменту начала работы над этим мини-проектом я был знаком с js всего один день (я побывал на «Html5 games hackaton» от 2niversity.com). Я давно хотел начать изучать JS, но как-то не находилось идеи.

Html5 & Js

Код буду публиковать не полностью, ибо нет в этом смысла, исходники доступны.

Ну с Html все просто:

<canvas id='bart' width='500' height='310'></canvas>

В js я начал с определения некоторых переменных

var canvas = document.getElementById('bart');  //Определяю canvas
var ctx = canvas.getContext('2d'); //Определяю контехт canvas
var sprite = new Image(); 
sprite.src = 'images/sprite.png'; //Определяю спрайт

Дальше я создал основную функцию draw(text);
Которая задаёт функцию write(text,n,marginStep) и присвавает контексту (ctx) необходимые параметры

function draw(text){
	marginTop = 36; //Определяю отступ сверху
	line = ''; 		// Сбрасываю line
	function write(text, n, marginStep){ // Эта функция напишет text по центру доски  n раз с шагом по вертикали marginStep
		for (var i = 0; i < n; i++) {
		ctx.fillText(text, 250, marginTop);
		marginTop += marginStep;
		};
	};
	line = '';
	ctx.fillStyle = '#fff';
	ctx.textAlign = 'center';
	ctx.font =  '18px Flow';

Потом вычисляется длинна текста при заданных параметрах

textWidth = ctx.measureText(text).width;

Затем отрисовывается доска

ctx.drawImage(sprite, 0, 0, 500, 250, 0, 0, 500, 250);

После этого есть несколько вариантов развития событий. В зависимости от ширины введённого тескта скрипт настраивает размер шрифта оптимальным образом и рисует его в десять строк, или же, если текст совсем большой, рисует его по две строки 5 раз.

if (textWidth > 0 && textWidth <= 220){		//Вывод для 0-220 тескта (tc раз на строку)
		textCount = Math.floor(450/(textWidth + 5));		// Узнаю сколько раз на строке поместится фраза( ширину холста делю на ширину(фразы+пробел))
		for (var tc = 0; tc < textCount; tc++){	// Формирую строку
		line += text + ' '; 
		};
		line = line.slice(0, -1);	// Убираю пробел в конце
		write(line, 10, 20);	//Пишу
	}else if(textWidth > 220 && textWidth <= 225){	//Вывод для 221-225 текста (2 раза внутри строки)
		ctx.font = '15px Flow';	//Уменьшаю шрифт
		line = text + ' ' + text;	//Формирую строку
		write(line, 10, 20);	//Пишу
	}else if(textWidth > 225 && textWidth <= 360 ){		//Вывод для 226-360 текста (1 раз на строку)
		ctx.font = '20px Flow';	//Увеличиваю шрифт
		if (textWidth > 445){	//Проверяю не стал ли текст шире чем нужно, если так, то уменьшаю шрифт до стандартного
				ctx.font = '18px Flow';
				textWidth = ctx.measureText(text).width;
			};
		write(text, 10, 20);		//Пишу
	}else if(textWidth > 360 && textWidth <= 450 ){	//Вывод для 361-450 текста, самый простой (1 раз на строку стандартным шрифтом)
		write(text, 10, 20);		//Соответственно сразу пишу
	}else if(textWidth > 450 && textWidth <= 500){	//Вывод 451-500 текста (1 раз на строку) 
		ctx.font = '15px Flow';	//Уменьшаю шрифт
		write(text, 10, 20);
	}

Дальше идёт вывод двухстрочного текста который я взял у naxel, здесь, спасибо ему за это.
Способ состоит в том, что текст сначала разбивается по словам в массив words, потом склеивается обратно по одному слову с пробелом и после каждой склейки проверяется ширина получившейся строки.
Если ширина больше 450(ширина доски), то в canvas выводится эта строчка без последнего добавленного слова 5 раз с отступом 40. Далее просто доклеивается вторая строчка и так же выводится 5 раз.

else if(textWidth > 500 && textWidth <= 700){	//Вывод 501-700 текста 2строчный! 
		var words = text.split(' ');		//Разбиваю текст на слова и зпихиваю их в массив words
		var countWords = words.length;	//Считаю количество слов
		if(countWords > 4){  
			for (var n = 0; n < countWords; n++) {	//Этот цикл будет вращатся countWords раз и в результате отдаст на доску 1 строчку, а в line положит вторую
				var testLine = line + words[n] + ' ';			//Создаю тестовую строку
				var testWidth = ctx.measureText(testLine).width;//Узнаю и проверяю её ширину
				if (testWidth > 450) {							//Если ширина тестовой строки уже больше чем доска
					write(line, 5, 40);	//Пишу 5 раз с шагом 40 (через строчку) начиная с 36
					line = words[n] + ' ';  //Сбрасываю line до последнего перебранного слова
				}else {
					line = testLine;
				};
			};
			marginTop = 56;	//Устанавливаю отступ на 2 строчку
			write(line, 5, 40);	//Пишу вторую строчку фразы 5 раз с шагом 40(через строчку) начиная со второй строки доски
		}

В конце рисуется Барт.

	ctx.drawImage(sprite, 498, 128, 80, 180, 406, 118, 80, 180); //Рисую Барта повер всего этого безобразия

Вот собственно и всё, остается прикрутить текстовую форму и кнопку.
Html

<form name='form_1' onsubmit='return false;' id='form_1'>
<input name='inputText' id='inputText' type='text' maxlength='150' autocomplete='off' autofocus placeholder='Напиши сюда что-нибудь, скорее!'>
<input id='writeIt' type='button' value='Написать!'>
<input id='downloadIt' type='button' class='center' value='Скачать!' >
</form>

Js

document.getElementById('writeIt').onclick=function(e){draw(document.forms.form_1.elements.inputText.value)};
document.getElementById('form_1').onsubmit=function(e){ //Нажатие Enter
	draw(document.forms.form_1.elements.inputText.value);
	return false	//Отменяю действие браузера
};

Сохранение картинки
Для сохранения картинки я воспользовался toDataURL('image/png'). Просто открываю новое окно с url canvas.toDataURL('image/png')

document.getElementById('downloadIt').onclick=function(e){
	ctx.drawImage(sprite, 9, 250, 142, 25, 17, 222, 142, 25); //Рисую копирайт
	window.open(canvas.toDataURL("image/png"), "Скачать Bart's Chalkoard", "resizable=no,toolbar=no,menubar=no,location=no,scrollbars=no,status=no,width=530,height=340, left=400, top=120,");	//Задаю параметры всплывающего окна.
	ctx.drawImage(sprite, 16, 226, 134, 18, 16, 226, 134, 18); //Затираю копирайт кусочком доски
};

Заключение

На всё это у меня ушло примерно две ночи и да дня, знаю что для нормального программиста это очень долго, но я только начал учить js, так что я собой почти доволен.

Извинения и оправдания.

Извините, если в коде есть какие промахи или недочёты. Я правда старался всё сделать красиво и правильно.
Извините, если что-то не так в оформление поста, опять же это мой первый пост, я старался.

Демо

Опробовать можно сдесь bart.grindel.su, исходники там же.
Грузится не очень быстро т.к. размещено на моём домашнем сервере который выходит в интернет через очень странный роутер, так что придётся подождать.

Источники

Надеюсь кому-нибудь было интересно это прочитать.
Спасибо за внимание!
Bart Chalkboard Generator

Автор: grindel


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js