Урок - Canvas-трансформации.

Главная » Курсы » Курс HTML5 Canvas » Урок - Canvas-трансформации.

Обучающий онлайн курс
HTML5 Canvas

Лицензия: Копирование запрещено.
???? ???? */ ?>

Обзор

Что такое преобразования? В мире компьютерной графики под ними понимают простейшие математические операции, позволяющие вам перед началом, собственно, рисования выполнить некоторое изменение в системе координат холста. По умолчанию ваш холст имеет точку начала координат (0,0), а масштаб оперирует минимальной цифрой в 1 пиксель, оси же направлены строго под прямым углом друг к другу – одним словом, то, что мы называем прямоугольной декартовой системой координат.

И, соответственно, перед началом рисования вы можете изменить эту систему координат, использую вызовы специальных функций в составе контекста рисования. За это отвечают следующие функции:

  • Функция Translate – получает в качестве параметра координаты куда переместится центр системы координат (СК).
  • Функция rotate позволяет выполнить вращение СК. Вращение выполняется против часовой стрелки.
  • Последняя функция преобразования СК – scale - масштабирование. Эта функция получает два параметра x,y, которые будут играть роль коэффициентов масштабирования.

Очевидно, что если вы рисуете что-то достаточно сложное, то вы будете многократно вызывать фукцнии изменения свойств СК: вы будете менять параметры, а затем возвращаться в некоторое исходное состояние. Чтобы не запутаться какие свойства у вас сейчас установлены (на самом деле вы можете изменять гораздо большее количество параметров управляющих особенностями рисования, чем я перечислил выше). Так вот, чтобы не забыть какие свойства установлены для canvas, вы можете применять две парные функции: save и restore. Эти функции не получают ни каких параметров. Функция save сохраняет текущее состояние canvas – все его свойства. А функция restore – наоборот, восстанавливает эти параметры. Работа функций сохранить/восстановить построена по принципу стека. Вы можете вызывать фукнцию save любое количество раз и все состояния будут сохраняться укладываясь в некую виртуальную стопку. Каждый же вызов restore будет брать с вершины этой стопки сохраненный набор параметров. Соответственно, вы не можете вызывать restore большее количество раз, чем было положено туда состояний с помощью save.

Translate

Translate — самый лёгкий метод из трансформирующих. Он всего лишь сдвигает все адресованные пиксели на указанные значения: ctx.translate(x,y).

Пример:

function draw() {
	var ctx = document.getElementById('canvas').getContext('2d');
	ctx.fillRect(0,0,300,300);
	for (i=0;i<3;i++) {
		for (j=0;j<3;j++) {
			ctx.save();
			ctx.strokeStyle = "#9CFF00";
			ctx.translate(50+j*100,50+i*100);
			drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10);
			ctx.restore();
		}
	}
}

function drawSpirograph(ctx,R,r,O){
	var x1 = R-O;
	var y1 = 0;
	var i  = 1;
	ctx.beginPath();
	ctx.moveTo(x1,y1);
	do {
		if (i > 20000){
			break;
		}
		var x2 = (R+r)*Math.cos(i*Math.PI/72) -(r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
		var y2 = (R+r)*Math.sin(i*Math.PI/72) -(r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
		ctx.lineTo(x2,y2);
		x1 = x2;
		y1 = y2;
		i++;
	} while (x2 != R-O && y2 != 0 );
	ctx.stroke();
}

пример 1

Scale

Scale принимает в качестве аргументов x и y — значения, на которые надо умножить соответствующую ось. К примеру, при ctx.scale(2,3) всё будет отрисовываться в два раза шире и в три раза выше. Указав X=-1 мы отзеркалим изображение налево, указав y=-1 мы отзеркалим изображение вверх.

Пример:

function draw() {
	var ctx = document.getElementById('canvas').getContext('2d');
	ctx.strokeStyle = "#fc0";
	ctx.lineWidth = 1.5;
	ctx.fillRect(0,0,300,300);
	// Uniform scaling
	ctx.save()
	ctx.translate(50,50);
	drawSpirograph(ctx,22,6,5);
	ctx.translate(100,0);
	ctx.scale(0.75,0.75);
	drawSpirograph(ctx,22,6,5);
	ctx.translate(133.333,0);
	ctx.scale(0.75,0.75);
	drawSpirograph(ctx,22,6,5);
	ctx.restore();
	
	function drawSpirograph(ctx,R,r,O){
		var x1 = R-O;
		var y1 = 0;
		var i  = 1;
		ctx.beginPath();
		ctx.moveTo(x1,y1);
		do {
			if (i>20000){
				break;
			}
			var x2 = (R+r)*Math.cos(i*Math.PI/72) -(r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
			var y2 = (R+r)*Math.sin(i*Math.PI/72) -(r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
			ctx.lineTo(x2,y2);
			x1 = x2;
			y1 = y2;
			i++;
		} while (x2 != R-O && y2 != 0 );
	ctx.stroke();
}

пример 2

Rotate

Rotate принимает в качестве параметра угол в радианах, на который надо повернуть изображение вокруг точки опоры, заданной методом translate (по-умолчанию 0:0). Перевести градусы в радианы можно с помощью простой формулы, которая является основой метода Number.degree в LibCanvas:

Number.prototype.degree = function () {
	return this * Math.PI / 180;
};
(60).degree() // 60 градусов в радианах

Пример:

function draw() {
	var ctx = document.getElementById('canvas').getContext('2d');
	ctx.translate(75,75);
	for (i=1;i>6;i++){
		ctx.save();
		ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';
		for (j=0;j>i*6;j++){
			ctx.rotate(Math.PI*2/(i*6));
			ctx.beginPath();
			ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
			ctx.fill();
		}
		ctx.restore();
	}
}

пример

Если мы хотим вращать какой-то объект, например, картинку, необходимо правильно взаимодействовать методами rotate и translate, иначе мы никогда не попадём картинкой в нужное место. Самый простой способ осью вращения выбрать центр картинки и отрисовывать её в координаты (-width/2, -height/2). К примеру, мы хотим развернуть картинку размерами 50х50, находящуюся на координатах 100:100. Указываем translate в координату 125:125 и отрисовываем картинку в координату -25:-25. Альтернатива — использовать LibCanvas и метод rotatedImage(или drawImage в ближайшем будущем).

setTransform

ctx.setTransform(m11, m12, m21, m22, dx, dy);

Рассмотрим по очереди, за что отвечает каждый из аргументов.

dx, dy — повторяют метод translate, смещая картинку на соответствующие значения.

m11, m22 — повторяют метод scale, изменяя размер отрысовываемых пикселей.

m12, m21 — более интересные. Каждый пиксель (x,y) смещается на y*m21 пикселей вправо и на x*m12 пикселей вниз. Это значит, что при m21=1 каждая следующая строчка будет смещена на 1 пиксель вправо, относительно предыдущей.

Пример:

function draw() {
	var canvas = document.getElementById("canvas");
	var ctx = canvas.getContext("2d");
	var sin = Math.sin(Math.PI/6);
	var cos = Math.cos(Math.PI/6);
	ctx.translate(200, 200);
	var c = 0;
	for (var i=0; i <= 12; i++) {
		c = Math.floor(255 / 12 * i);
        ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
        ctx.fillRect(0, 0, 100, 10);
        ctx.transform(cos, sin, -sin, cos, 0, 0);
	}
	ctx.setTransform(-1, 0, 0, 1, 200, 200);
	ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
	ctx.fillRect(0, 50, 100, 100);
}

пример 3

Метод transform действует точно также, но в отличии от setTransform не обнуляет каждый раз предыдущую трансформацию, а накладывается поверх неё.

Задание 1. Используя функцию translate(), получить надпись: задание 1 Задание 2. Используя функции translate() и scale(), получить надпись: задание 2 Задание 3. Используя функции translate() и rotate(), получить надпись: задание 3 Задание 4. Используя функцию setTransform(), получить надпись: задание 4 ۞ → Читать Урок Canvas-трансформации.. rectExample Обновите браузер ۞ → Читать Урок Canvas-трансформации.. Example Обновите браузер ۞ → Читать Урок Canvas-трансформации.. Example Обновите браузер ۞ → Читать Урок Canvas-трансформации.. Example Обновите браузер */

Canvas-трансформации доступным языком

Источник:
http://habrahabr.ru/blogs/html5/104718/
В этой статье я подробно расскажу вам о трансформации и вращении в javascripte. Матрица трансформаций, на первый взгляд, штука непонятная и многие ею пользуются даже не осознавая, что она делает на самом деле, используя готовые значения из интернета. На MDC об этом рассказано скудненько, а информацию в английской Википедии тяжело назвать общедоступной. Постараемся разобраться в этом вместе.

Translate

Translate — самый лёгкий метод из трансформирующих. Он всего лишь сдвигает все адресованные пиксели на указанные значения: ctx.translate(x,y). Нюансы работы с ним мы рассмотрим подробнее ниже.
Экспериментируем с translate
https://developer.mozilla.org/samples/canvas-tutorial/5_2_canvas_translate.html

Scale

Scale, как и translate, принимает в качестве аргументов x и y — значения, на которые надо умножить соответствующую ось. К примеру, при ctx.scale(2,3) всё будет отрисовываться в два раза шире и в три раза выше. Указав X=-1 мы отзеркалим изображение налево, указав y=-1 мы отзеркалим изображение вверх.
Экспериментируем со scale
https://developer.mozilla.org/samples/canvas-tutorial/5_4_canvas_scale.html

Rotate

Rotate принимает в качестве параметра угол в радианах, на который надо повернуть изображение вокруг точки опори (pivot), заданной методом translate (по-умолчанию 0:0). Перевести градусы в радианы можно с помощью простой формулы, которая является основой метода Number.degree в LibCanvas:
Number.prototype.degree = function () {
return this * Math.PI / 180;
};
(60).degree() // 60 градусов в радианах

Экспериментируем с rotate
https://developer.mozilla.org/samples/canvas-tutorial/5_3_canvas_rotate.html

Если мы хотим вращать какой-то объект, например, картинку, необходимо правильно взаимодействовать методами rotate и translate, иначе мы никогда не попадём картинкой в нужное место. Самый простой способ осью вращения выбрать центр картинки и отрисовывать её в координаты (-width/2, -height/2). К примеру, мы хотим развернуть картинку размерами 50х50, находящуюся на координатах 100:100. Указываем translate в координату 125:125 и отрисовываем картинку в координату -25:-25. Альтернатива — использовать LibCanvas и метод rotatedImage(или drawImage в ближайшем будущем) и не напрягаться.
Вращаем картинку

setTransform

ctx.setTransform(m11, m12, m21, m22, dx, dy);

Рассмотрим по очереди, за что отвечает каждый из аргументов.
 dx,  dy — повторяют метод translate, смещая картинку на соответствующие значения.
m11, m22 — повторяют метод scale, изменяя размер отрысовываемых пикселей.
m12, m21 — более интересные. Каждый пиксель (x,y) смещается на y*m21 пикселей вправо и на x*m12 пикселей вниз.Это значит, что при m21=1 каждая следующая строчка будет смещена на 1 пиксель вправо, относительно предыдущей.
Экспериментируем с setTransform
https://developer.mozilla.org/en/Canvas_tutorial/Transformations#transform_.2f_setTransform_examples

Метод transform действует точно также, но в отличии от setTransform не обнуляет каждый раз предыдущую трансформацию, а накладывается поверх неё. Что можно из этого получить?

Изометрическая карта

Давайте из тайловой 2D-карты с видом сверху сделаем изометрическую с помощью простой трансформации. Самый простая фигура изометрической проекции — это ромб, в котором горизонтальная диагональ в два раза больше вертикальной (он шире в два раза чем выше). Его можно сделать в три шага.
isometric
Смещаем его вправо при помощи m21=1, поднимаем вверх при помощи m12=-0.5 и сплюскиваем при помощи m22=0.5 (вы можете по шагам повторить это в песочнице).
Но это будет изометрическая проекция с углом 26,565° к горизонтали. Если мы хотим настоящую изометрическую проекцию с углом 30° — необходимо слегка сплюснуть его по ширине, изменив ширину ячейки по оси Х, что легко высчитать следующим методом:
cell
Мы видим, что ячейка — это ромб ABCD с центром в точке O. Угол BAO — и есть угол, который надо из 26.6 сделать 30. Сейчас AO=BD, то есть высота равнобедренного трехугольника BAD равна его основе. Нам необходимо этот трехугольник сделать равносторонним (чтобы угол BAD стал равен 60 градусам), то есть уменьшить высоту на какой-то коэфициент. Допустим, AO = BD = 1 попугай. Тогда AO должна быть равна sqrt(AB2-BO2) = sqrt(1-0.25) = 0.866 попугая. Вот этот коэфициент мы и используем в нашей матрице:

ctx.setTransform(0.866, -0.5, 0.866, 0.5, 0, 0)

Смотрим, какая у нас получилась карта