Cumulative transformations in CSS - html

If I rotate a div having set the origin and then set the origin to something else and apply another rotation, only the second rotation is seen.
I understand that this is because that rotations are always done from the element's original position without transformations applied.
How would I perform a second rotation so that it is applied relative to the first rotation but with a new origin?

Okay I finally found a way to achieve what I wanted. You have to manually change the origin by translating and combine all transformations into one call to transform so that the translations and rotations create one transformation matrix and are therefore cumulative.
So for example if you want to rotate 30deg around 12,32 and then 20deg around 100,78 you would do:
#element{
transform: translate(-12,-32) rotate(30deg) translate(12,32) translate(-100, -78) rotate(20deg) translate(100, 78);
}

Related

CSS - Independent Translate and Scale functions?

I'm currently learning front-end and in the course that I am taking (?) , we use transform: translate(-10px, 10px) but, I recently found an independent translate property that works and acts the exact same way. Does it mean that I should stop using transform and use individual properties instead?
According to CSS documentation here:
The translate, rotate, and scale properties allow authors to specify simple transforms independently, in a way that maps to typical user interface usage, rather than having to remember the order in transform that keeps the actions of translate(), rotate() and scale() independent and acting in screen coordinates.
So when you use transform and applies several transform functions (such as translate, scale or rotate), the functions order will effect the visual (which is hard to remember how each function effects the others).
When you use individual transforms you don't have to deal with it and the order doesn't matters.
No You Should not stop using it! as Sometime you may find it Better than using Individual property, In Some Cases.
Transform Property
The transform CSS property lets you rotate, scale, skew, or translate an element.
transform: translate(120px, 50%);
transform: scale(2, 0.5);
While Simply Translate
allows you to work with horizontal and vertical direction
transform: translate(100px, 200px);
transform: translate(100px, 50%);
So, With transform you can do multiple things in one line of code like Scale, Translate ,Rotate etc.
and simply Translate allows you to work in horizontal and Vertical direction.
Priority is given more to the individual Property i.e translate Here. Overriding of property can be Done
I think transform: translate(x,y); is the best way to move anything from its position. I did not hear about direct translate attribute which works.

html canvas transform ? is it only: first do the transform, and then you draw into the canvas? Not, stick an image or etc on canvas, and transform it?

My goal is to bring in an image on to part of the canvas, then scale it, move/translate it, and optionally skew it, also rotate and make alpha changes, kind of the primary "2d image manipulations", in an animated form, which is: do little changes over time from the starting state to the target end state.
Well, I figured to be efficient, I should use the canvas/2d context transform, https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform -- as it does the first 3: scale, move/translate, and skew, "all in one." I did half that code, and now I'm looking at examples and seeking to debug it. All the examples I see, are do 1) some transform, away from the "unity transform":
{ a:1, b:0, c: 0, d:1, e:0, f:0 }; // this basic transform does nothing
and then 2) draw into that. But that's the opposite order from what I want: which is draw on the canvas (the image), and then do an animation over time using the above primary changes (scale, translate, skew, rotate, and alpha). My question is: does it only "work this way", meaning I must setup the (single) transformation on the page first, and then "draw into that?"
I hope not ... that won't give me what I want, and I have to "ditch it", and go to 5 individual "transformations." Comments?
Yes that only works this way, canvas transforms and compositing mode and filters and lineWidth and fillStyle etc. properties are only applied to the next drawing operations.
The canvas itself only holds pixels information, it has no concept of drawn object. Your js code has to do this part.
So for what you wish, you can simply redraw everything every time:
reset the transform so we can clear ctx.setTransform(1,0,0,1,0,0);
Clear the canvas ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
Set the transform to your new matrix ctx.translate(x,y); ctx.scale(s)...
Draw your transformed graphics ctx.fill(); ctx.drawImage(...
Wait next frame to do it again requestAnimationFrame(update)

CSS translate with percentage causes blurred image

I've encountered this very annoying problem.
When you align an image with transform, translate percentage based it causes the image to blur slightly. This is only with percentage alignment
Consider this css:
img {
display: block;
height: auto;
max-width: 100%;
transform: translate(1%,1%);
}
Tried solutions:
translate3d fix
perspective fix
translateZ fix
Maybe somebody has an solution?
Updated: Js Fiddle
I updated the js fiddle with an image to better see the difference. It is very noticeable in photography.
Example image:
Thanks!
Try something like this
translateX(calc(-50% + 0.5px))
Using a percentage value with Transform: translate means it positions your element with sub-pixel accuracy. You can see the pixel-value offset of your second div in the jsfiddle when you query it in the console:
This means that your browser is forced to do some less than optimal anti-aliasing to position your image, and that's what's causing the blurriness. If you could position it to whole-pixel values instead the image would remain sharp.
I haven't found an elegant solution to this annoying problem, but with javascript (sorry, I know this is tagged as a css problem) it's conceivable you could measure the element's offset values, then remove the translate and measure the non-translated pixel offset values and then calculate the difference between the two offsets (difference in offset between pre and post translate). Then rather than putting translate(%, %) back onto your element you could round the differences you calculated to the nearest whole pixel value (ie: remove sub-pixel rendering) and then reapply those values as translate (px, px) instead. This would keep your image sharp. It's a less than optimal solution, but it's the best I've been able to come up with so far.
EDIT:
Here's a quick function I wrote that will do what I'm talking about above. Once again, I apologize that this is not a CSS solution, but I see no way to fix it with CSS. This is also not a great solution in that you lose the responsiveness of % values, and it will also overwrite any Transform attributes that aren't translateX or translateY (so maybe use a wrapper div if that's a problem). Somebody could probably solve that problem by doing this with a Transform matrix, but yeah...
[EDIT 2: updated function to account for any css transitions that may be assigned to element]
function snapTranslateXYValsToNearestPixel(element){
var xTransPos = $(element).offset().left;
var yTransPos = $(element).offset().top;
// turn off any transitions (but save values first):
var transitionVal = $(element).css('transition');
$(element).css('transition', 'none');
// turn off translate:
$(element).css('transform', 'translateX(0) translateY(0)');
var xPosDiff = xTransPos - $(element).offset().left;
var yPosDiff = yTransPos - $(element).offset().top;
var xPixelVal = Math.round(xPosDiff);
var yPixelVal = Math.round(yPosDiff);
var translateVal = 'translateX(' + xPixelVal + 'px) translateY(' + yPixelVal + 'px)';
$(element).css('transform', translateVal);
// reapply transition value (wait one tick for new css value to apply first):
setTimeout(function() {
$(element).css('transition', transitionVal);
}, 1);
}
Again, not a totally ideal solution... BUT it WILL convert the translateX and translateY percentages of your element to whole pixel values and it will give you a nice crisp image.
Example usage:
snapTranslateXYValsToNearestPixel('.align-per');
I just had the same problem with an overlay which moves to the middle of the window via CSS and the content determines the height and width. The overlay also has a maximum height and width.
Besides, I got some CSS transitions on the overlay I didn't want to lose.
All CSS fixes described here did not work.
I got rid of the blur by using JavaScript to ensure that the overlay is always divisible by 2. I call this function when creating the overlay, as well as when resizing the window.
Theoretically, the overlay works without JS - but with JS it's sharper. And it do not need any timeouts to keep the transitions work.
fullPixelFix = function() {
var overlay = $('#overlay');
overlay.removeAttr('style');
var dividable_width = Math.round(overlay.outerWidth() / 2) * 2;
var dividable_height = Math.round(overlay.outerHeight() / 2) * 2;
overlay.outerWidth(dividable_width).outerHeight(dividable_height);
overlay.css(maxWidth: dividable_width, maxHeight: dividable_height);
}
I found this fix here : CSS: transform: translate(-50%, -50%) makes texts blurry
div.align-per {
transform: translate(1%, 1%) translateZ(0) ;
-webkit-transform: translateZ(0) scale(1.0, 1.0);
}
I think it's worth investigating for others browsers.

HTML5 Canvas: mixing multiple transformations

How can I zoom out an image then rotate it 30 degrees by its center, then flip vertically the rotated image, while keeping it rotated?
Actually the solution was quite simple.
It's not about math - i.e. mixing the transformation matrix - it's about js and the way canvas works.
I've actually forgot to call context.save() and context.restore() before and after (all) my transformations. So actually half of the transformations were applied, and the other half were applied on the next iteration.
So now I just do context.save , then all my transformations, then context.restore at the end. Note that transformations are cumulative, i.e. one rotation of 1 degree followed by another rotation of 1 degree results in a 2 degree rotation at the end.

HTML5 Canvas - Zooming into a Point

So I know there are threads about it already here, like that one.
I followed the idea that was proposed in the thread above, and it works. However, I don't understand WHY it works.
Here is an example:
Let's say that i have a square centered at (100, 100), and its width/height is 100. So its top-left corner will be at (50, 50).
Now let's say that i want to zoom X2 into the square's center, that is, to zoom into (100, 100). So i will write the following transformation sequence:
translate(100, 100);
scale(2, 2);
translate(-100, -100);
So because the canvas apply the transformation in reverse order, my transformed square's top-left corner will be now at (0, 0), and its height/width will be 200.
Ok, let's say that now i want to zoom X2 into the right-bottom corner of the already transformed square. So intuitively, i would like to perform the following transformation sequence:
translate(200, 200);
scale(2, 2);
translate(-200, -200);
But it wont work, because again, the canvas apply transfomations in reverse order. That is to say, that if i sum up my two transformation sequences, i'll get:
// First Sequence
translate(100, 100);
scale(2, 2);
translate(-100, -100);
// Second Sequence
translate(200, 200);
scale(2, 2);
translate(-200, -200);
This means that the second sequence will be applied to each point before the first sequence (because the canvas will apply the transformation from bottom to top), and this is wrong. So as the thread in the link above suggest the following:
Because sequence 2 will be applied first, i should transform the point (200, 200) to its original coordinates, by applying to it the inverse of the first sequence. that is to say, if T1 is the matrix that represents the first sequence, then it will look like that:
// First Sequence
translate(100, 100);
scale(2, 2);
translate(-100, -100);
// Second Sequence
var point = SVGPoint(200, 200);
var transformedPoint = point.matrixTransform(T1.inverse());
translate(-transformedPoint.x, -transformedPoint.y);
scale(2, 2);
translate(transformedPoint.x, transformedPoint.y);
But why it works? I really don't understand why it should work like that... can anyone elaborate about it?
Thanks!
The HTML5 canvas transformations happen top-down, not bottom-up as you believe. The reason for the distinction is because the transformations applied to the canvas affect the coordinate system, not your logical coordinates.
Translating by translate(100, 100) will move your coordinate system right and down, which appears hauntingly similar to moving your logical coordinate up and left.
Let's take the first sequence (I have changed your use of transform to translate):
translate(100, 100);
scale(2, 2);
translate(-100, -100);
Naturally, when we think to scale an object from it's center, we translate the object to (0,0), scale the object, then move the object back. The above code, when read in reverse, would appear to do that. However, that's not the case.
When we read the above code from top-down, it says (assume we start with an identity transform):
Move the context's (0,0) right 100 units and down 100 units. This takes it to the canvas's (100,100) location.
Make the coordinate system 2x bigger.
Move the context's (0,0) left 100 units and up 100 units, essentially returning it to it's original location (in context coordinate space, not canvas space).
The scaling happens relative to the context's (0,0) point, which is at (100,100) on the canvas.
If we were to now add your second sequence:
translate(200, 200);
scale(2, 2);
translate(-200, -200);
This will:
Move the context's (0,0) to the coordinate system's (200,200) location.
Make the coordinate system 2x bigger than it already was.
Return the context's (0,0) back to where it was previously (in context coordinate space, not canvas space).
As you've found out, that does not give you what you are expecting because (200,200) is not the point about which you want to scale. Remember, all units are relative to the context coordinate system. So we need to convert the canvas location of (200,200) to the context coordinate location of (150,150) which is the original bottom-right corner of our rectangle.
So we change sequence #2 to be:
translate(150, 150);
scale(2, 2);
translate(-150, -150);
This gives us what we are expecting (to zoom in on the bottom-right corner of the rectangle). That's why we do the inverse-transform.
In the demo application, when the app zoom's in, it's taking the coordinate in canvas units where the user's mouse was, inverse-transforming that using the context transformation thus-far, to get the location in context coordinate space that was clicked on. The context origin is moved to that location, zoomed, then returned to it's previous location.
References:
Safari HTML5 Canvas Guide: Translation, Rotation, and Scaling
You seem to be way overthinking transforms!
Here’s the simple rule:
If you apply any set of transforms, then you must undo all of them if you want to get back to your untransformed state.
Period !!!!
So let say you do these 4 transforms:
Do #1. context.translate(100,100);
Do #2. context.scale(2,2);
Do #3. context.translate(-20,50);
Do #4. context.scale(10,10);
To get back to your original untransformed state, you must undo in exactly reverse order:
Undo #4: context.scale( 0.10, 0.10 ); // since we scaled 10x, we must unscale by 0.10
Undo #3: context.translate(20,-50);
Undo #2: context.scale( 0.50, 0.50 ); // since we scaled 2x, we must unscale by 0.50
Undo #1: context.translate(-100,-100);
Think of it like walking to your friends house.
You turn Right + Left + Right.
Then to go home you must reverse that: Left + Right + Left
You must undo your walking path in exactly the reverse of your original walk.
That’s how transforms work too.
That’s Why !!