Вы можете вручную ввести transformation matrix. Это позволяет вам настроить матрицу как для чтения, так и для записи, а затем применить ее к точкам, возвращающим абсолютные точки, с новыми фактическими значениями без необходимости или хлопот для создания специального кода для каждого случая использования.
Формула (2D аффинных) с использованием матрицы 3x3 будет:
или упрощены в JavaScript:
var newX = x * a + y * c + e, // e (or tx) x 1 => e
newY = x * b + y * d + f; // f (or ty) x 1 => f
Теперь вы должны умножить матрицу с другой добавить ротацию, перевод, масштаб и т. д. Общий способ сделать это - предположим, что у нас есть объект, содержащий наши значения, вы можете сделать:
function Matrix() {
this.a = 1; // 1,0,0,1,0,0 = identity matrix (untransformed)
this.b = 0;
this.c = 0;
this.d = 1;
this.e = 0;
this.f = 0;
}
Matrix.prototype = {
// we need to be able to multiply matrices to accumulate values:
transform: function(a2, b2, c2, d2, e2, f2) {
var a1 = this.a,
b1 = this.b,
c1 = this.c,
d1 = this.d,
e1 = this.e,
f1 = this.f;
/* matrix order (canvas compatible):
* ace
* bdf
* 001
*/
this.a = a1 * a2 + c1 * b2;
this.b = b1 * a2 + d1 * b2;
this.c = a1 * c2 + c1 * d2;
this.d = b1 * c2 + d1 * d2;
this.e = a1 * e2 + c1 * f2 + e1;
this.f = b1 * e2 + d1 * f2 + f1;
}
}
С ядром созданной теперь вы можете добавить методы, чтобы сделать, например, вращения:
rotate: function(angle) {
var cos = Math.cos(angle),
sin = Math.sin(angle);
this.transform(cos, sin, -sin, cos, 0, 0);
}
шкала:
scale: function(sx, sy) {
this.transform(sx, 0, 0, sy, 0, 0);
}
перевод:
translate: function(tx, ty) {
this.transform(1, 0, 0, 1, tx, ty);
}
и т. д. в зависимости от ваших потребностей. Обратите внимание, что они будут накапливаться как с обычной матрицей canvas/SVG. Сбросьте идентификатор, чтобы удалить все преобразования.
Все, что вам нужно сделать сейчас, чтобы кормить свои очки после преобразования параметров, чтобы получить абсолютные точки - позволяет сказать, что мы имеем коробку 100x100, мы хотим, чтобы повернуть в центре:
Добавление этого к прототипу:
applyToPoint: function(p) {
return {
x: p.x * this.a + p.y * this.c + this.e,
y: p.x * this.b + p.y * this.d + this.f
}
}
позволит нам сделать:
var m = new Matrix();
m.translate(50, 50);
m.rotate(0.3);
m.translate(-50, 50);
var points = [
{x: 0, y: 0}, // upper-left
{x: 100, y: 0}, // upper-right
{x: 100, y: 100}, // bottom-right
{x: 0, y: 100} // bottom-left
],
result = [],
i = 0, p;
// transform points
while(p = points[i++]) result.push(m.applyToPoint(p));
Demo
Красная коробка оригинальные координаты, синий преобразованные точки, которые мы получили доступ к:
function Matrix() {
this.a = 1; // identity matrix
this.b = 0;
this.c = 0;
this.d = 1;
this.e = 0;
this.f = 0;
}
Matrix.prototype = {
applyToPoint: function(p) {
return {
x: p.x * this.a + p.y * this.c + this.e,
y: p.x * this.b + p.y * this.d + this.f
}
},
transform: function(a2, b2, c2, d2, e2, f2) {
var a1 = this.a,
b1 = this.b,
c1 = this.c,
d1 = this.d,
e1 = this.e,
f1 = this.f;
/* matrix order (canvas compatible):
* ace
* bdf
* 001
*/
this.a = a1 * a2 + c1 * b2;
this.b = b1 * a2 + d1 * b2;
this.c = a1 * c2 + c1 * d2;
this.d = b1 * c2 + d1 * d2;
this.e = a1 * e2 + c1 * f2 + e1;
this.f = b1 * e2 + d1 * f2 + f1;
},
rotate: function(angle) {
var cos = Math.cos(angle),
sin = Math.sin(angle);
this.transform(cos, sin, -sin, cos, 0, 0);
},
scale: function(sx, sy) {
this.transform(sx, 0, 0, sy, 0, 0);
},
translate: function(tx, ty) {
this.transform(1, 0, 0, 1, tx, ty);
}
};
// apply some transformation:
var m = new Matrix(); // our manual transformation-matrix
m.translate(50, 50); // center of box
m.rotate(0.3); // some angle in radians
m.translate(-50, -50); // translate back
var points = [
{x: 0, y: 0}, // upper-left
{x: 100, y: 0}, // upper-right
{x: 100, y: 100}, // bottom-right
{x: 0, y: 100} // bottom-left
],
result = [], i = 0, p;
// transform points
while(p = points[i++]) result.push(m.applyToPoint(p));
// draw boxes to canvas:
var ctx = document.querySelector("canvas").getContext("2d");
ctx.translate(30, 30); // give some room for rotation for this demo
drawPolygon(points, "red");
drawPolygon(result, "blue");
drawCoord(points[0]); // plot old point
drawCoord(result[0]); // plot resulting point
// Compare using ordinary canvas: -------------------
ctx.translate(150, 0); // give some space
ctx.fillText("Regular canvas:", 0, -20);
drawPolygon(points, "red");
ctx.translate(50, 50); // center of box
ctx.rotate(0.3); // some angle in radians
ctx.translate(-50, -50); // translate back
drawPolygon(points, "blue");
// plot result:
function drawPolygon(pts, color) {
ctx.beginPath();
ctx.strokeStyle = color;
ctx.moveTo(pts[0].x, pts[0].y);
for(var i = 1, p; p = pts[i++];) ctx.lineTo(p.x, p.y);
ctx.closePath();
ctx.stroke();
}
function drawCoord(p) {
ctx.fillStyle = "#0c0"; ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
ctx.fillStyle = "#000";
ctx.fillText(p.x.toFixed(1) + "," + p.y.toFixed(1), p.x, p.y - 2);
}
<canvas><canvas>
Если вы не хотите, чтобы это реализовать себя, или хотите более обширное решение, не стесняйтесь проверить мой (бесплатно) matrix solution here.
future will give us currentTransform
от контекста (в настоящее время доступны только в Chrome, Firefox борется с bug) возвращает SVGMatrix
object, который вы можете использовать в значительной степени так же, как и выше реализации.
вы меняете 'tlx', затем используя измененное значение, чтобы выяснить' tly'. то же самое для всех других значений. во всех вычислениях вам необходимо использовать значения ORIGINAL. –
Посмотрите на ответ здесь: http://stackoverflow.com/questions/5998924/how-can-i-rotate-a-shape-in-canvas-html5 Я думаю, что это будет проще и может быть сделано для нескольких фигур. Второй ответ с большим количеством голосов. –
wow, nice catch, я разработал математику на бумаге, прежде чем делать это, и я даже не подумал, что обновляю x перед вычислением нового y. – DirkDefenderJr