In HTML5 I want to implement transform functionality to canvas element, so user can translate
(move), scale
(zoom in/out) and rotate
the canvas element. Each such tranformation can be done with different transform-origin point.
First tranformation is easy:
function transform(el, value, origin) {
el.style.Transform = value;
el.style.MozTransform = value;
el.style.msTransform = value;
el.style.OTransform = value;
el.style.webkitTransform = value;
el.style.TransformOrigin = origin;
el.style.MozTransformOrigin = origin;
el.style.msTransformOrigin = origin;
el.style.OTransformOrigin = origin;
el.style.webkitTransformOrigin = origin;
}
transform(myCanvas, 'translate('+ dx +'px, ' + dy + 'px) ' +
'scale(' + zoom + ', ' + zoom + ') ' +
'rotate(' + angle + 'deg)',
cx + 'px ' + cy + 'px');
User will move or zoom or rotate the element, not everything at once, so some parameters of tranformation will stay default (dx = 0, dy = 0, zoom = 1, angle = 0).
After such tranformation, if user wants to make another transformation (and antother, and another...), how can I bine (dx1, dy1, zoom1, angle1, cx1, cy1) with (dx2, dy2, zoom2, angle2, cx2, cy2) to get final values that can be bine later with new tranformation parameters? I cannot append another tranformation to transform
parameter, because tranform-origin
may be different. Is there a formula how to bine tranformations with different transform-origin points?
In HTML5 I want to implement transform functionality to canvas element, so user can translate
(move), scale
(zoom in/out) and rotate
the canvas element. Each such tranformation can be done with different transform-origin point.
First tranformation is easy:
function transform(el, value, origin) {
el.style.Transform = value;
el.style.MozTransform = value;
el.style.msTransform = value;
el.style.OTransform = value;
el.style.webkitTransform = value;
el.style.TransformOrigin = origin;
el.style.MozTransformOrigin = origin;
el.style.msTransformOrigin = origin;
el.style.OTransformOrigin = origin;
el.style.webkitTransformOrigin = origin;
}
transform(myCanvas, 'translate('+ dx +'px, ' + dy + 'px) ' +
'scale(' + zoom + ', ' + zoom + ') ' +
'rotate(' + angle + 'deg)',
cx + 'px ' + cy + 'px');
User will move or zoom or rotate the element, not everything at once, so some parameters of tranformation will stay default (dx = 0, dy = 0, zoom = 1, angle = 0).
After such tranformation, if user wants to make another transformation (and antother, and another...), how can I bine (dx1, dy1, zoom1, angle1, cx1, cy1) with (dx2, dy2, zoom2, angle2, cx2, cy2) to get final values that can be bine later with new tranformation parameters? I cannot append another tranformation to transform
parameter, because tranform-origin
may be different. Is there a formula how to bine tranformations with different transform-origin points?
- Have you tried to visualize it? Draw a box on a paper. Then, consider what happens when you want to translate/rotate/scale the box. And again. – Rob W Commented Jul 14, 2012 at 21:52
- @RobW - I tried a lot for over a week :-/ I am able to bine unlimited move (translate) and zoom (scale) tranformations, but once I have rotation involved, I am lost... – Ωmega Commented Jul 14, 2012 at 22:00
- I'm not sure the linear-algebra tag belongs on this question. It's more about bining CSS properties than using linear algebra. – TylerH Commented Jun 7, 2015 at 18:15
2 Answers
Reset to default 9You don't have to learn matrix math. According to the CSS Transform specification
- Start with the identity matrix.
- Translate by the puted X, Y and Z values of ‘transform-origin’.
- Multiply by each of the transform functions in ‘transform’ property in turn
- Translate by the negated puted X, Y and Z values of ‘transform-origin’
In other words, transform-origin: A; transform: B
is the same as transform: translate(-A) B translate(A)
. (Transformations apply from right to left, so the first thing you want to happen goes at the end.)
So use the above rules to eliminate transform-origin
and now you just have plain transforms you can concatenate.
Example:
transform-origin: 5px 5px; transform: translate(10px, 40px)
transform-origin: 25px 30px; transform: scale(2)
transform-origin: 10px 10px; transform: rotate(30deg)
bees
transform: translate(-5px, -5px) translate(10px, 40px) translate(5px, 5px)
transform: translate(-25px, -30px) scale(2) translate(25px, 30px)
transform: translate(-10px, -10px) rotate(30deg) translate(10px, 10px)
Now you can bine them since they all agree on the origin (i.e., no origin)
transform: translate(-5px, -5px) translate(10px, 40px) translate(5px, 5px) translate(-25px, -30px) scale(2) translate(25px, 30px) translate(-10px, -10px) rotate(30deg) translate(10px, 10px)
Of course you can collapse the consecutive translations if you want
transform: translate(-15px, 10px) scale(2) translate(15px, 20px) rotate(30deg) translate(10px, 10px)
Or you can dig out your math textbook and pute the final transformation matrix.
Edit: Transforms apply right to left.
You'd have to deal with matrix transformations.
Every linear operation can be represented with a 3x3 matrix and a 3x1 vector that you can apply on the point of the plane. If p is a point, M the matrix and q the other vectory, every linear transformation can be represented as Mp + q.
If you have a 2d point, then its vector will be [x; y; 1] (a vertical vector), while the matrix can be of several form.
For translations, the matrix is just the identity matrix. The vector q is the translation vector.
For scaling, M is like
[a 0 0]
M = [0 b 0]
[0 0 1]
where a and b are scaling factor for x and y respectively. The vector q is null.
For rotations, say of an angle a, you'll get
[cos(a) -sin(a) 0]
M = [sin(a) cos(a) 0]
[ 0 0 1]
and q is null again.
There are matrices for skewing, too. So, if you have to apply three consecutive transformations, you'll have to apply these kind of linear transformations. Your problem is that you have to deal with origins, too, so you'll have to subtract the vector o from p, than apply M, add q and then o again.
Let's say you have these transformation (M1, q1, o1) and (M2, q2, o2). When you apply the first one you get
p1 = M1 * (p - o1) + q1 + o1
Then you have to apply the second transformation:
p2 = M2 * (p1 - o2) + q2 + o2
In the end you'll get:
p2 = M2 * (M1 * (p - o1) + q1 + o1 - o2) + q2 + o2
And so on with and eventual third (M3, q3, o3).
A mess? It looks like. But things can be simplyfied a bit if you know how the matrices look like.
Now, do the math, and apply it to transform: matrix(a, b, c, d, tx, ty)
.