I was reading this article which explains how to do rotation transforms in react native using MatrixMath
. I am trying to animate the scale of an object, not the rotation, and I want it to scale using an origin at the top left, rather than center, of the object. Can anyone explain how to do this?
The relevant bits of code for the rotation matrix are:
const matrix = transformUtil.rotateX(dx);
transformUtil.origin(matrix, { x: 0, y, z: 0 });
const perspective = this.props.perspective || rootDefaultProps.perspective;
ref.setNativeProps({
style: {
transform: [
{ perspective },
{ matrix },
],
},
});
and, from the transformUtil:
import MatrixMath from 'react-native/Libraries/Utilities/MatrixMath';
function transformOrigin(matrix, origin) {
const { x, y, z } = origin;
const translate = MatrixMath.createIdentityMatrix();
MatrixMath.reuseTranslate3dCommand(translate, x, y, z);
MatrixMath.multiplyInto(matrix, translate, matrix);
const untranslate = MatrixMath.createIdentityMatrix();
MatrixMath.reuseTranslate3dCommand(untranslate, -x, -y, -z);
MatrixMath.multiplyInto(matrix, matrix, untranslate);
}
function rotateX(deg) {
const rad = (Math.PI / 180) * deg;
const cos = Math.cos(rad);
const sin = Math.sitransfn(rad);
return [
1, 0, 0, 0,
0, cos, -sin, 0,
0, sin, cos, 0,
0, 0, 0, 1,
];
}
export default {
rotateX,
origin: transformOrigin,
};
Before diving into solution of the problem described, I would highly recommend for anyone reading this to learn (or brush up on) matrix multiplication. There are a few great resources out there to do it, but my personal favourite is Khan Academy.
If you prefer to just read the code, here is working solution in Snack: https://snack.expo.io/BJnDImQlr
Breakdown:
First thing we need to do is to set the transformation origin of the object that we are going to scale. We will be using part of the transformOrigin
function from the article that OP included in their question. However, we only need to modify the origin once, since there is no need to reset it back to the top (specific animation in the article required it to be returned to the top).
function transformOrigin(matrix, origin) {
const { x, y, z } = origin;
const translate = MatrixMath.createIdentityMatrix();
MatrixMath.reuseTranslate3dCommand(translate, x, y, z);
MatrixMath.multiplyInto(matrix, translate, matrix);
}
We can scale any object represented by an appropriate matrix (ie. MatrixMath.createIdentityMatrix
) using matrix multiplication. If we multiply matrix described below by target object matrix, we will end up with that same object matrix scaled by x
.
function scale(x) {
return [
x, 0, 0, 0,
0, x, 0, 0,
0, 0, x, 0,
0, 0, 0, 1
];
}
Now we need to put everything together.
ref
and its properties.xAxis: 0, yAxis: 0
).MatrixMath.multiplyInto
to process all previous steps via matrix multiplication.ref
object via setNativeProps
.
function transformScale(ref, scaleBy, width, height) {
const matrix = MatrixMath.createIdentityMatrix();
const toScale = scale(scaleBy);
transformOrigin(matrix, {
x: (width * scaleBy - width) / 2,
y: (height * scaleBy - height) / 2,
z: 0
});
MatrixMath.multiplyInto(matrix, matrix, toScale);
ref.setNativeProps({
style: { transform: [{ matrix }] }
});
}
Now, we add all of the methods described above into the React Component. If you want to increase or decrease scale of the object, change the second parameter in transformScale(this._target, 3, width, height)
. You can even go as far as setting scaleBy
to be a dynamic value and make an animation with it.
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
width: 0,
height: 0
};
}
handleBaseLayout = (e) => {
const { width, height } = e.nativeEvent.layout;
this.setState({ width, height }, () => {
transformScale(this._target, 3, width, height);
});
};
render() {
return (
<View style={styles.container}>
<View
style={styles.target}
ref={c => (this._target = c)}
onLayout={this.handleBaseLayout}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
target: {
width: 100,
height: 100,
backgroundColor: 'blue',
},
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With