iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0

部署專案

複習 🚀[Day18] 實作 - Firebase 創建專案 & 部署

在這篇文章有說到如何部署專案,完成後可以再輸入 npm run build 再查看專案是否部署完成~

視訊間樣式

為了讓視訊間更完成這篇就加上簡約的樣式,此處使用 styled-component 有興趣或想參考 UI 的人可以繼續看下去,也可以用這個 UI 進行優化~

非常簡(偷)約(懶)的樣式🐥🐥🐥

https://ithelp.ithome.com.tw/upload/images/20231009/20151124eScJoXde0G.png

https://ithelp.ithome.com.tw/upload/images/20231009/20151124rOILCQl7nv.png

styled-component 介紹

styled-component 是一個 CSS-In-JS 的 libaray ,可以在 JSX 中撰寫 css ,且透過 JS 動態產生 unique 的 class name,不必擔心類名或全域樣式衝突的問題。

// style 用於創建 element 樣式,createGlobalStyle 用於創建全局樣式
import styled, { createGlobalStyle } from 'styled-components';

// 需注意名稱需大寫開頭
const Btn = styled.button`
  background-color: #007bff;
  color: #fff;
  padding: 10px 20px;
  border: none;
  cursor: pointer;
`;

return {
	<Btn />
}

react-icons 介紹

react-icons 提供多種風格的 icon,可以直接在 react 中 import 使用。

import { BsCameraVideoFill } from 'react-icons/bs';

return {
	<BsCameraVideoFill/>
}

實作

npm i styled-component
npm i react-icons
  • import 需要的 icon & styled-component function
import {
  BsCameraVideoFill,
  BsCameraVideoOffFill,
} from 'react-icons/bs';
import { FaMicrophone, FaMicrophoneSlash, FaPhoneSlash } from 'react-icons/fa';
import styled, { createGlobalStyle } from 'styled-components';
  • <GlobalStyle /> 在 component return 中
  • 藉由傳 props 到 styled-component isShow={isCall} 判斷 display 樣式
  • 以 state 判斷 videoState.isAudioMuted icon 顯示代替文字
return (
    <Container isColumn={peerConnection}>
      <GlobalStyle />
      <VideoContainer>
        <LocalVideo ref={localVideoRef} autoPlay muted playsInline />
        <RemoteVideo
          ref={remoteVideoRef}
          autoPlay
          playsInline
          isShow={isCall}
        />
      </VideoContainer>
      <ActionContainer isShow={!isCall}>
        <OpenMediaBtn onClick={openMedia}>Open Media</OpenMediaBtn>
        <RoomAction>
          <ActionTitle>Create Room & Share ID</ActionTitle>
          <ActionBtn onClick={createRoom}>Create Room</ActionBtn>
        </RoomAction>
        <RoomAction>
          <ActionTitle>Join Room by Entering ID</ActionTitle>
          <RoomIdInput
            value={roomId}
            onChange={(e) => setRoomId(e.target.value)}
          />
          <ActionBtn onClick={() => joinRoom(roomId)}>Join Room</ActionBtn>
        </RoomAction>
      </ActionContainer>
      <ToolBtnContainer isShown={peerConnection}>
        <ToolBtn onClick={toggleMute}>
          {videoState.isMuted ? <FaMicrophoneSlash /> : <FaMicrophone />}
        </ToolBtn>
        <ToolBtn onClick={toggleVideo}>
          {videoState.isVideoDisabled ? (
            <BsCameraVideoOffFill />
          ) : (
            <BsCameraVideoFill />
          )}
        </ToolBtn>
        <ToolBtn onClick={hangUp}>
          <FaPhoneSlash />
        </ToolBtn>
      </ToolBtnContainer>
    </Container>
  );
const GlobalStyle = createGlobalStyle`
* {
    box-sizing: border-box;
}`;
const Container = styled.div`
  font-family: "Gabarito", cursive;
  margin: 0 auto;
  max-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: ${({ isColumn }) => (isColumn ? 'column' : 'row')};
  gap: 10px;
  padding-top:25vh;
`;
const defaultBtn = styled.button`
  border: 2px solid black;
  border-radius: 6px;
  background: none;
  cursor: pointer;
  padding: 10px 18px;
  font-size: 16px;
  letter-spacing: 1px;
  box-shadow: 3px 3px 0px 1px rgba(0, 0, 0, 1);
  margin: 0 auto;
`;
const ActionContainer = styled.div`
  display: ${({ isShow }) => (isShow ? 'block' : 'none')};
  text-align: center;
`;
const OpenMediaBtn = styled(defaultBtn)`
  width: 250px;
  margin-bottom: 20px;
`;
const ActionBtn = styled(defaultBtn)`
  width: 100%;
`;
const RoomAction = styled.div`
  padding: 20px;
  border: 2px solid black;
  border-radius: 5px;
  max-width: 250px;
  max-height: 200px;
  margin: 0 auto;
  margin-bottom: 20px;
  &:last-child {
    margin-bottom: 0px;
  }
`;
const ActionTitle = styled.div`
  margin-bottom: 10px;
  padding-bottom: 10px;
`;
const RoomIdInput = styled.input`
  margin-bottom: 15px;
  width: 100%;
  border: 1.5px solid black;
  border-radius: 6px;
  padding: 5px 10px;
`;
const VideoContainer = styled.div`
  display: flex;
  gap: 10px;
`;
const VideoCall = styled.video`
  border: 2px solid black;
  border-radius: 6px;
  height: 350px;
  width: 460px;
`;
const LocalVideo = styled(VideoCall)``;
const RemoteVideo = styled(VideoCall)`
  display:${({ isShow }) => (isShow ? 'block' : 'none')}
`;
const ToolBtnContainer = styled.div`
  display: ${({ isShown }) => (isShown ? 'flex' : 'none')};
  gap: 10px;
`;
const ToolBtn = styled.button`
  border: 2px solid black;
  border-radius: 25px;
  background: #eaeaea;
  cursor: pointer;
  width: 50px;
  height: 50px;
  box-shadow: 2px 2px 0px 0.5px rgba(0, 0, 0, 1);
  svg {
    transform: scale(1.3);
  }
`;

這篇落落長,其實只是添加 style 樣式而已~ 可以依照自己的喜好完成樣式,接下來會有兩篇延伸討論。


上一篇
[Day26] 實作 - beforeunload 關閉視窗時提醒
下一篇
[Day28] 延伸討論 - 一對多、多人視訊通話
系列文
前端工程師30天 WebRTC + Firebase 視訊通話原理到實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言