Hero img
React ライトボックスを作る

Reactでライトボックスを自作した。

画像をフルスクリーンで表示させるライブラリを探して見たが、良いと思える物が無かったので最終的にモーダルウィンドウを使用し自作した。


目次

  • Reactでモーダルウィンドウを作成。
  • React Portal
  • モーダルウィンドウを作成する。
  • react-map-interactionを試してみる。
  • React Portalを使ってみる。
  • ReactDOM.createPortalを追加する
  • モーダルウィンドウ外のクリックので非表示
  • 最終コード
  • まとめ

Reactでモーダルウィンドウを作成。

このブログで使用しているが、Reactで画像をただ単に大きく見せ、ズームできる。それだけの機能が欲しかったのですが、ライブラリを探してみると、なかなか良いものが無かった。そのな時react-map-interactionというライブラリを発見した。
これです。ただズームが出来ればいいんです。
しかし、このライブラリは領域に画像を表示させ、ズームインができるだけのライブラリであり、モーダルウィンドウではない。
その辺を解決するため今回使用したものは

  • createPortal
  • e.stopPropagation
  • mapInteraction

React Portal

モーダルウィンドウは全画面に表示させたいが、問題が発生する。
react-map-interactionを使い画面いっぱい、いっぱい表示させたいのだが、親のタグに影響されて位置がずれたり、うまくフルスクリーンになってくれない。
通常のJS DOM操作なら一番大元の親のid element探して、そこにくっつければいいのだが、React jsxでは無理そう。
探しているうちにRreact Portalを知る。 React Portalはその大元の親を指定して、そこにjsxでレンダリングさせる物であり、の使い道としては、トースト通知、モーダルウィンドウがあるようだ。

モーダルウィンドウを作成する。

react-map-interactionを試してみる。

react-map-interactionはどんな感じで使うのか確認するため、とりあえずこのモジュールを公式ページを参照し、使用してみる。

react-map-interactionをインストールする。

npm install --save react-map-interaction

使い方は公式のBasicを参考にこんな感じでとりあえず表示させてみた。

mapInteraction-test

コード量も少なく、簡単に使えてしまう。

App.js
import logo from './logo.svg';
import './App.css';
import { MapInteractionCSS } from 'react-map-interaction';

function App() {
  return (
    <div className="App">
      <div style={{backgroundColor:"gray"}}>
      <MapInteractionCSS>
        <img src="logo192.png" />
      </MapInteractionCSS>
      </div>
    </div>
  );
}
export default App;

 

React Portalを使ってみる。

React Portalは参照先のelementを指定して、jsxのレンダリングをするものなので、今回新しく、htmlでelementを作成した。

public/index.html
<!--
  追加要素
  モーダルウィンドウの親のelement「modal-root」を作成します。  
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="modal-root"></div>
    <div id="root"></div>

  </body>
</html>


ReactDOM.createPortalを追加する

モーダルウィンドウのを作るために、次にReactDOM.createPortalを使います。

App.js

import logo from './logo.svg';
import ReactDOM from 'react-dom';//<--追加
import './App.css';
import { MapInteractionCSS } from 'react-map-interaction';

function App() {

  //先ほどのReactDom.createPortalを使い、引数に、id="modal-root"を指定。
const ModalDemo =(()=>{
  return ReactDOM.createPortal(
    <div className="App">
      <div style={{backgroundColor:"gray"}}>
      <MapInteractionCSS>
        <img src="logo192.png" />
      </MapInteractionCSS>
      </div>
    </div>,document.getElementById('modal-root'));
})

  return (
    <div className="App" style={{height:"120vh"}}>
      <ModalDemo/>
    </div>
  );
}
export default App;


モーダルウィンドウのトグルボタンを作成してテストする。

cssのスタイリングをして、モーダルウィンドウのトグルボタンを作成しました。

App.js
import ReactDOM from 'react-dom';
import './App.css';
import { MapInteractionCSS } from 'react-map-interaction';
import { useState } from 'react';

function App() {
  const [ShowModal , setShowModal] = useState(false)
  //displayとpositionのこの指定が重要。
  const ModalBox ={
    display:"flex",position:"fixed",
    top:"0",left:"0",width:"100vw",height:"100vh", padding:0, zIndex:20, backgroundColor:"gray"
  }
  const InnerBox = {
    transform:"translate(50%, 50%)",
    width:"50%",height:"50%",
    position:'absolute', backgroundColor:"#ddd"
  }

  const ToggleModal = (()=>{
    setShowModal(!ShowModal)
  })

  const ModalDemo =(()=>{
    return ReactDOM.createPortal(
      <div style={ModalBox}>
        <div style={InnerBox}>
        <MapInteractionCSS>
          <img src="logo192.png" />
        </MapInteractionCSS>
        </div>
      </div>,document.getElementById('modal-root'));
  })

  return (
    <div className="App">
      <button onClick={ToggleModal}>モーダルウィンドウボタン</button>
      {ShowModal && <ModalDemo/>}
    </div>
  );
}
export default App;




完成系

mapInteraction-modal-preview

モーダルウィンドウ外のクリックので非表示

モーダルウィンドウ全画面が出来ましたが、モーダルウィンドウが全面にくるため、このモーダルウィンドウを消す方法がありません。
消すためには、「ShowModal」をfalseにする必要があります。
外枠の背景をクリックすると消えるようにします。と言ってもonClickを追加するだけでいいのですが、

App.js
//onCLick=onClick={ToggleModal}
return ReactDOM.createPortal(
      <div style={ModalBox}  onClick={ToggleModal}>
        <div style={InnerBox}>
        <MapInteractionCSS>
          <img src="logo192.png" />
        </MapInteractionCSS>
        </div>
      </div>,document.getElementById('modal-root'));

これでいいと思ったが、中の画像をクリックしてしまうと「ToggleModal」のイベントが実行されてしまう!!

Modal-click-worng

stopPropagation

stopPropagationの出番です。
中の白色枠にクリックイベントを追加し、クリックした場合、その後親に渡るクリックイベントを無効にします。  

App.js

return ReactDOM.createPortal(
      <div style={ModalBox}  onClick={ToggleModal}>
        <div style={InnerBox} onClick={((e)=>e.stopPropagation())}>
        <MapInteractionCSS>
          <img src="logo192.png" />
        </MapInteractionCSS>
        </div>
      </div>,document.getElementById('modal-root'));

これで完成です。 modal-window-comp

最終コード

App.js
import ReactDOM from 'react-dom';
import './App.css';
import { MapInteractionCSS } from 'react-map-interaction';
import { useState } from 'react';

function App() {
  const [ShowModal , setShowModal] = useState(false)
  const ModalBox ={
    display:"flex",position:"fixed",
    top:"0",left:"0",width:"100vw",height:"100vh", padding:0, zIndex:20, backgroundColor:"gray"
  }
  const InnerBox = {
    transform:"translate(50%, 50%)",
    width:"50%",height:"50%",
    position:'absolute', backgroundColor:"#ddd"
  }

  const ToggleModal = (()=>{
    setShowModal(!ShowModal)
  })

  const ModalDemo =(()=>{
    return ReactDOM.createPortal(
      <div style={ModalBox}  onClick={ToggleModal}>
        <div style={InnerBox} onClick={((e)=>e.stopPropagation())}>
        <MapInteractionCSS>
          <img src="logo192.png" />
        </MapInteractionCSS>
        </div>
      </div>,document.getElementById('modal-root'));
  })

  return (
    <div className="App">
      <button onClick={ToggleModal}>モーダルウィンドウボタン</button> 
      {ShowModal && <ModalDemo/>}
    </div>
  );
}
export default App;

まとめ

モーダルウィンドウを作るためにライブラリを探したが、自分の求めているものが無く、仕方なく自作したが、こんなに簡単にできるのであれば、初めから自作にすれば良かった。
今回覚えた物が以下3つ今後も汎用性あり。十分便利な機能だった。

  • createPortal
  • e.stopPropagation
  • mapInteraction

関連記事

コメント

コメントを書く

質問や、間違いがありましたら、お気軽にどうぞ

※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください