본문 바로가기
프로젝트

[일렉트론][리액트] 캐릭터 만들어보기

by 꽁이꽁설꽁돌 2024. 5. 6.
728x90
반응형

목차

     
    데스크톱에 캐릭터를 띄어보고자 해서 일렉트론으로 만들어 보았다.

    대충 데스크톱에 띄어진 모습

     

    main.js

    // public/electron.js
    const { app, BrowserWindow} = await import("electron");
    const path = await import("path");
    const isDev = await import("electron-is-dev");
    
    let mainWindow;
    
    function createWindow() {
      //const { width, height } = screen.getPrimaryDisplay().workAreaSize;
      mainWindow = new BrowserWindow({
        width: 500,  //크기 조정
        height: 400,
        frame: false, //frame제거
        transparent: true, //투명하게 만들어 줌
        alwaysOnTop: true, //우선적으로 선택하게 해줌
    
        webPreferences: {
          nodeIntegration: true,
          enableRemoteModule: true,
          devTools: isDev,
        },
      });
      
      mainWindow.loadURL(
        isDev
          ? "http://localhost:3000"
          : `file://${path.join(__dirname, "../build/index.html")}`
      );
    
      if (isDev) mainWindow.webContents.openDevTools({ mode: "detach" }); //개발모드
    
      mainWindow.setResizable(true);
      mainWindow.on("closed", () => {
        mainWindow = null;
        app.quit();
      });
      mainWindow.focus();
      mainWindow.setPosition(1000, 480);  //창이 띄어질 위치 구현
    }
    
    app.on("ready", createWindow);
    
    app.on("activate", () => {
      if (mainWindow === null) createWindow();
    });
    
    app.on("window-all-closed", () => {
      if (process.platform !== "darwin") app.quit();
    });

     

    MainPage.tsx

    import { motion, AnimatePresence } from "framer-motion";
    import { useState } from "react";
    import styled from "styled-components";
    import MainCharacter from "./MainCharacter";
    import ChattingContainer from "./ChattingContainer";
    const Styledback = styled.div`
      width: 100vw;
      height: 100vw;
      border: none;
      display: flex;
      flex-direction: row;
    `;
    
    function MainPage() {
    
    
      return (
        <Styledback>
          <ChattingContainer></ChattingContainer>
          <MainCharacter></MainCharacter>
        </Styledback>
      );
    }
    
    export default MainPage;

     

    MainCharacter.tsx

    import { motion, AnimatePresence } from "framer-motion";  //부드러운 애니메이션용 라이브러리
    import styled from "styled-components";
    import { emotionState } from "./recoil/EmotionAtom";
    import { useState } from "react";
    import { useRecoilState } from "recoil";
    const Styledback = styled.div`
      width: 40vw;
      height: 85vh;
      //position: fixed;
      //bottom: 0;
      //right: 0;
      text-align: center;
    `;
    
    const Styledimg = styled.img`
      width: 40vw;
      height: 80vh;
      border-radius: 10px;
    `;
    
    function MainCharacter() {
      const [isVisible, setVisible] = useState<boolean>(false);
      const [emotion, setemotion] = useRecoilState(emotionState);  //상태 관리 recoil이용
    
      function handleClick() {
        setVisible(!isVisible);
      }
      return (
        <div>
          <Styledback>
            <AnimatePresence initial={false}>
              {isVisible && (
                <motion.div
                  initial={{ opacity: 0 }}
                  animate={{ opacity: 1 }}
                  exit={{ opacity: 0 }}
                  whileHover={{ scale: 1.2 }}
                >
                  <Styledimg src={`./img/${emotion}.jpg`} alt="img" />
                </motion.div>
              )}
            </AnimatePresence>
            Ai bot
          </Styledback>
          <button onClick={handleClick}>Click me</button>
        </div>
      );
    }
    
    export default MainCharacter;

     

    ChattingContainer.tsx

    import Chatting from "./Chatting";
    import styled from "styled-components";
    
    //말풍선 css
    const StyledChat = styled.div`
      position: relative;
      width: 40vw;
      height: 30vh;
      padding: 1px;
      background: #ffffff;
      -webkit-border-radius: 33px;
      -moz-border-radius: 33px;
      border-radius: 33px;
      text-align: center;
    
      &::after {
        content: "";
        position: absolute;
        border-style: solid;
        border-width: 15px 0 15px 15px;
        border-color: transparent #ffffff;
        display: block;
        width: 0;
        z-index: 1;
        right: -15px;
        top: 46px;
      }
    `;
    
    function ChattingContainer() {
      return (
        <StyledChat>
          <Chatting></Chatting>
        </StyledChat>
      );
    }
    
    export default ChattingContainer;

     

    .env.local

    BROWSER = none

    웹에 창이 안띄어지게 설정을 위해 필요하다.
     

    recoil 상태관리

    import { atom, useRecoilState } from "recoil";
    
    export const emotionState = atom({
      key: "imgState", // unique ID (with respect to other atoms/selectors)
      default: "happy", // default value (aka initial value)
    });
    
    export const textState = atom({
      key: "textState", // unique ID (with respect to other atoms/selectors)
      default: "hello", // default value (aka initial value)
    });

    리덕스보다 훨씬 쉽다 ㅎㅎ

    반응형