스켈레톤 UI
실제 데이터가 렌더링 되기 전에 보이게 될 화면의 윤곽을 먼저 그려주는 로딩 애니메이션
사용자의 이탈을 막고, ‘어떤 것들이 보여질 것이다’라고 미리 알려주는 효과
Loader.vue
<template>
<div
:style="{
width: `${size}rem`,
height: `${size}rem`,
zIndex
}"
:class="{ absolute, fixed }"
class="spinner-border text-primary"></div>
</template>
<script>
export default {
props: {
size: {
type: Number,
default: 2
},
absolute: {
type: Boolean,
default: false
},
fixed: {
type: Boolean,
default: false
},
zIndex: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss" scoped>
.spinner-border {
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
&.absolute {
position: absolute;
}
&.fixed {
position: fixed;
}
}
</style>
Movie.vue
<template>
<div class="container">
<template v-if="loading">
<div class="skeletons">
<div class="skeleton poster"></div>
<div class="specs">
<div class="skeleton title"></div>
<div class="skeleton spec"></div>
<div class="skeleton plot"></div>
<div class="skeleton etc"></div>
<div class="skeleton etc"></div>
<div class="skeleton etc"></div>
</div>
</div>
<Loader
:size="3"
:z-index="9"
fixed />
</template>
<div
v-else
class="movie-details">
<div
:style="{ backgroundImage: `url(${requestDiffSizeImage(theMovie.Poster)})` }"
class="poster"></div>
<div class="specs">
<div class="title">
{{ theMovie.Title }}
</div>
<div class="labels">
{{ theMovie.Released }}
{{ theMovie.Runtime }}
{{ theMovie.Country }}
</div>
<div class="plot">
{{ theMovie.Plot }}
</div>
<div class="ratings">
<h3>Ratings</h3>
<div class="rating-wrap">
<div class="rating">
<div
v-for="{ Source: name, Value: score } in theMovie.Ratings"
:key="name"
:title="name"
class="rating">
<img
:src="`https://raw.githubusercontent.com/ParkYoungWoong/vue3-movie-app/master/src/assets/${name}.png`"
:alt="name" />
<span>{{ score }}</span>
</div>
</div>
</div>
</div>
<div>
<h3>Actors</h3>
{{ theMovie.Actors }}
</div>
<div>
<h3>Director</h3>
{{ theMovie.Director }}
</div>
<div>
<h3>Production</h3>
{{ theMovie.Production }}
</div>
<div>
<h3>Genre</h3>
{{ theMovie.Genre }}
</div>
</div>
</div>
</div>
</template>
<script>
import Loader from '~/components/Loader';
export default {
components: {
Loader
},
computed: {
theMovie() {
return this.$store.state.movie.theMovie
},
loading() {
return this.$store.state.movie.loading
}
},
created() {
this.$store.dispatch('movie/searchMovieWithId', {
id: this.$route.params.id
})
},
methods: {
requestDiffSizeImage(url, size = 700) {
return url.replace('SX300', `SX${size}`)
}
}
}
</script>
<style lang="scss" scoped>
@import '~/scss/main';
.container {
padding-top: 40px;
}
.skeletons {
display: flex;
.poster {
flex-shrink: 0;
width: 500px;
height: 500px * calc(3 / 2);
margin-right: 70px;
}
.specs {
flex-grow: 1;
}
.skeleton {
border-radius: 10px;
background-color: $gray-200;
&.title {
width: 80%;
height: 70px;
}
&.spec {
width: 60%;
height: 30px;
margin-top: 20px;
}
&.plot {
width: 100%;
height: 250px;
margin-top: 20px;
}
&.etc {
width: 50%;
height: 50px;
margin-top: 20px;
}
}
}
.movie-details {
display: flex;
color: $gray-600;
.poster {
flex-shrink: 0;
width: 500px;
height: 500px * calc(3 / 2);
margin-right: 70px;
border-radius: 10px;
background-color: $gray-200;
background-size: cover;
background-position: center;
}
.specs {
flex-grow: 1;
.title {
color: $black;
font-family: 'Oswald', sans-serif;
font-size: 70px;
line-height: 1;
margin-bottom: 30px;
}
.labels {
color: $primary;
span {
&::after {
content: "\00b7";
margin: 0 6px;
}
&:last-child::after {
display: none;
}
}
}
.plot {
margin-top: 20px;
}
.ratings {
.rating-wrap {
display: flex;
.rating {
display: flex;
align-items: center;
margin-right: 32px;
img {
height: 30px;
flex-shrink: 0;
margin-right: 6px;
}
}
}
}
h3 {
margin: 24px 0 6px;
color: $black;
font-family: "Oswald", sans-serif;
font-size: 20px;
}
}
}
</style>
'영화 검색 사이트' 카테고리의 다른 글
[영화 검색 사이트] Vue Router 404 Page Not Found (0) | 2023.02.11 |
---|---|
[영화 검색 사이트] Vue 플러그인 (0) | 2023.02.11 |
[영화 검색 사이트] 로딩 애니메이션, Footer (0) | 2023.02.11 |
[영화 검색 사이트] 텍스트 말줄임 표시 (0) | 2023.02.11 |
[영화 검색 사이트] VUEX(STORE) (0) | 2023.02.10 |