3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Reactアプリ100本ノックやってみた 「06 Profile」

3
Last updated at Posted at 2024-01-05

はじめに

フロントエンドに苦手意識のある私が、@Sicut_studyさんの【Reactアプリ100本ノック】をやってみました。
変なところがあれば教えていただきたいです。

今回の課題

やってみる

App.tsx
import React, { useState, useRef } from "react";
import html2canvas from "html2canvas";

const App = () => {
  const [base64Images, setBase64Images] = useState<string[]>([]);
  const inputRef = useRef<HTMLInputElement>(null);
  const [name, setName] = useState("");
  const [date, setDate] = useState("");
  const [phone, setPhone] = useState("");

  const rectangleStyle = {
    width: "300px",
    height: "300px",
    border: "2px solid #000000",
    borderRadius: "10px",
  };

  const imageStyle = {
    width: "200px",
    height: "200px",
    borderRadius: "100px",
  };

  const buttonStyle = {
    color: "#fff",
    background: "#eb6100",
    width: "200px",
  };

  // お誕生日入力時の処理
  const handleDateChange = (event: {
    target: { value: React.SetStateAction<string> };
  }) => {
    setDate(event.target.value);
  };

  // プロフィール写真入力時の処理
  // 参考 https://zenn.dev/yuyan/articles/f35da08770a135
  const handleInputFile = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) {
      return;
    }

    const fileArray = Array.from(files);
    const loadImages = new Array(fileArray.length);
    let loadCount = 0;

    fileArray.forEach((file, index) => {
      const reader = new FileReader();
      reader.onloadend = () => {
        const result = reader.result;
        if (typeof result !== "string") {
          return;
        }
        loadImages[index] = result;
        loadCount++;
        if (loadCount === fileArray.length) {
          setBase64Images((prevImages) => [...prevImages, ...loadImages]);
        }
      };
      reader.readAsDataURL(file);
    });
    if (inputRef.current) {
      inputRef.current.value = "";
    }
  };

  // PNG出力時の処理
  //参考 https://oldbigbuddha.dev/posts/react-component-to-png
  const onClickExport = () => {
    // 画像に変換する component の id を指定
    const target = document.getElementById("target-component");
    if (target) {
      html2canvas(target).then((canvas) => {
        const targetImgUri = canvas.toDataURL("img/png");
        saveAsImage(targetImgUri);
      });
    } else {
      console.error("target-componentが見つかりません");
    }
  };

  const saveAsImage = (uri: any) => {
    const downloadLink = document.createElement("a");

    if (typeof downloadLink.download === "string") {
      downloadLink.href = uri;

      // ファイル名
      downloadLink.download = "component.png";

      // Firefox では body の中にダウンロードリンクがないといけないので一時的に追加
      document.body.appendChild(downloadLink);

      // ダウンロードリンクが設定された a タグをクリック
      downloadLink.click();

      // Firefox 対策で追加したリンクを削除しておく
      document.body.removeChild(downloadLink);
    } else {
      window.open(uri);
    }
  };

  return (
    <div>
      <div style={{ display: "flex" }}>
        <div style={{ marginRight: "50px" }}>
          <div style={rectangleStyle} id="target-component">
            <div>
              {base64Images.length !== 0 &&
                base64Images.map((image, idx) => (
                  <div key={idx}>
                    <img src={image} style={imageStyle} />
                  </div>
                ))}
            </div>

            <div>お名前:{name}</div>
            <div>お誕生日:{date}</div>
            <div>電話番号:{phone}</div>
          </div>
        </div>
        <div>
          <div>
            <div>お名前</div>
            <input
              type="text"
              value={name}
              onChange={(e) => {
                setName(() => e.target.value);
              }}
            />
          </div>

          <div>
            <div>お誕生日</div>
            <input type="date" value={date} onChange={handleDateChange} />
          </div>

          <div>
            <div>電話番号</div>
            <input
              type="text"
              value={phone}
              onChange={(e) => {
                setPhone(() => e.target.value);
              }}
            />
          </div>

          <div>
            <div>プロフィール写真</div>
            <input
              type="file"
              accept="image/png"
              onChange={handleInputFile}
              ref={inputRef}
            />
          </div>

          <div>
            <button style={buttonStyle} onClick={() => onClickExport()}>
              PNG出力
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;

完成

image.png

おわりに

ただただ要件を満たしただけのコードですが完了とします。やってみると分かってきたのですが、僕はロジックを考えるのは好きですが、デザインとかにはあまり興味がないようです。

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?