javascript - React - 显示和隐藏幻灯片动画
问题描述
我从事 React 项目已经有一段时间了。我有一块板子,上面有元素。这些元素包含图像。我在这里有一个工作版本:https ://geoboard.luukwuijster.dev/ 。这个版本只是简单的 JavaScript 和 jQuery,但它确实说明了我想要在新的 React 版本中实现的目标。当我上传图像(通过使用 CTRL + V 粘贴)时,我希望它具有动画效果。它应该滑入并滑出。在旧版本中,我只使用 jQuery 的 slideIn 和 slideOut,但我现在想知道如何在 React 中实现这一点。
我已经尝试过以下事情:
- CSS 过渡。我尝试通过将元素的高度设置为零并在高度上进行过渡来使用 CSSTransition。这不起作用,因为元素的高度是可变的,所以它应该是自动的。过渡不适用于高度:自动。
- Animate.CSS 我曾尝试在元素上放置一个 slideInDown,但由于 Animcate.CSS 使用的是 Transform,这不起作用。元素仅在上方几个像素处弹出并向下滑动。
- jQuery的slideIn和slideOut。这有点工作,但只是错误。我
ref
用来获取元素,但是当向数组添加新元素时,它会为最后一个元素而不是第一个元素设置动画。制作动画时也会出现一些故障。
这是我的代码:
Board.tsx
import React from 'react';
import { Config } from 'util/config';
import { container } from 'tsyringe';
import { AppState } from 'store';
import { connect } from 'react-redux';
import { BoardState } from 'store/board/types';
import { BoardHubService } from 'services/hubs/boardHub.service';
import { BoardElementViewModel } from 'models/BoardElementViewModel';
import { BoardViewModel } from 'models/BoardViewModel';
import { BoardElement } from './boardElement/boardElement';
import { HttpService } from 'services/http.service';
import { setActiveBoard } from 'store/board/actions';
import './board.scss'
import { mapToType } from 'helpers/helpers';
interface BoardProps {
activeBoardState: BoardState;
setActiveBoard: typeof setActiveBoard;
}
interface LocalBoardState {
boardElements: Array<BoardElementViewModel>
}
class Board extends React.Component<BoardProps, LocalBoardState> {
private config: Config;
private httpService: HttpService;
private boardHubService: BoardHubService;
constructor(props: any) {
super(props);
this.config = container.resolve(Config);
this.boardHubService = container.resolve(BoardHubService);
this.httpService = container.resolve(HttpService);
this.state = {
boardElements: []
}
}
async componentDidMount() {
// If there was any active board on page load...
if (this.props.activeBoardState.boardId) {
await this.loadBoardElements();
}
this.boardHubService.getConnection().on('SwitchedBoard', (response: BoardViewModel) => {
this.setState({
boardElements: response.elements
});
console.log('SwitchedBoard', this.props.activeBoardState);
this.updateSiteTitle();
});
this.boardHubService.getConnection().on('ReceiveElement', (response: BoardElementViewModel) => {
let elements = this.state.boardElements;
elements.unshift(response);
this.setState(() => ({
boardElements: elements
}))
});
this.boardHubService.getConnection().on('RemoveElement', (response: string) => {
let elements = this.state.boardElements;
let element = mapToType<BoardElementViewModel>(elements.find(x => x.id === response));
elements.splice(elements.indexOf(element), 1);
this.setState(() => ({
boardElements: elements
}))
});
}
/**
* Load the elements from the board that was already active on page load.
*/
private async loadBoardElements() {
await this.httpService.getWithAuthorization<Array<BoardElementViewModel>>(`/boards/${this.props.activeBoardState.boardId}/elements`)
.then((response: Array<BoardElementViewModel>) => {
this.setState({
boardElements: response
});
})
.catch((e) => console.warn(e));
}
private updateSiteTitle() {
if (this.props.activeBoardState.boardId != null) {
document.title = `${this.props.activeBoardState.name} | ${this.config.siteName}`;
}
else {
document.title = this.config.siteName;
}
}
render() {
return (
<>
{this.props.activeBoardState.boardId != null
?
<div className="board-elements">
{this.state.boardElements.map((element: BoardElementViewModel, index) => {
return (
<BoardElement
key={index}
id={element.id}
// TODO: Use number from server
number={element.elementNumber}
user={element.user}
direction={element.direction}
note={element.note}
imageId={element.imageId}
createdAt={element.createdAt}
/>
)
})}
</div>
:
<div className="select-board-instruction">
<h1>Please select or create a board.</h1>
</div>
}
</>
)
}
}
const mapStateToProps = (state: AppState) => ({
activeBoardState: state.activeBoard
});
export default connect(mapStateToProps, { setActiveBoard })(Board);
BoardElement.tsx
import React from 'react';
import { UserViewModel } from 'models/UserViewModel';
import { Direction } from 'models/Direction';
import './boardElement.scss';
import { dateToReadableString } from 'helpers/helpers';
import { Config } from 'util/config';
import { container } from 'tsyringe';
import { HttpService } from 'services/http.service';
import { CSSTransition, Transition } from 'react-transition-group';
import $ from 'jquery'
interface BoardElementProps {
id: string;
number: number;
user: UserViewModel;
// TODO: Use direction Enum
direction?: Direction;
note?: string;
imageId?: string;
createdAt: Date;
}
export class BoardElement extends React.Component<BoardElementProps> {
private config: Config;
private httpService: HttpService;
private ref: any;
constructor(props: BoardElementProps) {
super(props);
this.state = {
show: false,
height: 0
}
this.config = container.resolve(Config);
this.httpService = container.resolve(HttpService);
}
getReadableDirection(direction: Direction) {
// TODO: Richtingen vertalen
switch (direction) {
case Direction.North: return 'Noord';
case Direction.NorthEast: return 'Noordoost';
case Direction.East: return 'Oost';
case Direction.SouthEast: return 'Zuidoost';
case Direction.South: return 'Zuid';
case Direction.SouthWest: return 'Zuidwest';
case Direction.West: return 'West';
case Direction.NorthWest: return 'Noordwest';
}
}
removeElement() {
$(this.ref).slideUp(200);
setTimeout(() => {
this.httpService.deleteWithAuthorization(`/boards/elements/${this.props.id}`).then(() => {
}, (error) => {
console.warn(error);
});
}, 250);
}
componentDidMount() {
setTimeout(() => {
$(this.ref).slideDown(200);
}, 500);
}
render() {
let style: any = { display: "none" };
return (
<div ref={element => this.ref = element} style={style}>
<div className="board-element" data-element-id={this.props.id}>
<div className="board-element-header">
<span className="board-element-number">{this.props.number}</span>
<span className="board-element-creator">{this.props.user.username}</span>
<i className="fas fa-trash ml-auto delete-icon" onClick={() => this.removeElement()}></i>
</div>
<div className="board-element-body">
{this.props.imageId
? <img className="board-element-image" src={`${this.config.apiUrl}/content/${this.props.imageId}`} />
: <p className="board-element-message">{this.props.note}</p>
}
</div>
<div className="board-element-footer">
{this.props.direction &&
<div className="board-element-direction">
<i className="fas fa-location-arrow direction mr-2"></i>{this.getReadableDirection(this.props.direction)}
</div>
}
<time className="board-element-timestamp" dateTime={this.props.createdAt.toString()}>{dateToReadableString(this.props.createdAt)}</time>
</div>
</div>
</div>
)
}
}
更新:
https://gyazo.com/6dfb3db8cbd29da0f4f52af33c545421
这个剪辑可能会更好地说明我的问题。仔细观察元素角落的数字。当我粘贴新图像时,动画不会应用于新图像,而是应用于数组中的最后一个图像。
解决方案
推荐阅读
- java - 在java中为收集器添加显式导入
- time - 在使用时间从数据库中选择值时需要帮助,但没有显示
- javascript - Android WebView 禁用自定义输入的键盘
- react-native - 如何通过 id 更改值索引数组 json
- flutter - Firestore 确定日期范围之间的数据字段值性能并将其与其他值组合
- .net-core - IHostBuilder.ConfigureServices 引导程序中的简单注入器 LoggerFactory
- r - R:尝试应用lazy_dt以使用dtplyr时出现env_get_list错误
- python - 录制某个PID Ubuntu的声音
- python - “从 selenium 导入 webdriver”不能在 Apache 服务器上使用 .CGI Python,但可以在无服务器上工作
- delphi - Delphi - 7段显示器