처음과 끝 상태만 있는 transition 말고 복잡한 애니메이션을 구현해야 한다면 @keyframes 를 써보자.
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="index.css" />
<title>Document</title>
</head>
<body>
<div class="btn-wrap">
<button class="btn">고양이 조심</button>
</div>
</body>
</html>
//index.scss
.btn-wrap {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.btn {
background-color: yellowgreen;
width: 200px;
height: 100px;
border: 1px solid yellowgreen;
border-radius: 5px;
color: white;
font-size: 30px;
&:hover {
animation-name: shake;
animation-duration: 0.5s;
}
}
}
@keyframes shake {
0% {
transform: rotateX(0);
}
20% {
transform: rotateZ(-10deg);
}
50% {
transform: rotateZ(10deg);
}
80% {
transform: rotateZ(-10deg);
}
100% {
transform: rotateX(0);
}
}
@keyframes 이름 {
}
으로 작성하고 그 안에 진행 상황별 상태를 정의해 놓는다.
그 안에 진행상황을 %로 입력하는데 순서만 잘 지키면 몇가지로 세분화 하든 상관없다.
만들어둔 keyframes를 가져다 쓸때는
animation-name: 이름;
으로 가져다 쓰는데
여러가지 옵션들이 있다.
대표적으로
- animation-duration: 한 사이클이 돌때 애니메이션의 총 시간
- 0으로 설정하면 애니메이션 재생되지 않음
- 음수로 설정하면 애니메이션 재생되지않음
- ex) animation-duration: .5s // 전체 애니메이션 시간이 0.5초에 걸쳐 일어남
- animation-delay: 해당 엘리먼트가 로드되고 나서 언제 애니메이션이 시작될지 지정
- 0: 기본값, 로드 되자마자 실행
- now: 0으로 설정한 것과 같다. ios2.0부터 사용가능
- 음수: 음수로 값을 설정하면 로드 되자마자 실행 하지만 지정한 시간 만큼이 지난 후부터의 장면부터 재생한다. 예를들어 animation-delay: -1s; 로 설정하면 1초가 지난 후의 장면부터 바로 재생된다.
- ex) animation-delay: 1s // 1초 후에 실행
- animation-iteration-count: 애니메이션 재생 횟수를 정의한다.
- 1: 기본값. 한번 실행.
- infinite: 애니메이션 무한 반복
- ex) animation-iteration-count: 2.8; // 애니메이션이 2번 반복된 다음 전체 프레임의 8/10에 해당하는 프레임까지 재생됨.
- animation-timing-function: 애니메이션 키프레임 사이의 재생속도를 지정.
- ex) animation-timing-function: ease-in-out; //시작 부분 재생속도를 점점 빠르게 하고 마지막 부분 재생속도를 점점 느리게
그런데 말입니다.
//예시 1
@keyframes move {
0% {
transform: translateX(0);
}
50% {
transform: translateX(20px);
}
100% {
transform: translateX(0);
}
}
// 예시 2
@keyframes move {
0% {
margin-left:0;
}
50% {
margin-left:20px;
}
100% {
margin-left:0;
}
}
transform: translateX(20px);
굳이 이렇게 길게 쓰지않고
margin-left: 20px;
이렇게 짧게 쓰면 코드량도 줄고 좋지 않나요 ?
왜 굳이 transform: translateX(20px); 이렇게 길게 쓰나요 ?
이유는 transform 변경보다 margin 변경이 더 느려서 그렇습니다.
첫번째 이유
브라우저가 화면에 그림을 그리는 순서를 보면
1. render tree 만들기
2. layout 잡기 ( 네모 박스들 위치 잡기 )
3. paint 하기 ( 픽셀에 색칠하기 )
4. composite 처리하기 ( transform 이나 opacity 같은 애들 있으면 처리하기 )
순으로 합니다.
그런데 width, height, margin, padding 등의 애들은 변경이 되면 2번 layout 잡기 부터 2,3,4 순으로 다시 실행이 되고
background-color 같은 색관련 애들은 변경이 되면 3번 paint 하기 부터 3,4 순으로 다시 실행,
transform, opacity 같은 애들 변경이 되면 4번만 재실행 됩니다.
그래서 2,3,4 를 다시 하는거보다 4번만 다시 하는 transform 을 쓴다~ 이말입니다.
한가지 이유가 더 있습니다.
두번째 이유
transform 이런거는 다른 쓰레드에서 처리해 줍니다.
웹 브라우저는 원래 쓰레드 1개만 사용하는데 ( 자바스크립트 실행, html, css 처리 전부 한 쓰레드에서 처리 )
이외에도 성능 잡을수 있는 두가지 방법이 있는데
방법1. will-change 를 쓴다.
.box {
will-change: transform;
}
will-change: 애니메이션 줄 속성;
요고 쓰면 바뀔 내용을 미리 렌더링 해줌.
근데 뭔가 이상하게 버벅일 때만 쓰고 애니메이션이 스무스하게 잘 될때는 쓸필요 음슴.
이상하게 많이 쓰면 브라우저 오히려 더 느려질수도 있음.
방법 2.하드웨어 가속
.box {
transform: translate3d(0, 0, 0);
}
transform: translate3d 를 쓰면 3D 이동이 되는데 이 속성은 GPU를 사용해서 연산함.
그래서 translate3d(0,0,0) 이런식으로 아무데도 움직이지 않는 3d 이동명령 주고 뒤에 필요한 transform 적용하면 GPU 이용해서 .box가 가진 transform 속성 연산을 하게 됨.
'html & css' 카테고리의 다른 글
Anti-aliasing (1) | 2023.10.23 |
---|---|
화면 reflow 가 일어나는 경우 (0) | 2022.11.22 |
댓글