admin管理员组文章数量:1341459
This is my first question on StackOverflow. I want to build a little game with React, where users can drag and drop tetrominos onto a grid and also reposition or rotating them to their liking. The tetrominos are represented by a matrix and then every block gets rendered in a li element.
Example for the z-tetromino: [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0]
Unfortunately I cannot post images yet, that would make things easier.
The grid is too respresented by a matrix.
Now what I want to do is basically drag and drop these block matrices onto the grid, so that the values in the grid change accordingly (0 → 1 etc.).
The problem is, I have no clue how to drag multiple li elements at once with the standard HTML5 DnD API or with React DnD. When the user clicks on one li element of a certain tetromino, the whole piece should move. Maybe I could solve this using jQuery UI, but since in React no direct DOM manipulation is allowed, I'm left wondering how to do it.
I tried to drag one block onto the grid which worked semi optimally, because one block took the place of an entire row of grid blocks, even with display: inline-block set in CSS.
Here is some simple code from the first experiment.
onDragStart = e => {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', e.target.id);
// e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
};
handleDrop = e => {
const pieceOrder = e.dataTransfer.getData('text');
// console.log(document.getElementById(pieceOrder));
// e.target.appendChild(document.getElementById(pieceOrder));
// console.log(pieceOrder);
e.target.replaceWith(document.getElementById(pieceOrder));
e.target.remove();
};
renderEmptyBoardCell(i) {
return (
<li key={i} className="emptyBoardCell" onDragOver={(e) => e.preventDefault()} onDrop={(e) => this.handleDrop(e)}>></li>
);
}
renderTemplateBoardCell(i) {
return (
<li key={i} className="templateBoardCell" onDragOver={(e) => e.preventDefault()} onDrop={(e) => this.handleDrop(e)}>></li>
);
}
renderEmptyCell(i) {
return (
<li key={i} className="emptyCell"></li>
);
}
renderFilledCell(piece_config, i) {
return (
<li key={i} id={i} className={`filledCell ${piece_config}`} draggable onDragStart={this.onDragStart}></li>
);
}
So the question is, would that be theoretically possible with React DnD or any other library? If yes, what would be the approximate solution to DnD multiple elements at once.
Thanks for your time!
This is my first question on StackOverflow. I want to build a little game with React, where users can drag and drop tetrominos onto a grid and also reposition or rotating them to their liking. The tetrominos are represented by a matrix and then every block gets rendered in a li element.
Example for the z-tetromino: [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0]
Unfortunately I cannot post images yet, that would make things easier.
The grid is too respresented by a matrix.
Now what I want to do is basically drag and drop these block matrices onto the grid, so that the values in the grid change accordingly (0 → 1 etc.).
The problem is, I have no clue how to drag multiple li elements at once with the standard HTML5 DnD API or with React DnD. When the user clicks on one li element of a certain tetromino, the whole piece should move. Maybe I could solve this using jQuery UI, but since in React no direct DOM manipulation is allowed, I'm left wondering how to do it.
I tried to drag one block onto the grid which worked semi optimally, because one block took the place of an entire row of grid blocks, even with display: inline-block set in CSS.
Here is some simple code from the first experiment.
onDragStart = e => {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', e.target.id);
// e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
};
handleDrop = e => {
const pieceOrder = e.dataTransfer.getData('text');
// console.log(document.getElementById(pieceOrder));
// e.target.appendChild(document.getElementById(pieceOrder));
// console.log(pieceOrder);
e.target.replaceWith(document.getElementById(pieceOrder));
e.target.remove();
};
renderEmptyBoardCell(i) {
return (
<li key={i} className="emptyBoardCell" onDragOver={(e) => e.preventDefault()} onDrop={(e) => this.handleDrop(e)}>></li>
);
}
renderTemplateBoardCell(i) {
return (
<li key={i} className="templateBoardCell" onDragOver={(e) => e.preventDefault()} onDrop={(e) => this.handleDrop(e)}>></li>
);
}
renderEmptyCell(i) {
return (
<li key={i} className="emptyCell"></li>
);
}
renderFilledCell(piece_config, i) {
return (
<li key={i} id={i} className={`filledCell ${piece_config}`} draggable onDragStart={this.onDragStart}></li>
);
}
So the question is, would that be theoretically possible with React DnD or any other library? If yes, what would be the approximate solution to DnD multiple elements at once.
Thanks for your time!
Share asked Apr 18, 2019 at 17:03 FallcFallc 1051 gold badge1 silver badge8 bronze badges5 Answers
Reset to default 8In case anyone is looking for a solution in 2020. Here is my current solution with react-dnd
and react hooks. You can try the live demo here.
Here is another simpler example, you can check out the codesandbox here.
You can only drag one item at a time using react-dnd. Either use a different library, or somehow group together the different pieces into one item first, and then drag and drop that one item.
I know its a bit late but have you looked into: panResponder. I am looking into multiple d'n'd elements and panResponder is the most likely fit
A nice choice for your need could be react-beautiful-dnd
.
Try this out It will surely work in your case!
react-beautiful-dnd multi drag pattern
https://github./atlassian/react-beautiful-dnd/tree/master/stories/src/multi-drag
demo: https://react-beautiful-dndlify.app/?path=/story/multi-drag--pattern
import React, { Component } from 'react';
import styled from '@emotion/styled';
import { DragDropContext } from 'react-beautiful-dnd';
import initial from './data';
import Column from './column';
import type { Result as ReorderResult } from './utils';
import { mutliDragAwareReorder, multiSelectTo as multiSelect } from './utils';
import type { DragStart, DropResult, DraggableLocation } from 'react-beautiful-dnd';
import type { Task, Id } from '../types';
import type { Entities } from './types';
const Container = styled.div`
display: flex;
user-select: none;
justify-content: center;
`;
type State = {
entities: Entities,
selectedTaskIds: Id[],
columnFlag: false,
// sad times
draggingTaskId: Id,
};
const getTasks = (entities: Entities, columnId: Id): Task[] =>
entities.columns[columnId].taskIds.map(
(taskId: Id): Task => entities.tasks[taskId],
);
export default class TaskApp extends Component<any, State> {
state: State = {
entities: initial,
selectedTaskIds: [],
draggingTaskId: '',
};
ponentDidMount() {
window.addEventListener('click', this.onWindowClick);
window.addEventListener('keydown', this.onWindowKeyDown);
window.addEventListener('touchend', this.onWindowTouchEnd);
}
ponentWillUnmount() {
window.removeEventListener('click', this.onWindowClick);
window.removeEventListener('keydown', this.onWindowKeyDown);
window.removeEventListener('touchend', this.onWindowTouchEnd);
}
onDragStart = (start: DragStart) => {
const id: string = start.draggableId;
const selected: Id = this.state.selectedTaskIds.find(
(taskId: Id): boolean => taskId === id,
);
// if dragging an item that is not selected - unselect all items
if (!selected) {
this.unselectAll();
}
this.setState({
draggingTaskId: start.draggableId,
});
};
onDragEnd = (result: DropResult) => {
const destination = result.destination;
const source = result.source;
const draggableId = result.draggableId;
const bine = result.bine;
console.log('bine',bine);
console.log('destination',destination);
console.log('source',source);
console.log('draggableId',draggableId);
// nothing to do
if (!destination || result.reason === 'CANCEL') {
this.setState({
draggingTaskId: '',
});
return;
}
const processed: ReorderResult = mutliDragAwareReorder({
entities: this.state.entities,
selectedTaskIds: this.state.selectedTaskIds,
source,
destination,
});
this.setState({
...processed,
draggingTaskId: null,
});
};
onWindowKeyDown = (event: KeyboardEvent) => {
if (event.defaultPrevented) {
return;
}
if (event.key === 'Escape') {
this.unselectAll();
}
};
onWindowClick = (event: KeyboardEvent) => {
if (event.defaultPrevented) {
return;
}
this.unselectAll();
};
onWindowTouchEnd = (event: TouchEvent) => {
if (event.defaultPrevented) {
return;
}
this.unselectAll();
};
toggleSelection = (taskId: Id) => {
const selectedTaskIds: Id[] = this.state.selectedTaskIds;
const wasSelected: boolean = selectedTaskIds.includes(taskId);
console.log('hwwo',this.state.entities.columns);
console.log('hwwo',this.state.entities.columns.done.taskIds);
// if there is change in entities - update the state
const newTaskIds: Id[] = (() => {
// Task was not previously selected
// now will be the only selected item
if (!wasSelected) {
return [taskId];
}
// Task was part of a selected group
// will now bee the only selected item
if (selectedTaskIds.length > 1) {
return [taskId];
}
// task was previously selected but not in a group
// we will now clear the selection
return [];
})();
this.setState({
selectedTaskIds: newTaskIds,
});
};
toggleSelectionInGroup = (taskId: Id) => {
const selectedTaskIds: Id[] = this.state.selectedTaskIds;
const index: number = selectedTaskIds.indexOf(taskId);
// if not selected - add it to the selected items
if (index === -1) {
this.setState({
selectedTaskIds: [...selectedTaskIds, taskId],
});
return;
}
// it was previously selected and now needs to be removed from the group
const shallow: Id[] = [...selectedTaskIds];
shallow.splice(index, 1);
this.setState({
selectedTaskIds: shallow,
});
};
// This behaviour matches the MacOSX finder selection
multiSelectTo = (newTaskId: Id) => {
const updated: string[] | null | undefined = multiSelect(
this.state.entities,
this.state.selectedTaskIds,
newTaskId,
);
if (updated == null) {
return;
}
this.setState({
selectedTaskIds: updated,
});
};
unselect = () => {
this.unselectAll();
};
unselectAll = () => {
this.setState({
selectedTaskIds: [],
});
};
render() {
const entities = this.state.entities;
const selected = this.state.selectedTaskIds;
console.log('entities', entities);
console.log('selected', selected);
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragEnd={this.onDragEnd}
>
<Container>
{entities.columnOrder.map((columnId: Id) => (
<Column
column={entities.columns[columnId]}
tasks={getTasks(entities, columnId)}
selectedTaskIds={selected}
key={columnId}
draggingTaskId={this.state.draggingTaskId}
toggleSelection={this.toggleSelection}
toggleSelectionInGroup={this.toggleSelectionInGroup}
multiSelectTo={this.multiSelectTo}
entities={entities}
/>
))}
</Container>
</DragDropContext>
);
}
}
本文标签: javascriptHow to Drag amp Drop Multiple Elements In ReactStack Overflow
版权声明:本文标题:javascript - How to Drag & Drop Multiple Elements In React? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1743674980a2520157.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论