1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Chakra-ui v3 の画面オブジェクトを移動させる

Last updated at Posted at 2025-03-11

記事の目的

Chakra-ui v3 を併用して画面上を移動できるオブジェクトを作る必要があった。
Web上で調べてみたが、Chakra-uiを使って実装した記事がなく実装に苦労したため、備忘録として記録を残すことにした。 (同じように困っている人の役に立てば幸いです。)

実装要件

  1. ある一定の枠内にて、ドラッグ / ドロップで移動できるオブジェクト(アイコン)を表示
  2. オブジェクトが配置された、画面上の座標を取得

実装バージョン

  • react : 19.0.0
  • chakra-ui : 3.8.0
  • typescript : 5.7.2

必要なライブラリ

  • @chakra-ui/react
  • react-draggable
  • react-icons
npm i @chakra-ui/react react-draggable react-icons

実装

useState を使用して座標を管理

const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 });

useRef によるDOM要素への参照、操作

const dragRef = useRef<HTMLDivElement>(null!);
const containerRef = useRef<HTMLDivElement>(null!);

ドラッグ終了時の処理

const handleStop = (e: DraggableEvent, data: DraggableData) => {
  if (data?.x !== undefined && data?.y !== undefined) {
  setPosition({ x: data.x, y: data.y });
  }
};

移動可能な範囲の作成

<Box
  ref={containerRef}
  w="400px"
  h="300px"
  overflow="hidden"
>

Draggable の設定

  • デフォルトの位置
  • 移動終了時の座標取得
  • 枠内のみの移動制限
<Draggable
  defaultPosition={{ x: 0, y: 0 }}
  onStop={handleStop}
  nodeRef={dragRef}
  bounds="parent" // 枠内のみ移動可能
  enableUserSelectHack={false} // タッチパネル対応
>

アイコンの台座を作成

<Box
  ref={dragRef}
  color="white"
  display="flex"
  alignItems="center"
  justifyContent="center"
  borderRadius="md"
  cursor="grab" // hover時のマウスカーソルをグローブに変更
  position="absolute" // Draggable の bounds を機能させる
  touchAction="none" // タッチ操作を有効化
>

現在の座標を取得し、表示する

<Text>
  X: {position.x.toFixed(0)}, Y: {position.y.toFixed(0)}
</text>

完成画面

image.png

完成コード
import { Text, Box } from "@chakra-ui/react";
import { FC, memo, useRef, useState } from "react";
import { AiFillEnvironment } from "react-icons/ai";

export const Home:FC = () => {
  const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
  const dragRef = useRef<HTMLDivElement>(null!);
  const containerRef = useRef<HTMLDivElement>(null!);

  const handleStop = (e: DraggableEvent, data: DraggableData) => {
    if (data?.x !== undefined && data?.y !== undefined) {
      setPosition({ x: data.x, y: data.y });
    }
  };

  return (
    <>
      <Box p={5} position="relative">
        <Box
          ref={containerRef} // 制限する枠の ref
          w="400px"
          h="300px"
          bg="gray.100"
          position="relative"
          border="2px solid gray"
          borderRadius="md"
          overflow="hidden"
        >
          <Draggable
            defaultPosition={{ x: 0, y: 0 }}
            onStop={handleStop}
            nodeRef={dragRef}
            bounds="parent"
          >
            <Box
              ref={dragRef}
              color="white"
              display="flex"
              alignItems="center"
              justifyContent="center"
              cursor="grab"
              position="absolute"
            >
              <Icon color="gray.500" fontSize="30px" margin="auto">
                <AiFillEnvironment  />
              </Icon>
            </Box>
          </Draggable>
        </Box>
        <Text mt={4}>X: {position.x.toFixed(0)}, Y: {position.y.toFixed(0)}</Text>
      </Box>

実装できなかったこと

dnd-kit の使用

dnd-kit を使用して Dropperble 領域を作成したが、Draggable をうまく入れ子にできなかった。
コードが複雑化したため、諦めた。

Draggable をそのままラップ

アイコンをそのまま Draggable でラップしたが、Chakra-uiによりエラーとなってしまった。

今後の実装

初期の座標をDBから取得、移動したオブジェクトの座標をDBに登録する処理を実装していきます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?