Как масштабировать объект сверху слева в реагировать нативно, используя матрицы
Я читал эту статью, которая объясняет, как сделать преобразования вращения в реагировать родной, используя MatrixMath
, Я пытаюсь анимировать масштаб объекта, а не вращение, и я хочу, чтобы он масштабировался с использованием источника в левом верхнем углу, а не в центре объекта. Кто-нибудь может объяснить, как это сделать?
Соответствующие биты кода для матрицы вращения:
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 },
],
},
});
и из 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,
};
0 ответов
Прежде чем погрузиться в решение описанной проблемы, я настоятельно рекомендую всем, кто читает это, изучить (или освежить в памяти) умножение матриц. Для этого есть несколько отличных ресурсов, но лично мне больше всего нравится Khan Academy.
Если вы предпочитаете просто читать код, вот рабочее решение в Snack: https://snack.expo.io/BJnDImQlr
Сломать:
Первое, что нам нужно сделать, это установить источник преобразования объекта, который мы собираемся масштабировать. Мы будем использовать частьtransformOrigin
функция из статьи, которую OP включил в свой вопрос. Однако нам нужно изменить источник только один раз, так как нет необходимости возвращать его в начало (конкретная анимация в статье требовала, чтобы он был возвращен в начало).
function transformOrigin(matrix, origin) {
const { x, y, z } = origin;
const translate = MatrixMath.createIdentityMatrix();
MatrixMath.reuseTranslate3dCommand(translate, x, y, z);
MatrixMath.multiplyInto(matrix, translate, matrix);
}
Мы можем масштабировать любой объект, представленный соответствующей матрицей (т.е. MatrixMath.createIdentityMatrix
) с использованием матричного умножения. Если мы умножим матрицу, описанную ниже, на матрицу целевого объекта, мы получим ту же матрицу объекта, масштабированную наx
.
function scale(x) {
return [
x, 0, 0, 0,
0, x, 0, 0,
0, 0, x, 0,
0, 0, 0, 1
];
}
Теперь нам нужно собрать все вместе.
- Передать интересующий объект
ref
и его свойства. - Создайте матрицу идентичности для этого объекта.
- Увеличьте масштаб этого объекта умножением матриц.
- Переместите начало координат объекта в желаемое левое верхнее положение (
xAxis: 0, yAxis: 0
). - Использовать
MatrixMath.multiplyInto
для обработки всех предыдущих шагов посредством умножения матриц. - Применить преобразование к цели
ref
объект через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 }] }
});
}
Теперь мы добавляем все методы, описанные выше, в компонент React. Если вы хотите увеличить или уменьшить масштаб объекта, измените второй параметр вtransformScale(this._target, 3, width, height)
. Можно даже дойти до настройкиscaleBy
быть динамическим значением и сделать с ним анимацию.
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',
},
});