Реакция css-перехода не работает правильно
Я написал приложение React, используя переходы CSS. Но эти переходы работают некорректно в некоторых компонентах. В моем приложении только те компоненты, которые двигаются вверх, хорошо работают, те, кто движется вниз, мгновенно перемещаются без анимации. (Я хочу, чтобы они оба двигались с анимацией.)
Вот CSS, который я использовал там:
div.canvas {
position: absolute;
top: 90px;
left: 60px;
width: 640px;
height: 480px;
border: 1px solid #999;
background: white;
}
div.canvas-rect {
position: relative;
margin-left: 20px;
margin-top: 10px;
height: 20px;
background: green;
transition: all 1s linear;
-moz-transition: all 1s linear; /* Firefox 4 */
-webkit-transition: all 1s linear; /* Safari 和 Chrome */
-o-transition: all 1s linear; /* Opera */
}
ОБНОВЛЕНО:
Я также создал проект codepen.io, чтобы показать проблему. Он имеет полный код этого демонстрационного проекта.
Я попытался добавить запись журнала в методы componentDidUpdate
, componentDidMount
и componentWillUnmount
, чтобы показать, воссоздан или обновлен этот компонент, он показывает, что все они обновлены (не воссозданы, или удаляются) каждую секунду.
Ответы
Ответ 1
Ну, после того, как я начал зарабатывать, потому что у меня также есть эта проблема, я, наконец, нашел, что кажется проблемой.
Когда вы используете абсолютную позицию (или относительную, как в вашем случае), если вы каждый раз перерисовываете весь список, React будет переупорядочивать элементы в DOM (как вы сказали, элементы не являются воссоздан, просто обновлен). Но это создает проблему с переходами... по-видимому, если вы перемещаете элемент во время перехода, вы заканчиваете резку анимации.
Итак, для случаев, когда вы хотите использовать абсолютную позицию, ключевая концепция состоит в том, чтобы один раз отобразить контейнеры ваших элементов (в данном случае просто div) и изменить только внутреннее содержимое на основе нового порядка. Если вам нужно добавить еще несколько элементов, просто добавьте их в конец.
Я изменил код, чтобы он отражал то, что я говорю. Мой пример очень тупой, потому что я просто создал 4 ad-hoc divs, но он иллюстрирует идею: создайте столько контейнеров, сколько вам нужно, но НЕ используйте карту, которая их воссоздает каждый раз, или ваши переходы будут обрезаны.
https://codepen.io/damianmr/pen/boEmmy?editors=0110
const ArrList = ({
arr
}) => {
return (
<div style={{position: 'relative'}}>
<div className={`element element-${arr[0]} index-${arr[0]}`}>{arr[0]}</div>
<div className={`element element-${arr[1]} index-${arr[1]}`}>{arr[1]}</div>
<div className={`element element-${arr[2]} index-${arr[2]}`}>{arr[2]}</div>
<div className={`element element-${arr[3]} index-${arr[3]}`}>{arr[3]}</div>
</div>
);
}
Итак, проблема заключается в том, как вы создаете статический список контейнеров и как вы перебираете этот список, чтобы первый контейнер отображал первый элемент ваших данных, второй контейнер - второй элемент и т.д.
Надеюсь, что это помогает, эта проблема сводила меня с ума!:)
Ответ 2
Если вы удаляете элемент из виртуальной DOM, тогда реакция обновит его содержимое, поэтому вы не увидите анимацию. Что вы можете сделать, либо используйте react-transition-group ИЛИ скажите приложению, чтобы он ожидал x ms перед обновлением dom после того, как событие вызывается или использует видимость для переключения между скрытым и показом вместо полного удаления из DOM.
Ответ 3
Вы каждый раз воссоздавали элементы DOM
.
Вы должны определить значение ключевого ключа.
Я изменил значение ключа '' + el
на '' + index
.
<div key={'' + index} className={'element element-' + el + ' index-' + index} >
Просто измените только свойства css
:)
Ответ 4
Вы можете обмануть React с помощью клавиши index
в качестве. Если вы думаете о el
и index
как начальное положение (индекс) и конечную позицию (el), элемент переместился в старую конечную позицию к концу перехода, а когда она там, она перешла по новой стартовой позиции и (индекс) переключается в соответствии с новой настройкой. Это связано с тем, что когда вы устанавливаете key
в элементе, который реагирует, виртуальный DOM всегда будет интерпретировать его, поскольку он является одним и тем же элементом. И ради этого вы правы в установке индекса как "id" в целом.
Я сделал рабочий пример только путем переключения index/el (и установки позиции элемента в абсолютный).
const {combineReducers, createStore} = Redux;
const { Provider, connect } = ReactRedux;
const ArrList = ({
arr
}) => (
<div>{
arr.map((el, index)=>
<div
key={""+index}
className={`element element-${el}` + ` index-${el}`}
>
{el}
</div>) }
</div>
)
const mapStateToArrList = (state) => {
return {
arr: state.appReducer.arr
}
};
const App = connect(mapStateToArrList, null)(ArrList);
const initialState = {
arr: [1, 2, 3, 4]
}
const appReducer = (state = initialState, action) => {
switch(action.type) {
case "tick":
return {
...state,
arr: _.shuffle(state.arr)
}
default:
return state
}
}
const reducer = combineReducers({
appReducer
})
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const dispatcher = () => {
store.dispatch({
type: "tick"
})
setTimeout(dispatcher, 1000)
}
dispatcher()
.element {
position: absolute;
height: 20px;
background: green;
margin-left: 20px;
margin-top: 20px;
text-align: right;
color: white;
line-height: 20px;
transition: all 1s ease-in;
-moz-transition: all 1s ease-in; /* Firefox 4 */
-webkit-transition: all 1s ease-in; /* Safari 和 Chrome */
-o-transition: all 1s ease-in; /* Opera */
}
.element-1 {
width: 20px;
}
.element-2 {
width: 40px;
}
.element-3 {
width: 60px;
}
.element-4 {
width: 80px;
}
.index-1 {
top: 20px;
}
.index-2 {
top: 40px;
}
.index-3 {
top: 60px;
}
.index-4 {
top: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.js"></script>
<div id="root"></div>