
스크롤이 되었을 때 화면에 고정되는 요소를 만들고 싶을 때 사용할 수 있는 CSS 속성이다.
CSS - Position: Sticky 를 잘 적용 하려면?
(Edge 이상에서 사용가능)
position : fixed는 항상 화면에 고정이 되는 요소를 만들 때 사용한다고 배웠었는데 이거랑 뭔 차이가 있냐면,
position : sticky 는 스크롤이 되어서 이 요소가 화면에 나오면 고정시킨다는 특성이 있다.
한번 예제를 만들어 활용해보자
<body style="background: grey; height: 3000px">
<div class="grey">
<div class="image">
<img src="./assets/파워퍼프걸 썬구리.JPG" width="100%" />
</div>
<div style="clear: both"></div>
<div class="text">Meet the first Triple Camera System</div>
</div>
</body>
.grey {
background: lightgrey;
height: 2000px;
margin-top: 500px;
}
.text {
float: left;
width: 300px;
}
.image {
float: right;
width: 400px;
position: sticky;
top: 100px;
}
이렇게 작성하면 검고 긴 화면에 텍스트와 이미지가 하나씩 보인다.
이미지에 position : sticky를 주면
1. 스크롤이 되어서 이미지가 보이는 순간
2. viewport의 맨 위에서부터 100px 위치에서 고정이 된다.
3. 그리고 부모 박스를 넘어서 스크롤 되면 이미지도 같이 사라진다.

(주의점)
position : sticky는
1. 스크롤을 할 만한 부모 박스가 있어야하고
2.top등 좌표속성과 함께 써야 제대로 보인다.
응용하면 남들과는 다른 레이아웃을 만들 수 있다.

우리는 위와 같은 UI를 만들어볼 예정이다!
index파일에 준비된 HTML과 CSS를 붙여넣을 것이다.
<!DOCTYPE html>
<html lang="en">
<head>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="./index.css" />
<title>Level3</title>
</head>
<body>
<div class="card-background">
<div class="card-box">
<img src="카드이미지1경로" />
</div>
<div class="card-box">
<img src="카드이미지2경로" />
</div>
<div class="card-box">
<img src="카드이미지3경로" />
</div>
</div>
<script src="./index.js"></script>
</body>
</html>
.card-background {
height: 3000px;
margin-top: 800px;
margin-bottom: 1600px;
}
.card-box img {
display: block;
margin: auto;
max-width: 80%;
}
.card-box {
position: sticky;
top: 400px;
margin-top: 200px;
}
긴 배경에 카드를 세로로 3개 배치했고,
position : sticky를 이용해서 스크롤시 화면에 고정되게 만들었다.
여기까지만 해도 나름 봐줄만 하다.
이제 예제처럼 스크롤시 opacity가 점점 줄어드는 애니메이션도 추가해보자.

(일단 예제를 보면 스크롤을 내릴 때 카드가
1. opacity가 변하고
2. 사이즈가 줄어든다. 일단 1번 opacity만 구현해보도록 하자)
여태까지 한 애니메이션은 one-way 애니메이션이었다. (one-way 애니메이션이 기억이 나질 않는다면 아래사이트 참고)
one-way 애니메이션 1
one-way 애니메이션 2
그냥 시작화면과 최종화면만 있었을 뿐인데, 지금은 스크롤 위치에 따라 opacity가 0에서 1까지 무수히 많은 중간단계가 존재한다.
이런 애니메이션을 어떻게 만들지 코드로 짜보도록 해보자.
그 전에 일단 아래 자바스크립트를 빨리 작성해보자.
window.addEventListener("scroll", function() {
let height = window.scrollY
console.log(height);
});
스크롤 애니메이션의 기본은 위와 같은 자바스크립트이다.
많이 보던 "스크롤시 내부 코드를 실행해주세요~" 라는 코드이다.
내부 코드엔 현재 창의 스크롤바 높이를 알려주는 window.scrollY;; 이라는 함수를 써본 뒤에
콘솔창에 출력을 해보았다.

현재 창의 스크롤바 높이가 콘솔에 출력된다.
왜 현재 창의 스크롤바 높이를 출력할까?
왜냐면 현재 스크롤바 높이에 따라
opacity가 변하니까
스크롤바가 어디까지 스크롤 되면opacity가0이고
어디까지 스크롤 되면opacity가1인지 찾고 싶어서 출력해본 것이다.
650px 쯤 스크롤하면 opacity를 1로,
900px 쯤 스크롤하면 opacity를 대충 0.5로,
1150px 쯤 스크롤하면 opacity를 0으로 설정하면 좋을 것 같다.
이걸 코드로 표현해보자.
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
// 650~1150까지 스크롤바를 내리면,
// 첫째카드의 opacity 1~0으로 서서히변경해야함
cardBox[0].style.opacity = ???
});
첫카드의 opacity를 ???로 변하게 해야되는데 ???는 0 이런 고정된 값으로 설정할 수 없을 것 같다.
???부분은
"스크롤바높이가 650~1150이 될 때 1~0이 되는 가변적인 값"이 되어야할 것 같다.
그래서 일단 미지의 변수인 y라고 표현해두자.
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
let y = ???
// 650~1150까지 스크롤바를 내리면,
// 첫째카드의 opacity 1~0으로 서서히변경해야함
cardBox[0].style.opacity = y
});
y구하는 법
- "스크롤바높이가 650~1150이 될 때 1~0이 되는 가변적인 값"을 구하면 된다.
그래프로 표현해보면 이렇다.
그림으로 표현해보면 이럴 것이다.
높이가 650~1150 이렇게 변할 때 1에서 0이 되는건 저런 그래프로 표현할 수 있을 것 같다.
(여기서 높이라는건 아까 출력해본 높이 변수이다)
이걸 수학용어로 치면1차함수라고 부른다.
- 그럼 이걸 수식으로 표현해보자.
//수학 y = a * 높이 + b
y에 대한1차함수는 우리가 뭐 기울기 이런걸 모를 때 일단ax+b이런 식으로 표현할 수 있다고 배웠다.
그럼a랑b라는미지수만 잘 구하면y가 뭔지 알 수 있는 것이다.
a는 전문용어로기울기,b는 전문용어로y절편이라고 한다.
그건 너무 어려우니 우리는 대입법을 이용해보도록하자.
3. a,b를 구하는건 대입법을 이용한다.
위의 함수는
높이가650일 때y가1,
높이가1150일 때y가0이다.//수학 1 = a * 650 + b 0 = a * 1150 + b그럼 이렇게 대입해봐도 맞는 것 같다.
방정식이 두개고 미지수가 두개면 충분히 풀 수 있다.
그래서 연습장에 방정식을 잘 풀어보면//수학 1 = a * 650 + b 0 = a * 1150 + b a = -1/500 b = 115/50이렇게 나온다.
그럼a와b값을 아까y를 표현하던 수식에 대입하면//다시 자바스크립트 let y = -1/500 * 높이 + 115/50이렇게 된다.
이제 y를 구했으니 아까 코드를 이렇게 업데이트 할 수 있을 것 같다.
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
let y = (-1 / 500) * height + 115 / 50;
// 650~1150까지 스크롤바를 내리면,
// 첫째카드의 opacity 1~0으로 서서히변경해야함
cardBox[0].style.opacity = y;
});

스크롤바 내려보시면 opacity라는 값이 y에 의해서 결정됩니다.
y는 스크롤 높이가 변할 때마다 매번 바뀌기 때문에
아까와 같은 UI를 구현가능한 것이다.
카드 1만 투명도를 씌우지 않고 전체 다 씌우기
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
let y = (-1 / 500) * height + 115 / 50;
// 650~1150까지 스크롤바를 내리면,
// 첫째카드의 opacity 1~0으로 서서히변경해야함
cardBox.forEach((a, i) => {
cardBox[i].style.opacity = y;
});
});
forEach를 사용해서 순환하면서 투명도를 씌울 수 있도록 하였다.
투명도 뿐만 아니라 카드가 0.9배 정도로 서서히 작아지는 것도 똑같이 구현해봅시다.
지금까지 카드 투명도가 서서히 변하는걸 구현해 봤는데
현재 창을650~1150만큼 스크롤 했을 때 카드의 크기가 1에서 0.9로 작아지도록 만들어주시면 됩니다.
사이즈를 줄이는 것은 transform : scale(0.9)이런 속성을 이용하면 된다.
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
let y = (-1 / 500) * height + 115 / 50;
// 650~1150까지 스크롤바를 내리면,
// 모든카드가 opacity 1~0으로 서서히변경해야함
cardBox.forEach((a, i) => {
cardBox[i].style.opacity = y;
});
// 650~1150까지 스크롤바를 내리면,
// 모든카드가 transform 1 ~ 0.9으로 서서히변경해야함
let z = (-1 / 5000) * 높이 + 565 / 500;
cardBox.forEach((a, i) => {
cardBox[i].style.transform = `scale(${z})`;
});
});

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="./index.css" />
<title>Level3</title>
</head>
<body>
<div class="card-background">
<div class="card-box">
<img src="./assets/파워퍼프걸 썬구리.JPG" />
</div>
<div class="card-box">
<img src="./assets/파워퍼프걸 썬구리.JPG" />
</div>
<div class="card-box">
<img src="./assets/파워퍼프걸 썬구리.JPG" />
</div>
</div>
<script src="./index.js"></script>
</body>
</html>
const cardBox = document.querySelectorAll(".card-box");
window.addEventListener("scroll", function () {
let height = window.scrollY;
console.log(height);
let y = (-1 / 500) * height + 115 / 50;
// 650~1150까지 스크롤바를 내리면,
// 모든카드가 opacity 1~0으로 서서히변경해야함
cardBox.forEach((a, i) => {
cardBox[i].style.opacity = y;
});
// 650~1150까지 스크롤바를 내리면,
// 모든카드가 transform 1 ~ 0.9으로 서서히변경해야함
let z = (-1 / 5000) * 높이 + 565 / 500;
cardBox.forEach((a, i) => {
cardBox[i].style.transform = `scale(${z})`;
});
});
index.css
.card-background {
height: 3000px;
margin-top: 800px;
margin-bottom: 1600px;
}
.card-box img {
display: block;
margin: auto;
max-width: 80%;
transition: all 0.2s;
}
.card-box {
position: sticky;
top: 400px;
margin-top: 200px;
}