프로젝트 하나를 끝내고, 이번엔 뭘 만들어볼까 하면 하던 중 javascript의 감을 올리기 위해서 시계 만들기에 도전했다. 디지털 시계도 좋지만 아날로그의 감성을 컴퓨터에서 느끼는 것도 좋지 않을까. 하는 마음에 한 번 시도해봤다.
30분이면 끝날 줄 알았는데, 이것저것 손대고 문제점 때문에 손 좀 대고 하니 시간이 꽤 걸렸다.
그래도 완성.
이 글을 본다면 필자는 초보자니 그냥 참고만 하시길
이미 만들고 글을 쓰는 거라 이미 완성된 것을 보고 회고식으로 작성한다.
우선 html파일과 css파일 js 파일을 만들고, html에 연결해주었다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<script src="clock.js"></script>
</body>
</html>
시계는 애쁠와치가 아니면 웬만한 시계는 원형으로 되어있다.
이것도 그래서 원형으로 만들어주었다.
view-body클래스 width와 height를 통해서 전체 화면이 내가 바라보는 사이즈임을 명시했다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="clock-container" id="clock_container">
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
vmin을 기준으로 시계틀을 잡았고, 나머지 또한 그러한 방식으로 했다.
background는 에메랄드색 시계를 만들어 보고 싶어서 저런식으로 그라데이션을 주면서 하는 방향으로 진행을 했다.
body{
margin: 0;
}
.view-body{
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.clock-container{
width: 60vmin;
height: 60vmin;
border: 1px solid #000000;
border-collapse: collapse;
border-radius: 30vmin;
position: relative;
background: radial-gradient(circle, #ffffff 0%, #3ed4be 100%);
}
이제 여기서 시간을 확인할 수 있는 표를 만들기 위해서 코드를 진행했다.
js파일에
clock_container를 찾아서 총 30개의 라인을 그어주었다.
const clock_container = document.getElementById("clock_container")
// 시간 표시 라인
for (let i = 0; i < 30; i++) {
const gradation = document.createElement('div')
gradation.classList.add('line')
gradation.style.transform = `rotate(${6 * i}deg)`
if (i % 5){
gradation.classList.add('thin')
}else{
gradation.classList.add('thick')
}
clock_container.appendChild(gradation)
}
css에서는 아래를 추가해주었다.
.line{
position: absolute;
width: 60vmin;
background: #000000;
z-index: 1;
}
.thin{
height: 0.25vmin;
top: calc(50% - 0.125vmin);
}
.thick{
height: 0.5vmin;
top: calc(50% - 0.25vmin);
z-index: 3;
}
시간표시선을 가리기 위해 위해 다시 div로 덮어주었다.
2개의 div로 가려줬으며, absolute와 top, left를 이용해서 중심을 잡고, 시간표시선을 지워주도록했다.
이 때, 큰 선은 내 취향대로 일부러 여기선 남겨놨다.
이 과정에서 z-index를 사용해서 뒤로 더 보낼 수도 있다.
<body>
<div class="view-body">
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
</div>
</div>
<script src="clock.js"></script>
</body>
css에서 z-index를 사용해 줬고, background는 이전과 마찬가지로 에메랄드 색상을 위해 이렇게 배경을 넣어봤다.(근데 에메랄드 색이 안나온다. 눈물)
.clock-body-add1{
width: 58vmin;
height: 58vmin;
border-radius: 29vmin;
position: absolute;
top: calc(50% - 29vmin);
left: calc(50% - 29vmin);
z-index: 3;
background: radial-gradient(circle, #ffffff 0%, #a4ebe0 100%);
}
.clock-body-add2{
width: 55vmin;
height: 55vmin;
border-radius: 27.5vmin;
position: absolute;
top: calc(50% - 27.5vmin);
left: calc(50% - 27.5vmin);
z-index: 3;
background: radial-gradient(circle, #ffffff 0%, #ecfffc 100%);
background: #ecfffc;
}
html은 아래처럼 해주었다.
clock-body에서 이제 시침과 분침, 초침을 집어넣을 것이고, 추가 기능도 넣어줄 것이다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="clock-body" id="clock">
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
그리고 css에서 clock-body를 시계의 주요 기능인 초침이 들어갈 수 있게 짰다.
.clock-body{
width: 52vmin;
height: 52vmin;
border-radius: 26vmin;
position: relative;
top: calc(50% - 26vmin);
left: calc(50% - 26vmin);
background: #ffffff;
z-index: 4;
}
이제 시계가 조금 더 시계처럼 보이기 위해 시간을 넣을 것이다. 그런데 난 로마자로 넣어봤다. 이유는 그게 더 머찌니까.
<div class="num-div"></div>
위 코드를 clock-container안에 추가해주었다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
사용할 css로 다음을 이용했다.
.num-div{
width: 50vmin;
height: 10%;
position: absolute;
top: calc(50% - 5%);
left: calc(50% - 25vmin);
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 700;
}
.font1{
font-size: calc(0.5rem + 2vmin);
font-weight: 700;
}
그리고 js에서
const clock = document.getElementById("clock")
로 clock을 선언해주었다.
로마자를 쓰기위해 rome_number 변수안에 입력해주고, 이를 for문을 통해 사용해주었다.
// clock-container안에 포함
// 로마자 시간 표시
const rome_number = ['Ⅰ','Ⅱ','Ⅲ','Ⅳ','Ⅴ','Ⅵ','Ⅶ','Ⅷ','Ⅸ','Ⅹ','Ⅺ','Ⅻ']
for (let j = 0; j < 6; j++) {
const number_div = document.createElement('div')
number_div.style.transform = `rotate(${30 * j + 120}deg)`
const span1 = document.createElement('span')
const span2 = document.createElement('span')
span1.innerText = rome_number[j]
span2.innerText = rome_number[6 + j]
span1.style.transform = `rotate(${-(30 * j + 120)}deg)`
span2.style.transform = `rotate(${-(30 * j + 120)}deg)`
number_div.appendChild(span1)
number_div.appendChild(span2)
number_div.classList.add('num-div')
number_div.classList.add('font1')
clock_container.appendChild(number_div)
}
이게 어떻게되어있는 거냐면 다음과 같다.
div태그에 span을 넣고 양쪽으로 위치를 잡아주도록 space-between을 이용했다. 그리고 이 div태그들을 각각의 시각에 맞도록 회전시켜준 것 되겠다.
그런데 이 때, 그냥 회전 시켜주면 그러니까
span1.style.transform =
rotate(${-(30 * j + 120)}deg)
span2.style.transform =rotate(${-(30 * j + 120)}deg)
위의 js코드중 이 코드가 없으면, 다음처럼 보이게 된다.
그래서 span태그는 돌아가면 안되니까 span태그에는 div를 회전시킨만큼 역회전시켜주었다.
이제 시계가 작동할 수 있도록, 시침, 분침, 초침, 그리고 가운데 고정핀도 시계다운 느낌을 위해 만들어주자.
html에 각각을 그려줄 div를 넣어주자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
<div class="clock-center"></div>
<div class="niddle hours" id="hours-time"></div>
<div class="niddle minutes" id="minutes-time"></div>
<div class="niddle seconds" id="seconds-time"></div>
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
그리고 그에 맞게 css를 그려주었다. 나는 12시가 기준이 되게 만들어서 height가 width보다 긴편이라 position: absolute;에서 left를 50%에서 나의 widht의 절반을빼주도록 했다.
또한 회전을 위해서 transform: rotate(몇deg);를 기입하는데 이때, transform-origin : 50% 100%;을 해주면 내가 어디를 기준으로 회전을 하게 될 지를 정할 수 있다. 자세한 것은 검색 ㄱㄱ
나의 경우 아까도 말했지만, 12시를 0도 기준으로 회전하기 때문에, 시침은 300deg, 분침은 60deg, 초침은 180deg를 향하도록 만들었다.
왜냐! 시계 국룰 10시 10분!
.clock-center{
background: #ffffff;
width: 3vmin;
height: 3vmin;
border: 1px solid #000000;
border-collapse: collapse;
border-radius: 1.5vmin;
position: absolute;
top: calc(50% - 1.5vmin);
left: calc(50% - 1.5vmin);
z-index: 6;
}
.niddle{
height: 30%;
width: 2%;
position: absolute;
left: calc(50% - 1%);
border-radius: 12px;
transition: transform 0.05s linear;
}
.hours{
top: calc(20%);
background: #000000;
z-index: 4;
transform-origin : 50% 100%;
transform: rotate(300deg);
}
.minutes{
height: 40%;
top: calc(10%);
background: #8c32e7;
z-index: 4;
transform-origin : 50% 100%;
transform: rotate(60deg);
}
.seconds{
height: 48%;
top: calc(2%);
width: 0.5%;
left: calc(50% - 0.25%);
background: #ff1d1d;
z-index: 5;
transform-origin : 50% 100%;
transform: rotate(180deg);
}
이제 하루 2번만 맞는 시계가 아닌 매초 맞는 시계로 수리해보자.
js파일에서 setInterval을 사용하자.
먼저 아래 코드를 수가해서 수리를 진행했다.
let hours_time = document.getElementById("hours-time")
let minutes_time = document.getElementById("minutes-time")
let seconds_time = document.getElementById("seconds-time")
// 이전 시간을 이용하기 위 등록
let previous_seconds_degree = 0
let previous_minutes_degree = 0
let previous_hours_degree = 0
// 이전 시간을 이용하기 위 등록
let previous_seconds_degree = 0
let previous_minutes_degree = 0
let previous_hours_degree = 0
이 이전 시간을 이용하기 위 등록이라 주석달린 코드는 59초에서 0초로 넘어갈때, 예시로 354deg에서 0deg로 넘어갈 때, 시계방향이 아닌 반시계방향으로 넘어가는 문제를 해결하기 위해 등록한 변수이다.
setClock = () => {
let current_time = new Date()
let hours = current_time.getHours()
let minutes = current_time.getMinutes()
let seconds = current_time.getSeconds()
let milliseconds = current_time.getMilliseconds()
// 1초당 6도 + 6도를 1000으로 나눈 만큼 milliseconds값 더해서 부드럽게.
let seconds_degree = 6 * seconds + (6/1000 * milliseconds)
// 1분당 6도 + 그리고 6도를 60초로 나눠서 0.1씩 초마다 더움직이게, 그리고 밀리초까지 계산
let minutes_degree = 6 * minutes + (0.1 * seconds) + (6 / 60 / 1000 * milliseconds)
// 1시간당 30도 + 그리고 30도를 60분으로 나눠서 0.5씩 분마다 더움직이게, 그리고 초와 밀리초까지 계산
let hours_degree = 30 * (hours % 12) + (0.5 * minutes) + (30 / 3600 * seconds) + (30 / 60 / 60 / 1000 * milliseconds)
seconds_time.style.transform = `rotate(${seconds_degree}deg)`
minutes_time.style.transform = `rotate(${minutes_degree}deg)`
hours_time.style.transform = `rotate(${hours_degree}deg)`
// Math.abs(현재 값 - 이전 값) > 180이면
// 예) 59초 -> 0초 이므로 이때 발생하는 반시계 방향 돌기 문제를 해결하기 위함
// 잠시 transition을 꺼놓고 이외에는 켜놓는 방식을 사용
if (Math.abs(seconds_degree - previous_seconds_degree) > 180) {
seconds_time.style.transition = 'none'
} else {
seconds_time.style.transition = 'transform 0.05s linear'
}
if (Math.abs(minutes_degree - previous_minutes_degree) > 180) {
minutes_time.style.transition = 'none'
} else {
minutes_time.style.transition = 'transform 0.05s linear'
}
if (Math.abs(hours_degree - previous_hours_degree) > 180) {
hours_time.style.transition = 'none'
} else {
hours_time.style.transition = 'transform 0.05s linear'
}
previous_seconds_degree = seconds_degree
previous_minutes_degree = minutes_degree
previous_hours_degree = hours_degree
}
setInterval(setClock, 50)
위 코드를 보면 알 수 있듯이 new Date()를 이용해서 현재의 시간, 분, 초, 밀리초까지 구해주었다.
난 여기서 조금 더 시계와 유사한 모습을 위해서 밀리초까지 포함해서 시침, 분침, 초침의 위치를 정할 수 있게 해주었다.
그 결과 아래와 같은 코드를 사용했다.
// 1초당 6도 + 6도를 1000으로 나눈 만큼 milliseconds값 더해서 부드럽게.
let seconds_degree = 6 * seconds + (6/1000 * milliseconds)
// 1분당 6도 + 그리고 6도를 60초로 나눠서 0.1씩 초마다 더움직이게, 그리고 밀리초까지 계산
let minutes_degree = 6 * minutes + (0.1 * seconds) + (6 / 60 / 1000 * milliseconds)
// 1시간당 30도 + 그리고 30도를 60분으로 나눠서 0.5씩 분마다 더움직이게, 그리고 초와 밀리초까지 계산
let hours_degree = 30 * (hours % 12) + (0.5 * minutes) + (30 / 3600 * seconds) + (30 / 60 / 60 / 1000 * milliseconds)
그리고 이것이 작동하는 것은 디지털이 아닌 아날로그이므로 각 침들이 회전을 해야하기에 .style.transform을 이용해서 rotate시켜주었다.
seconds_time.style.transform = `rotate(${seconds_degree}deg)`
minutes_time.style.transform = `rotate(${minutes_degree}deg)`
hours_time.style.transform = `rotate(${hours_degree}deg)`
그리고
setInterval(setClock, 50)
왜 1000이 아닌 50으로 했냐고 하면 좀 더 정밀함을 요구했기 때문이다. 사실 1000이나 500으로 해도 되지만, 코옴퓨타라고 항상 밀리초까지 완벽한 타이밍에 데이터를 제공해주진 않기에, 0.05초마다 확인하게하고 움직이게 했다. 사실 주기가 짤아서 그리 좋은 방식은 아니다.
이제 잘 작동하는 모습을 보여 준다.
기능은 잘 작동하지만 좀 더 멋쟁이 시계를 만들기 위해 기능을 추가했다.
사실 이건 시계가 아닌 html에 추가한거다.
<div class="date-container" id="date_container"></div>
를 추가해서 오늘 날짜를 추가했다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="date-container" id="date_container"></div>
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
<div class="clock-center"></div>
<div class="niddle hours" id="hours-time"></div>
<div class="niddle minutes" id="minutes-time"></div>
<div class="niddle seconds" id="seconds-time"></div>
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
css에는 아래 코드를 추가했다.
.date-container{
margin: 5vmin;
}
js에는 setClock 함수에 아래를 더 추가 해줬다.
// 날짜 갱신
let year = current_time.getFullYear()
let month = current_time.getMonth() + 1 // 월은 0부터 시작하므로 1을 더해줌
let date = current_time.getDate()
let day = current_time.getDay().toString()
const change_day_str = {
'1' : '월',
'2' : '화',
'3' : '수',
'4' : '목',
'5' : '금',
'6' : '토',
'0' : '일',
}
// 시계 위 날짜 표시를 위한 JS
const date_container = document.getElementById('date_container')
// date_container를 완전히 비워서기존 날짜를 지우고 새로 추가
date_container.innerHTML = ''
let input_date_container = [year, month, date, day]
for (let k = 0; k < 4; k++) {
const span = document.createElement('span')
if(k == 3){
span.innerText =`(${change_day_str[input_date_container[k]]})`
}else{
span.innerText =`${input_date_container[k]}.`
}
date_container.appendChild(span)
}
new Date()없이 바로 getFullYear()를 들어간 이유는 위에서 이미 손을 대지 않은 new Date()가 있기에 따로 추가하지 않았다.
유명한 시계들은 이니셜이 박혀있다.
나도 나만의 시계를 만들어보자.
로고를 넣은 html코드를 입력해주었다.
<div class="clock-logo font1">
<span>KBG</span>
</div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="date-container" id="date_container"></div>
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
<div class="clock-logo font1">
<span>KBG</span>
</div>
<div class="clock-center"></div>
<div class="niddle hours" id="hours-time"></div>
<div class="niddle minutes" id="minutes-time"></div>
<div class="niddle seconds" id="seconds-time"></div>
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
css에 아래 코드를 추가해줬다.
.clock-logo{
width: 100%;
text-align: center;
position: absolute;
top: 25%;
}
굳(근데 시간이 시간인지라 가려졌다.)
또 고오오급시계들을 보니 시계 안에 날짜가 표시되는 것을 알 수 있었다.(아님말고..)
그래서 시계안에 날짜를 표시할 수 있게도 진행해봤다.
부채꼴을 만드는 css가 참으로 쉽지 않았다.
우선 html에 아래를 추가해줬다.
<div class="inner-date" id="inner_date"></div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="date-container" id="date_container"></div>
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
<div class="inner-date" id="inner_date"></div>
<div class="clock-logo font1">
<span>KBG</span>
</div>
<div class="clock-center"></div>
<div class="niddle hours" id="hours-time"></div>
<div class="niddle minutes" id="minutes-time"></div>
<div class="niddle seconds" id="seconds-time"></div>
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
css에는 다음의 코드를 추가해주었다.
.inner-date-div는 js에서 추가해줄 것이다.
inner-date의
background: conic-gradient(transparent 60deg,rgba(218, 253, 248, 0.5) 60deg, rgba(218, 253, 248, 0.5) 120deg, transparent 120deg);
를 통해서 부채꼴 형태로 시계의 날짜를 볼 수 있도록 하는 배경을 만들었다.
.inner-date::before를 이용해서 다른 부분은 원래는 피자형태의 부채꼴이 되어야 하는 것을 도넛형태의 부채꼴이 되도록 눈속임을 해주었다.
즉 배경을 안보이게 해버렸다는 뜻.
이 부채꼴에서 나는 전날, 오늘, 내일 날짜를 볼 수 있게 할 것이다.
.inner-date {
width: 40vmin;
height: 40vmin;
position: relative;
top: calc(50% - 20vmin);
left: calc(50% - 20vmin);
border-radius: 50%;
background: conic-gradient(transparent 60deg,rgba(218, 253, 248, 0.5) 60deg, rgba(218, 253, 248, 0.5) 120deg, transparent 120deg);
}
.inner-date::before {
content: '';
width: 30vmin;
height: 30vmin;
background: white;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.inner-date-div{
position: absolute;
width: 37vmin;
top: calc(50% - 0.5rem);
left: calc(50% - 18.5vmin);
display: flex;
justify-content : flex-end;
align-items : center;
}
이제 그림은 그려졌으니, 오늘 날짜가 보이게 코드를 작성해주자.
JS의 setClock함수에 다음을 추가해주면 된다. 이유는 내일인지 오늘인지 컴퓨터는 모르니까 0.05초마다 시계 확인할 때마다 날짜도 확인해줘야해서 그렇다.
// 시계 안에 날짜를 표시하는 JS
const inner_date = document.getElementById('inner_date')
// inner_date 완전히 비워서기존 날짜를 지우고 새로 추가
inner_date.innerHTML = ''
// 지난 달의 마지막 날, 이번 달의 마지막 날 구하는 코드
// 이번 달의 마지막날
let closing_d = new Date()
closing_d= new Date(closing_d.getFullYear(), closing_d.getMonth()+1, 0)
// console.log(closing_d.toLocaleDateString()) // 이번달의 마지막날
let closing_date = closing_d.getDate()
// 지난 달의 마지막날
let pre_d = new Date()
let pre_closing_d = new Date(pre_d.getFullYear(), pre_d.getMonth(), 0)
// console.log(pre_closing_d.toLocaleDateString()) // 이전 달의 마지막 날
let pre_closing_date = pre_closing_d.getDate()
for (let l = 0; l < 3; l++) {
// date가 이번 달의 첫날이면, 전 날은 이전 달의 마지막 날
let write_date
if(date == 1 && l == 0){
write_date = pre_closing_date
}else if(date == closing_date && l == 2){
write_date = 1
}else{
write_date = date + l - 1
}
const div = document.createElement('div')
div.classList.add('inner-date-div')
div.classList.add('font2')
const span = document.createElement('span')
span.innerText = `${write_date}`
div.appendChild(span)
div.style.transform = `rotate(${l * 20 - 20}deg)`
inner_date.appendChild(div)
}
JS에서
// 이번 달의 마지막날
let closing_d = new Date()
closing_d= new Date(closing_d.getFullYear(), closing_d.getMonth()+1, 0)
// console.log(closing_d.toLocaleDateString()) // 이번달의 마지막날
let closing_date = closing_d.getDate()
// 지난 달의 마지막날
let pre_d = new Date()
let pre_closing_d = new Date(pre_d.getFullYear(), pre_d.getMonth(), 0)
// console.log(pre_closing_d.toLocaleDateString()) // 이전 달의 마지막 날
let pre_closing_date = pre_closing_d.getDate()
이번 달의 마지막날과 지난 달의 마지막 날을 구해서, 이를 이용해 오늘이 1일 또는 이번 달의 마지막일 경우에 전날과 다음날의 이상해지는 경우를 방지하였다.
마침 글을 작성하는 시점이 25일에서 26일로 넘어가는 시점이라 잘 작동하는지 확인했는데, 아주 잘 작동하는 것을 확인할 수 있었다.
이제 나 만의 시계 완성이다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>아날로그 시계</title>
<link rel="stylesheet" href="clock.css" />
</head>
<body>
<div class="view-body">
<div class="date-container" id="date_container"></div>
<div class="clock-container" id="clock_container">
<div class="clock-body-add1"></div>
<div class="clock-body-add2"></div>
<div class="num-div"></div>
<div class="clock-body" id="clock">
<div class="inner-date" id="inner_date"></div>
<div class="clock-logo font1">
<span>KBG</span>
</div>
<div class="clock-center"></div>
<div class="niddle hours" id="hours-time"></div>
<div class="niddle minutes" id="minutes-time"></div>
<div class="niddle seconds" id="seconds-time"></div>
</div>
</div>
</div>
<script src="clock.js"></script>
</body>
</html>
body{
margin: 0;
}
.view-body{
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.clock-container{
width: 60vmin;
height: 60vmin;
border: 1px solid #000000;
border-collapse: collapse;
border-radius: 30vmin;
position: relative;
background: radial-gradient(circle, #ffffff 0%, #3ed4be 100%);
}
.line{
position: absolute;
width: 60vmin;
background: #000000;
z-index: 1;
}
.thin{
height: 0.25vmin;
top: calc(50% - 0.125vmin);
}
.thick{
height: 0.5vmin;
top: calc(50% - 0.25vmin);
z-index: 3;
}
.clock-body-add1{
width: 58vmin;
height: 58vmin;
border-radius: 29vmin;
position: absolute;
top: calc(50% - 29vmin);
left: calc(50% - 29vmin);
z-index: 3;
background: radial-gradient(circle, #ffffff 0%, #a4ebe0 100%);
}
.clock-body-add2{
width: 55vmin;
height: 55vmin;
border-radius: 27.5vmin;
position: absolute;
top: calc(50% - 27.5vmin);
left: calc(50% - 27.5vmin);
z-index: 3;
background: radial-gradient(circle, #ffffff 0%, #ecfffc 100%);
background: #ecfffc;
}
.clock-body{
width: 52vmin;
height: 52vmin;
border-radius: 26vmin;
position: relative;
top: calc(50% - 26vmin);
left: calc(50% - 26vmin);
background: #ffffff;
z-index: 4;
}
.num-div{
width: 50vmin;
height: 10%;
position: absolute;
top: calc(50% - 5%);
left: calc(50% - 25vmin);
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 700;
}
.font1{
font-size: calc(0.5rem + 2vmin);
font-weight: 700;
}
.clock-center{
background: #ffffff;
width: 3vmin;
height: 3vmin;
border: 1px solid #000000;
border-collapse: collapse;
border-radius: 1.5vmin;
position: absolute;
top: calc(50% - 1.5vmin);
left: calc(50% - 1.5vmin);
z-index: 6;
}
.niddle{
height: 30%;
width: 2%;
position: absolute;
left: calc(50% - 1%);
border-radius: 12px;
transition: transform 0.05s linear;
}
.hours{
top: calc(20%);
background: #000000;
z-index: 4;
transform-origin : 50% 100%;
transform: rotate(300deg);
}
.minutes{
height: 40%;
top: calc(10%);
background: #8c32e7;
z-index: 4;
transform-origin : 50% 100%;
transform: rotate(60deg);
}
.seconds{
height: 48%;
top: calc(2%);
width: 0.5%;
left: calc(50% - 0.25%);
background: #ff1d1d;
z-index: 5;
transform-origin : 50% 100%;
transform: rotate(180deg);
}
.date-container{
margin: 5vmin;
}
.clock-logo{
width: 100%;
text-align: center;
position: absolute;
top: 25%;
}
.inner-date {
width: 40vmin;
height: 40vmin;
position: relative;
top: calc(50% - 20vmin);
left: calc(50% - 20vmin);
border-radius: 50%;
background: conic-gradient(transparent 60deg,rgba(218, 253, 248, 0.5) 60deg, rgba(218, 253, 248, 0.5) 120deg, transparent 120deg);
}
.inner-date::before {
content: '';
width: 30vmin;
height: 30vmin;
background: white;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.inner-date-div{
position: absolute;
width: 37vmin;
top: calc(50% - 0.5rem);
left: calc(50% - 18.5vmin);
display: flex;
justify-content : flex-end;
align-items : center;
}
const clock_container = document.getElementById("clock_container")
const clock = document.getElementById("clock")
// 시간 표시 라인
for (let i = 0; i < 30; i++) {
const gradation = document.createElement('div')
gradation.classList.add('line')
gradation.style.transform = `rotate(${6 * i}deg)`
if (i % 5){
gradation.classList.add('thin')
}else{
gradation.classList.add('thick')
}
clock_container.appendChild(gradation)
}
// clock-container안에 포함
// 로마자 시간 표시
const rome_number = ['Ⅰ','Ⅱ','Ⅲ','Ⅳ','Ⅴ','Ⅵ','Ⅶ','Ⅷ','Ⅸ','Ⅹ','Ⅺ','Ⅻ']
for (let j = 0; j < 6; j++) {
const number_div = document.createElement('div')
number_div.style.transform = `rotate(${30 * j + 120}deg)`
const span1 = document.createElement('span')
const span2 = document.createElement('span')
span1.innerText = rome_number[j]
span2.innerText = rome_number[6 + j]
span1.style.transform = `rotate(${-(30 * j + 120)}deg)`
span2.style.transform = `rotate(${-(30 * j + 120)}deg)`
number_div.appendChild(span1)
number_div.appendChild(span2)
number_div.classList.add('num-div')
number_div.classList.add('font1')
clock_container.appendChild(number_div)
}
let hours_time = document.getElementById("hours-time")
let minutes_time = document.getElementById("minutes-time")
let seconds_time = document.getElementById("seconds-time")
// 이전 시간을 이용하기 위 등록
let previous_seconds_degree = 0
let previous_minutes_degree = 0
let previous_hours_degree = 0
setClock = () => {
let current_time = new Date()
let hours = current_time.getHours()
let minutes = current_time.getMinutes()
let seconds = current_time.getSeconds()
let milliseconds = current_time.getMilliseconds()
// 1초당 6도 + 6도를 1000으로 나눈 만큼 milliseconds값 더해서 부드럽게.
let seconds_degree = 6 * seconds + (6/1000 * milliseconds)
// 1분당 6도 + 그리고 6도를 60초로 나눠서 0.1씩 초마다 더움직이게, 그리고 밀리초까지 계산
let minutes_degree = 6 * minutes + (0.1 * seconds) + (6 / 60 / 1000 * milliseconds)
// 1시간당 30도 + 그리고 30도를 60분으로 나눠서 0.5씩 분마다 더움직이게, 그리고 초와 밀리초까지 계산
let hours_degree = 30 * (hours % 12) + (0.5 * minutes) + (30 / 3600 * seconds) + (30 / 60 / 60 / 1000 * milliseconds)
seconds_time.style.transform = `rotate(${seconds_degree}deg)`;
minutes_time.style.transform = `rotate(${minutes_degree}deg)`
hours_time.style.transform = `rotate(${hours_degree}deg)`
// Math.abs(현재 값 - 이전 값) > 180이면
// 예) 59초 -> 0초 이므로 이때 발생하는 반시계 방향 돌기 문제를 해결하기 위함
// 잠시 transition을 꺼놓고 이외에는 켜놓는 방식을 사용
if (Math.abs(seconds_degree - previous_seconds_degree) > 180) {
seconds_time.style.transition = 'none'
} else {
seconds_time.style.transition = 'transform 0.05s linear'
}
if (Math.abs(minutes_degree - previous_minutes_degree) > 180) {
minutes_time.style.transition = 'none'
} else {
minutes_time.style.transition = 'transform 0.05s linear'
}
if (Math.abs(hours_degree - previous_hours_degree) > 180) {
hours_time.style.transition = 'none'
} else {
hours_time.style.transition = 'transform 0.05s linear'
}
previous_seconds_degree = seconds_degree
previous_minutes_degree = minutes_degree
previous_hours_degree = hours_degree
// 날짜 갱신
let year = current_time.getFullYear()
let month = current_time.getMonth() + 1 // 월은 0부터 시작하므로 1을 더해줌
let date = current_time.getDate()
let day = current_time.getDay().toString()
const change_day_str = {
'1' : '월',
'2' : '화',
'3' : '수',
'4' : '목',
'5' : '금',
'6' : '토',
'0' : '일',
}
// 시계 위 날짜 표시를 위한 JS
const date_container = document.getElementById('date_container')
// date_container를 완전히 비워서기존 날짜를 지우고 새로 추가
date_container.innerHTML = ''
let input_date_container = [year, month, date, day]
for (let k = 0; k < 4; k++) {
const span = document.createElement('span')
if(k == 3){
span.innerText =`(${change_day_str[input_date_container[k]]})`
}else{
span.innerText =`${input_date_container[k]}.`
}
date_container.appendChild(span)
}
// 시계 안에 날짜를 표시하는 JS
const inner_date = document.getElementById('inner_date')
// inner_date 완전히 비워서기존 날짜를 지우고 새로 추가
inner_date.innerHTML = ''
// 지난 달의 마지막 날, 이번 달의 마지막 날 구하는 코드
// 이번 달의 마지막날
let closing_d = new Date()
closing_d= new Date(closing_d.getFullYear(), closing_d.getMonth()+1, 0)
// console.log(closing_d.toLocaleDateString()) // 이번달의 마지막날
let closing_date = closing_d.getDate()
// 지난 달의 마지막날
let pre_d = new Date()
let pre_closing_d = new Date(pre_d.getFullYear(), pre_d.getMonth(), 0)
// console.log(pre_closing_d.toLocaleDateString()) // 이전 달의 마지막 날
let pre_closing_date = pre_closing_d.getDate()
for (let l = 0; l < 3; l++) {
// date가 이번 달의 첫날이면, 전 날은 이전 달의 마지막 날
let write_date
if(date == 1 && l == 0){
write_date = pre_closing_date
}else if(date == closing_date && l == 2){
write_date = 1
}else{
write_date = date + l - 1
}
const div = document.createElement('div')
div.classList.add('inner-date-div')
div.classList.add('font2')
const span = document.createElement('span')
span.innerText = `${write_date}`
div.appendChild(span)
div.style.transform = `rotate(${l * 20 - 20}deg)`
inner_date.appendChild(div)
}
}
setInterval(setClock, 50)
다음엔 뭘 만들어 볼까나