Я пытаюсь использовать SharedValue из react-native-reanimated2 для хранения объекта (позиции ящика). Объект имеет следующую форму:
boxPositions.value = {
[boxId]: {x: 0, y: 0}
...
}
Моя цель — обновить объект в SharedValue, чтобы он анимировал положение определенного поля с учетом его id
.
Чтобы обновить положение блока, я делаю следующее и применяю withSpring()
к свойству y
объекта, чтобы он анимировался:
const moveRandomBox = () => {
const randId = Math.floor(Math.random() * boxes.length) + 1;
boxPositions.value = {
...boxPositions.value,
[randId]: {
x: boxPositions.value[randId].x,
y: withSpring(boxPositions.value[randId].y - 50)
}
};
}
Затем я передаю этот объект общего значения самому моему компоненту коробки, а затем создаю анимированный стиль для преобразования положения коробки:
const rStyle = useAnimatedStyle(() => {
const boxPosition = boxPositions.value[box.id] ? ? {x: 0, y: 0};
return {
transform: [
{translateX: boxPosition.x},
{translateY: boxPosition.y} // this value should be animated
]
};
}, []);
Проблема в том, что такой подход применения анимации withSpring()
приводит к тому, что поле вообще не двигается при вызове moveRandomBox()
.
Однако, если я изменю функцию moveRandomBox()
, чтобы установить позицию y
на простое значение:
[randId]: {
x: boxPositions.value[randId].x,
y: boxPositions.value[randId].y - 50 // instead of: withSpring(boxPositions.value[randId].y - 50)
}
а затем измените хук useAnimatedStyle()
, чтобы использовать метод анимации withSpring()
, он работает так, как ожидалось:
return {
transform: [
{translateX: boxPosition.x},
{translateY: withSpring(boxPosition.y)} // instead of just: boxPosition.y
]
};
В моем реальном коде я не хочу этого делать, так как у меня есть разные места, где я устанавливаю позиции x
и y
моего поля, и я хочу, чтобы анимация была другой, а не всегда одинаковой. Поэтому я хочу, чтобы анимация была установлена, когда я обновляю положение поля (как в первом подходе), а не в определении стиля (второй пример).
Мой вопрос: почему первый подход к настройке анимации не работает, а второй работает? Что не так с первым подходом, который мешает ему работать? Как мне заставить работать первый подход? Кажется, я сталкиваюсь с этой проблемой только тогда, когда SharedValue является объектом, установка SharedValue для отдельного значения withSpring()
, кажется, работает правильно.
Выполняемый пример:
Чтобы воспроизвести, измените превью на iOS. После загрузки нажмите кнопку «Переместить случайный ящик», и вы увидите, что ни один из ящиков не перемещается. Если вы измените код для вызова withSpring()
в хуке useAnimatedStyle()
, как указано выше, и удалите вызов withSpring()
из метода moveRandomBox()
, вы увидите, что коробка движется.
Вероятно, это связано с тем, что ссылка на объект остается неизменной, поэтому он не понимает изменения свойства. Я не очень разбираюсь в «ссылках», «копиях» и т. д. объектов, но в Интернете есть много сообщений, которые подробно объясняют это.
Спасибо, определение двух useSharedValues()
будет работать нормально, но я пытаюсь сделать это масштабируемым, скажем, если бы у меня было 10 ящиков или 100 ящиков. Я бы не хотел явно создавать переменную SharedValue для каждого. Я мог бы использовать цикл, но насколько я знаю, что не следует делать, если это действительно не нужно...?
В этом случае, не лучше ли было бы создать один повторно используемый компонент коробки, а затем зациклиться на том количестве, которое вы хотите?
@Kipnoedels это на самом деле то, что я делаю (вы можете видеть в примере с закусками на выставке, у меня есть компонент Box). Я не могу поместить туда вызов useSharedValue() для создания одной переменной xPos и yPos, потому что мне нужно иметь возможность контролировать положение Box вне самого компонента Box. Вот почему я вытащил логику SharedValue из самого компонента коробки и переместил ее в родительский компонент (приложение). Именно поэтому я создал объект, чтобы отслеживать положение каждой отдельной коробки.
Основываясь на вашем комментарии, вы хотите на самом деле две проблемы.
Для первой проблемы мы уже нашли решение. Вместо пружинизации всего объекта (что невозможно из-за того, как работает реанимация), вам нужно указать значения x
и y
отдельно. Я не очень разбираюсь в деталях того, как работает общее значение, но оно должно быть чем-то между двумя потоками и иметь ссылки на объекты, где дочерние изменения не распознаются из-за того, как они обрабатываются/передаются.
Ваша вторая проблема - это совершенно другая проблема, когда вы хотите обновить состояние одного компонента. В этом случае, чтобы переместить случайный ящик. Для этого вам нужно, чтобы компоненты коробки можно было использовать повторно. Так что логика их движения должна оставаться внутри самого компонента, а не доводиться до родительского состояния.
Например, при использовании ScrollView
вы не поднимаете все их состояния позиций до родительского, а вместо этого назначаете ref
, который содержит функции, которые вы хотите вызвать. Например. scrollTo
.
Это то же самое для ваших компонентов коробки. У них должно быть собственное поведение, чтобы вы могли назначить им ref
и случайным образом получить одну из этих ссылок для вызова нужной вам функции. Например. moveBoxTo()
.
Если это не то, что вы хотите, вы можете дать каждому ящику уникальный идентификатор. Что-то вроде того, что есть у вас сейчас. Поскольку ваше «случайное» движение кажется статичным (поэтому всегда делайте то же самое). Вы можете передать «id-to-move» всем ящикам и прослушать это значение, используя useAnimatedReaction
. В этом хуке вы проверяете, соответствует ли идентификатор ящика случайному идентификатору, который должен двигаться, и анимируете его положение в ожидаемую точку.
Спасибо. Использование ссылки для предоставления дочернего метода родительскому, казалось, было тем, что мне нужно (обычно я привык делать это наоборот и передавать метод от родителя дочернему). Это то, что я в итоге сделал, и я думаю, что вы имели в виду.
Хорошая работа, мне кажется!
Невозможно пружинить (или использовать любую другую анимацию) свойства объекта. Если вы хотите увеличить значение y, просто определите два
useSharedValues
. Один для вашей позиции X и один для вашей позиции Y.