오늘 한 일
1. 오후 1,2시까지 다이어리 페이지에 오류 없는 지 테스트 해보고, 변수/함수 정리할 예정
2. 네비게이션 바 컴포넌트 만들 예정
3. 힐링 보이스 페이지 구현
4. 로그인 유도 페이지 구현 하신 거 반영은 안했지만 프로젝트에 연결 해본 코드
더보기
더보기
import { fontWeight, margin, textAlign } from "@mui/system";
import React from "react";
import Modal from "react-modal";
import { history } from "../redux/configureStore";
const RequireLogin = (props) => {
const [modal, setModal] = React.useState(true); // 모달창
const yes = () => {
history.push("/login");
};
const no = () => {
setModal(!modal);
history.push("/");
};
const modalOff = () => {
setModal(!modal);
history.goBack();
};
return (
<>
<Modal
isOpen={modal}
ariaHideApp={false}
onRequestClose={modalOff}
style={{
overlay: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(15, 15, 15, 0.79)",
},
content: {
position: "absolute",
top: "0%",
left: "0%",
width: "300px",
height: "300px",
border: "1px solid #ccc",
background: "#C4C4C4ff",
overflow: "auto",
WebkitOverflowScrolling: "touch",
borderRadius: "30px",
outline: "none",
padding: "0px",
margin: "auto",
},
}}
>
<div style={{ height: "80px", lineHeight: "80px" }}>
<p
style={{
fontSize: "13px",
fontWeight: "700",
textAlign: "center",
margin: "40px 0px 0px 0px",
}}
>
해당 서비스는 로그인 후 이용 가능합니다
</p>
<p
style={{
fontSize: "13px",
fontWeight: "500",
textAlign: "center",
margin: "10px 0px 0px 0px",
}}
>
로그인 하러 가시겠습니까?
</p>
</div>
<div
style={{
display: "flex",
alignContent: "flex-end",
position: "absolute",
bottom: "0px",
width: "100%",
}}
>
<button
style={{
width: "100%",
height: "50px",
border: "none",
margin: "0px 1px 0px 0px",
}}
onClick={yes}
>
예
</button>
<button
style={{ width: "100%", height: "50px", border: "none" }}
onClick={no}
>
아니오
</button>
</div>
</Modal>
</>
);
};
export default RequireLogin;
질문 사항
1) 음원 출력 할 때 음원이 재생 되어야 하는 건지 그렇다면 활성화도 같이 시켜야 하는 건지 재생 버튼이 있어야 하는 건지 → 볼륨 default 50%, 누르면 활성화 되면서 음성 들리고, 한번 더 누르면 비활성화 되면서 음성 꺼지도록 구현 해야 한다.
2) cookie 보다는 localStorage에 javascript 객체를 JSON 파일로 바꿔서 setItem 하고 getItem 할 때는 JSON 파일로 javascript 객체로 사용하거나 로그인 시 response로 받은 객체 데이터에서 필요한 데이터만 각각 setItem 하면 될 것 닽다. → 필요한 데이터(userIdx, token, noticeSet)만 각각 setItem 해서 localStorage에 담는 걸로 결정
기능 구현
1) 네비게이션 바 컴포넌트 구현
더보기
더보기
import React from "react";
import styled from "styled-components";
import { useHistory } from "react-router-dom";
const Navigation = (props) => {
const history = useHistory();
return (
<>
<div
style={{
display: "flex",
justifyContent: "space-between",
width: "20%",
backgroundColor: "#dddddd",
margin: "auto",
marginTop: `${props.marginTop ? props.marginTop : ""}`,
padding: "20px",
}}
>
<div
onClick={() => {
history.push("/");
}}
>
<Icon></Icon>홈
</div>
<div
onClick={() => {
history.push("/asmr");
}}
>
<Icon></Icon>ASMR
</div>
<div
onClick={() => {
history.push("/diary");
}}
>
<Icon></Icon>다이어리
</div>
<div
onClick={() => {
history.push("/mypage");
}}
>
<Icon></Icon>마이
</div>
</div>
</>
);
};
const Icon = styled.div`
width: 30px;
height: 30px;
border: 1px dotted black;
margin: auto;
`;
export default Navigation;
2) 힐링 보이스 페이지 구현
: 전에 연습 했던 코드에서 업그레이드만 시켰다.
더보기
더보기
import React, { useState, useRef, useEffect } from "react";
import { useDispatch } from "react-redux";
import { actionCreators as voiceActions } from "../redux/modules/voice";
const AudioRecord = () => {
// 오디오 녹음
const [stream, setStream] = useState();
const [media, setMedia] = useState();
const [onRec, setOnRec] = useState(true);
const [source, setSource] = useState();
const [analyser, setAnalyser] = useState();
const [audioUrl, setAudioUrl] = useState();
const [sound, setSound] = useState();
// 타이머
const [count, setCount] = useState(0);
const [currentMinutes, setCurrentMinutes] = useState(0);
const [currentSeconds, setCurrentSeconds] = useState(0);
const intervalRef = useRef(null);
const [disabled, setDisabled] = useState(true);
// --------------- 타이머 ---------------
const start = () => {
intervalRef.current = setInterval(async () => {
setCount((c) => c + 1);
}, 1000);
};
const end = () => {
clearInterval(intervalRef.current);
};
const timer = () => {
const checkMinutes = Math.floor(count / 60);
const minutes = checkMinutes & 60;
const seconds = count % 60;
setCurrentMinutes(minutes);
setCurrentSeconds(seconds);
};
// count의 변화에 따라 timer 함수 렌더링
useEffect(timer, [count]);
// --------------- 녹음 ---------------
const onRecAudio = () => {
setCount(0); // 재녹음 시 타이머 초기화
start(); // 타이머 시작
setDisabled(true);
// 음원정보를 담은 노드를 생성하거나 음원을 실행또는 디코딩 시키는 일을 한다
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// 자바스크립트를 통해 음원의 진행상태에 직접접근에 사용된다.
const analyser = audioCtx.createScriptProcessor(0, 1, 1);
setAnalyser(analyser);
function makeSound(stream) {
// 내 컴퓨터의 마이크나 다른 소스를 통해 발생한 오디오 스트림의 정보를 보여준다.
const source = audioCtx.createMediaStreamSource(stream);
setSource(source);
source.connect(analyser);
analyser.connect(audioCtx.destination);
}
// 마이크 사용 권한 획득
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
setStream(stream);
setMedia(mediaRecorder);
makeSound(stream);
analyser.onaudioprocess = function (e) {
// 3분(180초) 지나면 자동으로 음성 저장 및 녹음 중지
if (e.playbackTime > 180) {
stream.getAudioTracks().forEach(function (track) {
track.stop();
});
mediaRecorder.stop();
// 메서드가 호출 된 노드 연결 해제
analyser.disconnect();
audioCtx.createMediaStreamSource(stream).disconnect();
mediaRecorder.ondataavailable = function (e) {
setAudioUrl(e.data);
setOnRec(true);
};
} else {
setOnRec(false);
}
};
});
};
// 사용자가 음성 녹음을 중지 했을 때
const offRecAudio = () => {
end();
// dataavailable 이벤트로 Blob 데이터에 대한 응답을 받을 수 있음
media.ondataavailable = function (e) {
setAudioUrl(e.data);
setOnRec(true);
};
// 모든 트랙에서 stop()을 호출해 오디오 스트림을 정지
stream.getAudioTracks().forEach(function (track) {
track.stop();
});
// 미디어 캡처 중지
media.stop();
// 메서드가 호출 된 노드 연결 해제
analyser.disconnect();
source.disconnect();
if (audioUrl) {
URL.createObjectURL(audioUrl); // 출력된 링크에서 녹음된 오디오 확인 가능
}
// File 생성자를 사용해 파일로 변환
const sound = new File([audioUrl], "soundBlob", {
lastModified: new Date().getTime(),
type: "audio",
});
setDisabled(false);
setSound(sound); // File 정보 출력
};
const play = () => {
const audio = new Audio(URL.createObjectURL(audioUrl));
audio.loop = false;
audio.volume = 1;
audio.play();
};
const dispatch = useDispatch();
const send = () => {
setCount(0);
dispatch(voiceActions.voiceRecordDB(sound));
};
return (
<>
<h1>
{currentMinutes < 10 ? `0${currentMinutes}` : currentMinutes} :{" "}
{currentSeconds < 10 ? `0${currentSeconds}` : currentSeconds}
</h1>
<div>
<button onClick={play} disabled={disabled}>
재생
</button>
<button onClick={onRec ? onRecAudio : offRecAudio}>녹음</button>
<button onClick={send} disabled={disabled}>
전송
</button>
</div>
</>
);
};
export default AudioRecord;
import React from "react";
import { useSelector } from "react-redux";
const Bubble = (props) => {
const style = { area: [], gap: [] }; // div들을 담고 있는 객체
let checkCircle = ""; // 현재 재생 중인 음성의 index가 무엇인 지 담는다.
let checkLineHeight = ""; // 현재 재생 중인 음성의 height 값을 담는다.
const voiceInfo = useSelector((state) => state.voice.voiceInfo);
for (let i = 0; i < 10; i++) {
style.area.push(Math.ceil(Math.random() * (100 - 40) + 40)); // 동그라미
style.gap.push(Math.ceil(Math.random() * (10 - 5) + 5)); // 간격
}
// ** 사용자가 누른 div만 정해진 색상으로 활성화 **
const changeColor3 = (index, voice, item) => {
if (checkCircle === "") {
// 전에 눌렀던 게 없을 때
let x = document.getElementById(index);
checkLineHeight = item;
x.style.backgroundColor = "purple";
checkCircle = x;
} else if (parseInt(checkCircle.id) === index) {
console.log("눌렀던 거 또 누름");
} else {
// 전에 눌렀던 음성이 있으면 다시 비활성화
checkCircle.style.backgroundColor = "grey";
document.getElementById(checkCircle.id + "item").innerHTML = "▶";
let x = document.getElementById(index);
checkCircle = x; // 바꿔치기
checkLineHeight = item; // 바꿔치지
checkCircle.style.backgroundColor = "purple"; // 정해진 색상으로만 활성화 되게
}
document.getElementById(index + "item").innerHTML = "■";
play(voice);
};
const play = (voice) => {
const audio = new Audio(URL.createObjectURL(voice));
audio.loop = false;
audio.volume = 1;
audio.play();
};
return (
<div>
<button
onClick={() => {
window.location.reload();
}}
>
새로고침
</button>
<div
style={{
width: "23%",
flexWrap: "wrap",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
{style.area.map((item, index) => {
return (
<p
key={item}
id={index}
style={{
width: `${item}px`,
height: `${item}px`,
backgroundColor: "grey",
borderRadius: "50%",
marginRight: `${style.gap[index]}px`,
marginBottom: `-${style.gap[index]}px`,
}}
onClick={() => {
changeColor3(index, voiceInfo[index], item);
}}
>
<div
id={`${index}item`}
style={{
width: `${item}px`,
height: `${item}px`,
lineHeight: `${item}px`,
}}
>
▶
</div>
</p>
);
})}
</div>
</div>
);
};
export default Bubble;
'항해 중 > 8-13주차 실전 프로젝트' 카테고리의 다른 글
[실전 프로젝트] 음원 재생 시 pause()가 동작 안하는 문제 (0) | 2021.12.30 |
---|---|
[실전 프로젝트] ASMR 페이지 구현 (0) | 2021.12.30 |
[실전 프로젝트] 달력 + 해당 월의 일수 계산하기 (+ fill : 특정 값으로 배열 채우기) (0) | 2021.12.28 |
[실전 프로젝트] 녹음, 녹음 파일 재생 + 보이스 페이지..? (0) | 2021.12.28 |
[실전 프로젝트] 푸쉬 알람 여부 (0) | 2021.12.27 |
댓글