Marquee
Marquee란
marqee는 텐트나 천막을 뜻하는 옛 프랑스어인 marquise에서 유래했습니다.
(출처: Google Search)
marquee는 영어로 차용된 이후 공연장이나 극장 앞에 설치된 큰 천막이나 간판을 의미하게 되었습니다.
marquee중 일부는 사람들의 주의를 끌기 위해 수동 또는 자동으로 움직이는 텍스트를 포함했는데,
이러한 이유로 marquee라는 단어가 HTML 태그 이름으로 채택되었습니다.
Marquee Tag
HTML 태그 <marquee>
는 텍스트나 이미지를 수평 또는 수직으로 움직이게 하는 데 사용되었습니다.
현재는 HTML 표준에서 제외되었기 때문에 직접 구현해야 합니다.
구현
marquee는 다양한 방법으로 구현할 수 있고, 그 중 한 가지 방법을 소개합니다.
우선, 움직임을 표현할 리스트에 애니메이션을 적용해 보겠습니다.
<div class="marquee">
<ul class="marquee_list">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
</div>
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.marquee {
display: flex;
width: 500px;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
animation: roll 10s linear infinite;
}
.marquee_item {
list-style: none;
width: 180px;
height: 100px;
background-color: lightblue;
margin-right: 10px;
}
- 1
- 2
- 3
- 4
- 5
잘 움직이긴 하지만, 연속성이 없어 부자연스러워 보입니다.
목록을 복사해서 뒤에 붙여넣어주면 자연스럽게 보일 것 같습니다.
<div class="marquee">
<ul class="marquee_list">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
<ul class="marquee_list copy">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
</div>
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.marquee {
display: flex;
width: 500px;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
animation: roll 10s linear infinite;
}
.marquee_item {
list-style: none;
width: 180px;
height: 100px;
background-color: lightblue;
margin-right: 10px;
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
이해를 위해 복사된 영역은 색상을 다르게 해보겠습니다.
원본 영역과 복사 영역이 모두 본래 자리에서 100%만큼 이동한 후에
원래 자리로 회귀하는 동작을 반복하고,
우리의 눈에는 계속해서 이동하는 것처럼 보이게 됩니다.
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
하지만 목록의 크기가 컨테이너보다 작을 경우에는 여전히 부자연스럽습니다.
- 1
- 2
- 1
- 2
요구사항에 따라 달라지겠지만
여기서는 목록의 크기가 컨테이너보다 작을 경우에 애니메이션을 동작하지 않도록 막고 싶습니다.
요소를 선택적으로 제어하기 위해, js
를 통해 목록의 복사가 이루어지도록 수정합니다.
<div id="marquee" class="marquee">
<ul id="marquee_list" class="marquee_list">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
</div>
const container = document.getElementById("marquee");
const marqueeList = document.getElementById("marquee_list");
let copy = marqueeList.cloneNode(true);
copy.id = "marquee_list_copy";
container.appendChild(copy);
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.marquee {
display: flex;
width: 500px;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
animation: roll 10s linear infinite;
}
.marquee_item {
list-style: none;
width: 180px;
height: 100px;
background-color: lightblue;
margin-right: 10px;
}
이제, 조건에 따라 목록을 복사하거나 애니메이션의 실행을 막도록 코드를 수정합니다.
<div id="marquee" class="marquee">
<ul id="marquee_list" class="marquee_list">
<ul id="marquee_list" class="marquee_list active">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
</div>
const container = document.getElementById("marquee");
const marqueeList = document.getElementById("marquee_list");
const containerWidth = container.offsetWidth;
const listWidth = marqueeList.offsetWidth;
if (containerWidth < listWidth) {
let copy = marqueeList.cloneNode(true);
copy.id = "marquee_list_copy";
container.appendChild(copy);
} else {
marqueeList.classList.remove("active");
}
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.marquee {
display: flex;
width: 500px;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
}
.marquee_list.active {
animation: roll 10s linear infinite;
}
.marquee_item {
list-style: none;
width: 180px;
height: 100px;
background-color: lightblue;
margin-right: 10px;
}
다음과 같이 컨테이너의 크기와 목록의 크기에 따라 애니메이션을 실행하게 되었습니다.
HTML Code
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Marquee</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div class="container">
<div id="marquee" class="marquee">
<ul id="marquee_list" class="marquee_list active">
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
<li class="marquee_item"></li>
</ul>
</div>
</div>
<script src="./index.js"></script>
</body>
</html>
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.marquee {
display: flex;
width: 500px;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
}
.marquee_list.active {
animation: roll 10s linear infinite;
}
.marquee_item {
list-style: none;
width: 180px;
height: 100px;
background-color: lightblue;
margin-top: 0 !important;
margin-right: 10px;
}
const container = document.getElementById("marquee");
const marqueeList = document.getElementById("marquee_list");
const containerWidth = container.offsetWidth;
const listWidth = marqueeList.offsetWidth;
if (containerWidth < listWidth) {
let copy = marqueeList.cloneNode(true);
copy.id = "marquee_list_copy";
container.appendChild(copy);
} else {
marqueeList.classList.remove("active");
}
Vue Code
BaseMarquee.vue
<script setup>
import { ref, computed, onMounted } from "vue";
const props = defineProps({
duration: {
type: String,
default: "10s",
},
});
const container = ref();
const list = ref();
const copy = ref(false);
const isOverflowingContainer = computed(() => {
return container.value.offsetWidth < list.value.offsetWidth;
});
onMounted(() => {
if (isOverflowingContainer.value) {
copy.value = true;
}
});
</script>
<template>
<div class="marquee" ref="container">
<div
ref="list"
:class="{ marquee_list: true, active: copy }"
:style="{ animationDuration: duration }"
>
<slot class="marquee_item"></slot>
</div>
<div
v-if="copy"
class="marquee_list copy active"
:style="{ animationDuration: duration }"
>
<slot class="marquee_item"></slot>
</div>
</div>
</template>
<style scoped>
.marquee {
width: 100%;
display: flex;
overflow: hidden;
}
.marquee_list {
display: flex;
padding: 0 !important;
}
.marquee_list.active {
animation: roll 0s linear infinite;
}
@keyframes roll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
</style>