问题
I've written a React app, using CSS transitions. But those transitions does not work correctly in some of the components. In my app, only the components who are moving upwards works well, those who are moving downwards moves instantly without animation. (I want them both moves with animation.)
Here is the CSS I used there:
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 */
}
UPDATED:
I also built a codepen.io project to show the problem. It has the complete code of this demo project.
I've tried to add a log entry to componentDidUpdate
, componentDidMount
and componentWillUnmount
methods to show whether these component are re-created or updated, it shows that they are all updated (not re-created, or removed) every second.
回答1:
Well, after I started a bounty because I also have this problem I finally found what seems to be the problem.
When you are using absolute position (or relative, as in your case), if you re-render the whole list every time, React will re-order the elements in the DOM (as you said, the elements are not being recreated, just updated). But this creates the problem with the transitions... apparently, if you move an element while the transition is running then you end up cutting the animation.
So, for cases in which you want to use position absolute, the key concept is to render the containers of your elements once (in this case, just divs) and only change the inner contents based on the new order. If you need to add more elements, just add them at the end.
I modified your codepen so that it reflects what I am saying. My example is very dumb because I just created 4 ad-hoc divs, but it illustrates the idea: create as many containers as you need, but DO NOT use a map that recreates them every time, or your transitions will be cut.
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>
);
}
So, the problem is basically how you create a static list of containers and how you iterate through that list so that the first container renders the first element of your data, the second container the second element, etc.
Hope that it helps, this problem was driving me crazy too! :)
回答2:
I know this wasn't the case, but since I got here also looking for React css transition does not work correctly
, I just wanted to share:
If you create an element using arrow functions inside render
, it won't get properly animated, since a new componente is always being created.
You should create a function outside and invoke it in 'render'.
回答3:
You can trick React by using index
as key. If you think about el
, and index
as starting position (index) and end position (el), the element has moved to the old end position by the end of the transition, and by when it's there, it's taken over by the new start position and (index) is switched to match the new setup. This is because when you set key
in an element in react, the virtual DOM will always interpret it as it is the same element. And for the sake of it, you're right in setting index as the "id" in general.
I made a working example only by switching index/el (and setting element position to absolute).
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>
回答4:
If you are removing the element from the virtual DOM, then the react will update its contents, so you won't see the animations. What you can do is either use react-transition-group OR tell your app to wait x ms before updating the dom once the event is called OR use visibility to toggle between hidden and showing instead of removing it completely from the DOM.
回答5:
You did recreate DOM
elements each time.
You should define collect key value.
I changed your key value '' + el
to '' + index
.
<div key={'' + index} className={'element element-' + el + ' index-' + index} >
Just change css
properties only :)
来源:https://stackoverflow.com/questions/45671107/react-css-transition-does-not-work-correctly