CSS nth-child 이용해서 랜덤처럼 보이게 구현하기
사실 자바스크립트
를 이용하면 간단하게 랜덤을 구현할 수 있다.
하지만 아래 두 가지
를 만족하는 상태에서 각 요소에 대해 랜덤을 구현하는것은 까다롭다.
javscript 코드 변경 금지
요소가 얼마나 만들어질지 모름
아래는, 순수하게 css
만 이용하여 랜덤
하게 보이도록 구현
하는 방법이다.
1. 랜덤하게 보이도록 구현하기
우선 부모가
.container
, 자식이 모두.content
클래스명을 가지며, 자식이 얼마나 추가될지 모르는 상태로 가정
<div class="container">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>
아래 예시와 같이 scss에서 반복문으로 nth-child를 돌리면 랜덤이 가능하다.
하지만 요소의 최대 개수를 지정해줘야 하기 때문에, 이 값을 초과했을 경우 (예시에서는 11번째 요소부터) 랜덤이 적용되지 않는다.
@for $i from 1 through 10 {
.content:nth-child(#{$i}) {
background-color: rgb(random(256), random(256), random(256));
}
}
그렇다고 최대 개수를 너무 높게 지정하게 되면 컴파일된 css 파일의 크기가 커지기 때문에 비효율적이다.
이때 nth-child
의 함수형 표기법
을 이용하면 요소 개수만큼만 적용할 수 있는데, nth-child의 인자
로 숫자 대신 An+B
형식으로 넣어주면 된다.
예시 : [n -> 0, 1, 2 ...]
:nth-child(n)
: 0, 1, 2, 3, ...:nth-child(2n)
: 0, 2, 4, 6, ...:nth-child(3n+1)
: 1, 4, 7, 10, ...:nth-child(-2n+7)
: 7, 5, 3, 1:nth-child(n+3):nth-child(-n+6)
: 3, 4, 5, 6
요소 개수에 맞게 css가 적용이 되긴 하지만, 하나만 적용한다면 규칙적인 모습을 가지게 된다.
이를 해결하려면 n, 2n, 3n, ...
이렇게 여러개를 적용해주면 된다. 그 수가 많아질수록 더 랜덤해 보이는 결과가 나타난다.
.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(4n) {
background-color: black;
}
.content:nth-child(5n) {
background-color: orange;
}
.content:nth-child(6n) {
background-color: violet;
}
.content:nth-child(7n) {
background-color: cyan;
}
.content:nth-child(8n) {
background-color: yellow;
}
.content:nth-child(9n) {
background-color: gray;
}
.content:nth-child(10n) {
background-color: white;
}
이렇게 되면, nth-child 인자
들의 최소공배수 이후
에 패턴
이 반복
되게 된다.
1~10까지의 최소공배수, lcm(1,2,3,4,5,6,7,8,9,10)
-> 즉 2520번
이후 같은 패턴
이 반복되게 된다.
만약 아래와 같이 소수
를 활용한다면 같은 양의 코드로 패턴의 길이를 훨씬 늘릴 수 있다.
(또는 같은 길이의 패턴에 코드의 양을 줄일 수 있다.)
2. 소수를 사용하여 효율성 최대화
매미는 수명이 소수인 5, 7, 13, 17년으로, 천적과 마주칠 확률을 낮춘다고 한다.
- 소수와 자연수의 최소공배수는 두 수의 곱 (자연수가 해당 소수의 배수가 아닐 경우)
수명이 천적
4년
, 매미5년
이면, 최소20년
주기로 한 번씩 마주친다.
수명 이 천적
6년
, 매미17년
이면, 최소102년
주기로 한 번씩 마주친다.
또한 매미끼리도 서로 먹이 경쟁을 같이 할 확률을 낮춘다고 한다.
- 서로소일 경우, 최소공배수는 두수의 곱
매미1
5년
, 매미27년
이면, 최소35년
주기로 한 번씩 먹이 경쟁을 한다.
매미1
13년
, 매미217년
이면, 최소221년
주기로 한 번씩 먹이 경쟁을 한다.
이 원리를 css
에 적용하면, 같은 양의 코드에서 패턴의 길이를 최대로 가져갈 수 있다.
3. 코드
단순히 부모 요소인 .container
내부에 .content
자식을 반복문으로 추가하는 html, js 코드이다.
<div class="container"></div>
const container = document.querySelector('.container');
for (let i = 0; i < 1000; i++) {
const content = document.createElement('div');
content.classList.add('content');
container.appendChild(content);
}
nth-child
인자로 소수
를 사용한 css
코드이다.
.content {
float: left;
width: 13px;
height: 13px;
}
.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(5n) {
background-color: black;
}
.content:nth-child(7n) {
background-color: orange;
}
.content:nth-child(11n) {
background-color: violet;
}
.content:nth-child(13n) {
background-color: cyan;
}
.content:nth-child(17n) {
background-color: yellow;
}
.content:nth-child(19n) {
background-color: gray;
}
.content:nth-child(23n) {
background-color: white;
}
만약 scss
를 사용할 경우 아래와 같이 사용하면 되며, 색상을 랜덤하게 사용할 수도 있다.
.content {
float: left;
width: 13px;
height: 13px;
}
$prime_num: 1, 2, 3, 5, 7, 11, 13, 17, 19, 23;
@for $i from 1 through length($prime_num) {
.content:nth-child(#{nth($prime_num, $i)}n) {
background-color: rgb(random(256), random(256), random(256));
}
}
이 경우, 소수
는 모두 서로소
이기 때문에 최소공배수
를 구할때 모두 곱해
주면 된다.
lcm(1,2,3,5,7,11,13,17,19)
-> 즉 223,092,870번
이후 같은 패턴
이 반복되게 된다.
같은 10줄의 코드
라도 소수
를 활용하였을때 훨씬 효율적
이라는 것을 알 수 있다.
전체 코드
<!DOCTYPE html>
<html>
<head>
<title>css 랜덤 예제</title>
<meta charset="UTF-8" />
<style>
body {
background-color: lightgray;
}
.content {
float: left;
width: 10px;
height: 10px;
}
.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(5n) {
background-color: black;
}
.content:nth-child(7n) {
background-color: orange;
}
.content:nth-child(11n) {
background-color: violet;
}
.content:nth-child(13n) {
background-color: cyan;
}
.content:nth-child(17n) {
background-color: yellow;
}
.content:nth-child(19n) {
background-color: gray;
}
.content:nth-child(23n) {
background-color: white;
}
</style>
</head>
<body>
<div class="container"></div>
</body>
<script>
const container = document.querySelector('.container');
for (let i = 0; i < 1000; i++) {
const content = document.createElement('div');
content.classList.add('content');
container.appendChild(content);
}
</script>
</html>
실행 결과
function App() { const html = ` <!DOCTYPE html> <html> <head> <title>css 랜덤 예제</title> <meta charset="UTF-8" /> <style> body { background-color: lightgray; } .content { float: left; width: 11px; height: 11px; } .content:nth-child(n) { background-color: red; } .content:nth-child(2n) { background-color: green; } .content:nth-child(3n) { background-color: blue; } .content:nth-child(5n) { background-color: black; } .content:nth-child(7n) { background-color: orange; } .content:nth-child(11n) { background-color: violet; } .content:nth-child(13n) { background-color: cyan; } .content:nth-child(17n) { background-color: yellow; } .content:nth-child(19n) { background-color: gray; } .content:nth-child(23n) { background-color: white; } </style> </head> <body> <div class="container"></div> </body> <script> const container = document.querySelector(".container"); for (let i = 0; i < 4018; i++) { const content = document.createElement("div"); content.classList.add("content"); container.appendChild(content); } </script> </html> `; return ( <div> <iframe srcDoc={html} style={{ width: '100%', height: '35em' }}></iframe> </div> ); }
참고자료
- https://www.the215guys.com/learning/nth-child-cicada-principle/
- https://devopedia.org/cicada-principle
- https://developer.mozilla.org/ko/docs/Web/CSS/:nth-child
- https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ysuny2&logNo=20182947888
- https://www.sitepoint.com/the-cicada-principle-and-why-it-matters-to-web-designers/