Как масштабировать объект сверху слева в реагировать нативно, используя матрицы

Я читал эту статью, которая объясняет, как сделать преобразования вращения в реагировать родной, используя 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',
        },
    });

Другие вопросы по тегам