ドラッグ&ドロップが必要なんだけど、自力コンポーネントをより良くしたい。
ライブラリに頼ってみようということで前回のreact-grid-layoutに引き続き今日はreact-draggableを触ってみた。
GitHub - react-grid-layout/react-draggable: React draggable component
React draggable component. Contribute to react-grid-layout/react-draggable development by creating an account on GitHub.
今回も基本的な部分だけ自分用覚え書き残しておく
draggableパターン
共通
- react-draggableの呼び出し
import Draggable from "react-draggable";
- dragイベントのアクティブ状態をstateで管理
const [activeDrags, setActiveDrags] = React.useState(false);
const onStart = () => {
setActiveDrags(true);
};
const onStop = () => {
setActiveDrags(false);
};
const dragHandlers = { onStart: onStart, onStop: onStop };
まずは普通に移動できるブロック
<Draggable {...dragHandlers}>
<div className="box">どこにでも移動</div>
</Draggable>
縦横移動制限
<Draggable axis="x" {...dragHandlers}>
<div className="box cursor-x">横にしか移動しない (x axis)</div>
</Draggable>
<Draggable axis="y" {...dragHandlers}>
<div className="box cursor-y">縦にしか移動しない (y axis)</div>
</Draggable>
- axiosプロパティでx,yを渡す
動かないブロック
<Draggable onStart={() => false}>
<div className="box">動かぬ</div>
</Draggable>
移動値を記録
const [deltaPosition, setDeltaPosition] = React.useState({
x: 0,
y: 0
});
const handleDrag = (e, ui) => {
const { x, y } = deltaPosition;
setDeltaPosition({ ...deltaPosition, x: x + ui.deltaX, y: y + ui.deltaY });
};
<Draggable onDrag={handleDrag} {...dragHandlers}>
<div className="box">
<div>移動値を記録する</div>
<div>
x: {deltaPosition.x.toFixed(0)}, y: {deltaPosition.y.toFixed(0)}
</div>
</div>
</Draggable>
- onDragプロパティに移動距離がdeltaX,deltaYとして渡ってくるので記録に使用できる
draggableな要素の指定
<Draggable handle="strong" {...dragHandlers}>
<div className="box no-cursor">
<strong className="cursor">
<div>移動するボタン</div>
</strong>
<div>移動するボタン押して移動してね</div>
</div>
</Draggable>
<Draggable handle="strong" {...dragHandlers}>
<div
className="box no-cursor"
style={{ display: "flex", flexDirection: "column" }}
>
<strong className="cursor">
<div>移動するボタン</div>
</strong>
<div style={{ overflow: "scroll" }}>
<div style={{ background: "yellow", whiteSpace: "pre-wrap" }}>
スクロールバー付きコンテンツ
{"\\n" + Array(40).fill("x").join("\\n")}
</div>
</div>
</div>
</Draggable>
- handleプロパティに要素を文字列で指定(クラス名指定だったら”.classname”でいける)
- 指定してないとDraggable配下全部がdraggableな要素になる
unDraggableな要素の指定
<Draggable cancel="strong" {...dragHandlers}>
<div className="box">
<strong className="no-cursor">ここを押してもdragできない</strong>
<div>ここはdragできるよ</div>
</div>
</Draggable>
- cancelプロパティに要素名を指定するとDragできない部分の指定が可能
スナップにグリッドを効かせる
<Draggable grid={[25, 25]} {...dragHandlers}>
<div className="box">25 x 25 gridで移動するよ</div>
</Draggable>
<Draggable grid={[50, 50]} {...dragHandlers}>
<div className="box">50 x 50 gridで移動するよ</div>
</Draggable>
- gridプロパティに[x,y]値を渡すと値刻みでカクカク移動するようになる
移動域の制限
<Draggable
bounds={{ top: -100, left: -100, right: 100, bottom: 100 }}
{...dragHandlers}
>
<div className="box">100pxまでしか移動できない</div>
</Draggable>
<Draggable bounds="body" {...dragHandlers}>
<div className="box">body要素内だけで動く</div>
</Draggable>
- boundsプロパティに対して各域の数値指定や要素名指定ができる
ブロックホバー検出
const onDropAreaMouseEnter = (e) => {
//マウスエンターイベント&&drag中の要素があればホバーしているものとする
if (activeDrags) {
e.target.classList.add("hovered");
}
};
const onDropAreaMouseLeave = (e) => {
e.target.classList.remove("hovered");
};
<Draggable {...dragHandlers}>
<div
className="box drop-target"
onMouseEnter={onDropAreaMouseEnter}
onMouseLeave={onDropAreaMouseLeave}
>
ブロックが乗ったのを検出するよ
</div>
</Draggable>
ブロックドロップ検出
const onDrop = (e) => {
setActiveDrags(false);
if (e.target.classList.contains("drop-target")) {
//Dropされた位置がターゲット内であればアラートを表示
alert("Dropped!");
e.target.classList.remove("hovered");
}
};
<Draggable {...dragHandlers} onStop={onDrop}>
<div className={`box ${activeDrags ? "no-pointer-events" : ""}`}>
別のブロックの上に乗ったのを検出するよ
</div>
</Draggable>
親要素内でのみ移動可能な子要素
<div
className="box"
style={{
height: "500px",
width: "500px",
position: "relative",
overflow: "auto",
padding: "0"
}}
>
<div style={{ height: "1000px", width: "1000px", padding: "10px" }}>
<Draggable bounds="parent" {...dragHandlers}>
<div className="box">
offsetParent内だけで動くよ
<br />
<br />
親要素のpaddingと子要素のmarginが機能します
</div>
</Draggable>
<Draggable bounds="parent" {...dragHandlers}>
<div className="box">
offsetParent内だけで動くよ
<br />
<br />
親要素のpaddingと子要素のmarginが機能します
</div>
</Draggable>
</div>
</div>