Quero fazer um componente React arrastável (isto é, reposicionável pelo mouse), que parece necessariamente envolver o estado global e manipuladores de eventos dispersos. Posso fazer isso da maneira suja, com uma variável global em meu arquivo JS, e provavelmente poderia até envolvê-lo em uma interface de encerramento agradável, mas quero saber se há uma maneira que mescla melhor com o React.
Além disso, como nunca fiz isso em JavaScript bruto antes, gostaria de ver como um especialista faz isso, para ter certeza de que tenho todos os casos menores tratados, especialmente no que se refere ao React.
Obrigado.
javascript
reactjs
Andrew Fleenor
fonte
fonte
Respostas:
Eu provavelmente deveria transformar isso em um post de blog, mas aqui está um exemplo bastante sólido.
Os comentários devem explicar as coisas muito bem, mas entre em contato se tiver dúvidas.
E aqui está o violino para brincar: http://jsfiddle.net/Af9Jt/2/
Reflexões sobre propriedade estatal, etc.
"Quem deve ser o proprietário de qual estado" é uma pergunta importante a ser respondida, desde o início. No caso de um componente "arrastável", posso ver alguns cenários diferentes.
Cenário 1
o pai deve possuir a posição atual do arrastável. Nesse caso, o arrastável ainda teria o estado "estou arrastando", mas chamaria
this.props.onChange(x, y)
sempre que um evento mousemove ocorrer.Cenário 2
o pai só precisa possuir a "posição de não movimento", e então o arrastável possuiria sua "posição de arrastar", mas no mouseup ele chamaria
this.props.onChange(x, y)
e adiaria a decisão final para o pai. Se o pai não gosta de onde o arrastável acabou, ele simplesmente não atualizaria seu estado, e o arrastável "voltaria" à sua posição inicial antes de arrastar.Mixin ou componente?
@ssorallen apontou que, como "draggable" é mais um atributo do que uma coisa em si, pode servir melhor como um mixin. Minha experiência com mixins é limitada, então não vi como eles podem ajudar ou atrapalhar em situações complicadas. Essa pode ser a melhor opção.
fonte
Mixin
que uma classe completa, já que "Arrastável" não é realmente um objeto, é a habilidade de um objeto.var computedStyle = window.getComputedStyle(this.getDOMNode()); pos = { top: parseInt(computedStyle.top), left: parseInt(computedStyle.left) };
Se você estiver usando jquery com react, provavelmente está fazendo algo errado;) Se precisar de algum plugin jquery, acho que geralmente é mais fácil e menos código reescrevê-lo em react puro.this.getDOMNode().getBoundingClientRect()
- getComputedStyle pode gerar qualquer propriedade CSS válida, incluindoauto
nesse caso o código acima resultará em aNaN
. Consulte o artigo MDN: developer.mozilla.org/en-US/docs/Web/API/Element/…this.getDOMNode()
, isso foi descontinuado. Use um ref para obter o nó dom. facebook.github.io/react/docs/…Implementei o react-dnd , um mixin de arrastar e soltar HTML5 flexível para o React com controle DOM completo.
As bibliotecas existentes de arrastar e soltar não se adequavam ao meu caso de uso, então escrevi o meu próprio. É semelhante ao código que executamos há cerca de um ano no Stampsy.com, mas reescrito para aproveitar as vantagens do React e do Flux.
Principais requisitos que eu tinha:
Se isso soa familiar para você, continue lendo.
Uso
Fonte de arrasto simples
Primeiro, declare os tipos de dados que podem ser arrastados.
Eles são usados para verificar a “compatibilidade” de fontes de arrastar e destinos de soltar:
(Se você não tiver vários tipos de dados, esta biblioteca pode não ser para você.)
Então, vamos fazer um componente arrastável muito simples que, quando arrastado, representa
IMAGE
:Ao especificar
configureDragDrop
, informamosDragDropMixin
o comportamento de arrastar e soltar desse componente. Os componentes arrastáveis e soltáveis usam o mesmo mixin.Por dentro
configureDragDrop
, precisamos chamarregisterType
cada um de nossos customItemTypes
que esse componente suporta. Por exemplo, pode haver várias representações de imagens em seu aplicativo e cada uma forneceria umdragSource
forItemTypes.IMAGE
.A
dragSource
é apenas um objeto que especifica como a fonte de arrastar funciona. Você deve implementarbeginDrag
para retornar o item que representa os dados que você está arrastando e, opcionalmente, algumas opções que ajustam a IU de arrastar. Você pode, opcionalmente, implementarcanDrag
para proibir arrastar ouendDrag(didDrop)
para executar alguma lógica quando o soltar ocorreu (ou não). E você pode compartilhar essa lógica entre os componentes, permitindo que um mixin compartilhado seja geradodragSource
para eles.Finalmente, você deve usar
{...this.dragSourceFor(itemType)}
em alguns (um ou mais) elementosrender
para anexar manipuladores de arrasto. Isso significa que você pode ter várias “alças de arrastar” em um elemento e elas podem até corresponder a diferentes tipos de itens. (Se você não estiver familiarizado com os atributos de propagação JSX sintaxe de , verifique).Alvo de queda simples
Digamos que queremos
ImageBlock
ser um alvo de soltar paraIMAGE
s. É praticamente o mesmo, exceto que precisamos fornecerregisterType
umadropTarget
implementação:Arraste a fonte + solte o destino em um componente
Digamos que agora desejamos que o usuário seja capaz de arrastar uma imagem para fora de
ImageBlock
. Precisamos apenas adicionar apropriadodragSource
a ele e alguns manipuladores:O que mais é possível?
Não abordei tudo, mas é possível usar esta API de mais algumas maneiras:
getDragState(type)
egetDropState(type)
para saber se arrastar está ativo e use-o para alternar classes ou atributos CSS;dragPreview
paraImage
usar imagens como espaços reservados para arrastar (useImagePreloaderMixin
para carregá-las);ImageBlocks
reordenável. Só precisamos deles para implementardropTarget
edragSource
paraItemTypes.BLOCK
.dropTargetFor(...types)
permite especificar vários tipos de uma vez, de modo que uma zona de lançamento pode capturar muitos tipos diferentes.Para obter a documentação atualizada e as instruções de instalação, acesse o reac-dnd repo no Github .
fonte
A resposta de Jared Forsyth está terrivelmente errada e desatualizada. Ele segue todo um conjunto de antipadrões, como o uso de
stopPropagation
, inicializando estado a partir de props , uso de jQuery, objetos aninhados em estado e tem algumdragging
campo de estado estranho . Se estiver sendo reescrito, a solução será a seguinte, mas ainda força a reconciliação virtual do DOM em cada movimento do mouse e não tem muito desempenho.UPD. Minha resposta estava terrivelmente errada e desatualizada. Agora, o código alivia os problemas do ciclo de vida lento do componente React usando manipuladores de eventos nativos e atualizações de estilo, usa
transform
porque não leva a refluxos e limita as alterações de DOMrequestAnimationFrame
. Agora são consistentemente 60 FPS para mim em todos os navegadores que experimentei.e um pouco de CSS
Exemplo no JSFiddle.
fonte
Atualizei a solução polkovnikov.ph para React 16 / ES6 com melhorias como manipulação de toque e encaixe em uma grade, que é o que eu preciso para um jogo. O ajuste a uma grade alivia os problemas de desempenho.
fonte
Reagir arrastável também é fácil de usar. Github:
https://github.com/mzabriskie/react-draggable
E meu index.html:
Você precisa dos estilos deles, que são curtos, ou não obtém o comportamento esperado. Gosto do comportamento mais do que de algumas das outras opções possíveis, mas também há algo chamado reajustável e móvel . Estou tentando fazer o redimensionamento funcionar com arrastável, mas nenhuma alegria até agora.
fonte
Aqui está uma abordagem simples e moderno a este com
useState
,useEffect
euseRef
em ES6.fonte
Eu gostaria de adicionar um terceiro cenário
A posição de movimento não é salva de forma alguma. Pense nisso como um movimento do mouse - o cursor não é um componente do React, certo?
Tudo o que você faz é adicionar um objeto como "arrastável" ao seu componente e um fluxo de eventos de arrastamento que irão manipular o dom.
Nesse caso, a manipulação de DOM é uma coisa elegante (nunca pensei que diria isso)
fonte
setXandY
função com um componente imaginário?Atualizei a classe usando refs, pois todas as soluções que vejo aqui têm coisas que não são mais suportadas ou em breve serão depreciadas
ReactDOM.findDOMNode
. Pode ser pai de um componente filho ou um grupo de filhos :)fonte
Aqui está uma resposta para 2020 com um gancho:
Em seguida, um componente que usa o gancho:
fonte