Raphael.FreeTransform
Raphael.FreeTransform copied to clipboard
FT position when initializing on an object with existing transformation
Hi there
I've read previously that if you're not using sets, FT will try to automatically transform itself to match the transformation of the object it's being applied to, upon initialization.
This works great, if the existing transformation on the object only contains a translate.
However if it contains a scale and translate, the bbox appears away from the object. The distance it appears away is proportional to the amount of scale in the object's transformation.
I've spent many hours experimenting but can't quite wrap my head around what's going on. Any help on this would be greatly appreciated.
Thanks, David
Ok, I think I have a better idea of what's happening.
Say you're trying to scale an element using the x axis handle. The apply() function will build a transform string like this:
S1.5, 0, 50, 50, T0, 0
ie. Scale the shape horizontally by 1.5, using the center of the shape (50, 50) as the origin. No translate required.
The problem comes about when applying this transform to the element. It seems to want to convert things so the scale origin is 0,0 rather than the center of the element as we specified.
To do that, and retain the x,y coordinates of the element, it has to add a negative translate.
So after applying the above transformation to the element, this is the result of element.matrix.toTransformString()
t-25, 0s1.5,1,0,0 //it has reset the scale origins to 0,0 and added a negative translate to compensate
So now when I try and JSON import this and FT tries to apply this existing transformation to itself, as far as I can tell, it doesn't check the scale origins. It just grabs the translate and scale amount, and assumes that the scale needs to be done from the center of the object. The final result is that the FT box appears too far to the left as a result of the negative translate.
Any ideas?
It's been a while since I worked on this but ft.offset may be what you need. If you know the initial scale and rotation you can apply these values so FreeTransform is aware of them.
I met the same problem: http://jsfiddle.net/MvNvL/13/
I experienced the same problem or issue when trying to apply FreeTransform to elements that already had transformations applied to them before. FreeTransform tries to read the elements existing transformations and convert them into absolute values, like how many degrees is the item rotated, what percent is it scaled, and how many units is it translated. It reads this information by looking at the existing transformation array/string. The bug is that it assumes any transformations that start with the letter T relate to the number of units the item is being translated. That is not true in the cases where the item has been scaled. If you scale an item, and don't translate it at all, the transformation array/string on the item will have a value for T even though it didn't move anywhere. I think the reason is because the translation is relative to the scale, like the item gets scaled first, then needs to be shifted over so it's central point remains the same.
It took me several days to figure out how to fix this, but I eventually came up with a solution that has been working for me so far. Instead of trusting that the T in the transformation array/string applies to translation only, this code change takes the existing elements transformation in matrix form, applies the transformation to the center point, and then gets the difference of the new center point from the original center point to find the accurate number of units of translation that occurred based on the transformation.
Replace the similar code block in the current code with this. The two new lines added are commented:
// If subject is not of type set, the first item _is_ the subject
if (subject.type !== 'set')
{
ft.attrs.rotate = ft.items[0].attrs.rotate;
ft.attrs.scale = ft.items[0].attrs.scale;
ft.attrs.translate = ft.items[0].attrs.translate;
ft.items[0].attrs = {
rotate: 0,
scale: { x: 1, y: 1 },
translate: { x: 0, y: 0 }
};
ft.items[0].transformString = '';
// NEW CODE TO RESOLVE TRANSLATION ISSUE FROM PREVIOUS TRANSFORMATION
ft.attrs.translate.x = ft.items[0].el.matrix.x(ft.attrs.center.x, ft.attrs.center.y) - ft.attrs.center.x;
ft.attrs.translate.y = ft.items[0].el.matrix.y(ft.attrs.center.x, ft.attrs.center.y) - ft.attrs.center.y;
}
@jwarren1980 this works great. Would be great if you can create a PR with this
Thank you jwarren1980, your solution is spot on!