Sticky로 독특한 인터랙션(구동 효과) 주기
요즘 새로 진행하고 있는 프로젝트의 마크업을 담당하면서, 랜딩 구동 효과 구현을 위해 position의 Sticky 속성을 사용하고 있습니다. Sticky라는 속성이 있다는 것은 알았지만 어떻게 동작하는지, 또 어디다 사용할 수 있는지 몰랐었는데 이번에 활용하면서 다양한 인터렉션에 활용할 수 있음을 알게 되었고 이에 대한 경험을 다른 분들과 함께 나누고자 합니다.
Sticky는 뭐하는 속성일까?
웹페이지 스크롤 시, Sticky가 적용된 엘리트먼트의 부모 요소의 height 범위 내에서는 fixed(고정)로 작동되지만, 그 외의 경우에는static으로 동작하게 하는 속성입니다.
이미지만으로 이해하기 어렵다면, CODEPEN에 있는 예제로 직접 체험해 볼 수 있습니다.
Sticky를 사용할 때의 큰 어려움은 없습니다. 고정하고 싶은 요소에 다음과 같이 선언해 주기만 하면 됩니다.
position: -webkit-sticky;
position: sticky;
현시점에서 모든 브라우저(IE 미지원, edge 16 이상)에서 지원하나, safari에서 그냥 Sticky로는 적용이 불가능하기 때문에 -webkit- prefix를 추가로 붙였습니다. 참고로 부모 요소의 height 범위 내에서만 Sticky가 동작하기 때문에 scroll이 가능할 만한 충분한 높이가 있어야 합니다.
어떻게 활용 가능한가요?
영문권역 사이트의 예제는 과거 iOS에서 많이 사용하던 알파벳(혹은 가나다) 카테고리 혹은 GNB&LNB로 가리킵니다. 이렇게만 보면 사용할 일이 생각보다 적겠구나 할 수 있겠지만 그렇지 않습니다. Javascript로만 표현할 수 있을 것 같은 상호작용을 sticky로 쉽게 구현할 수 있습니다!
위 3개 예시 인터렉션 모두 CSS Sticky로 대체 가능하지만, 아쉽게도 Javascript를 사용해 구현되어 있습니다. (물론 fade in-out, 내용이 바뀌는 등의 구동은 Javascript를 사용해야 합니다.)특히 l.Point의 경우 스크롤을 위아래로 바꿀 때 튕기는 버그가 있는데 CSS Sticky를 활용한다면 쉽게 해결할 수 있습니다.
apple iPhone XS 프로모션 사이트의 일부분인데, 실제로 저 부분을, 개발자도구를 사용해 살펴보면 Sticky를 사용해 구현한 것을 볼 수 있습니다.
Sticky를 활용한 예시
Sticky를 이용한 예시를 제작했습니다. 스크롤 위치 (뷰포트 범위내에 특정 엘리트먼트가 존재하는지 여부)와 이미지 변경 및 슬라이드를 위한 Javascript만 사용되었을 뿐, 화면에 고정되는 효과는 Sticky만 이용했습니다.
css
@charset "UTF-8";
html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:""}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}
html { min-width: 1400px; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; scroll-behavior: smooth; }
body { font-family: "Spoqa Han Sans", "Roboto", sans-serif; color: #1a1d33; }
div.wrap { width: 1400px; padding: 0 50px; margin: 0 auto; position: relative; box-sizing: border-box; }
section#intro { background: #f5f7fa url("../images/bg_intro.png") 50% 50%; width: 100%; height: 100vh; overflow: hidden; }
section#intro div.wrap { position: relative; top: 50%; transform: translateY(-50%); z-index: 1; }
section#intro header { padding-left: 268px; }
section#intro header:after { width: 100%; height: 2px; background: #1a1d33; position: absolute; left: -1060px; top: 157px; content: ""; display: block; }
section#intro header h1 { font-size: 93px; font-weight: 900; line-height: 80px; }
section#intro header h1 span { display: block; color: transparent; text-stroke: 2px #1a1d33; -webkit-text-stroke: 2px #1a1d33; }
section#intro header p { font-size: 15px; position: absolute; top: 56px; left: 695px; }
section#intro h2 { font-size: 28px; font-family: "NanumSquare", sans-serif; font-weight: 700; letter-spacing: 10px; line-height: 41px; position: absolute; top: 6px; left: 50px; }
section#intro h2 span { font-size: 15px; font-weight: 400; letter-spacing: 0; vertical-align: top; line-height: 30px; }
section#intro ul { margin-top: 50px; }
section#intro li { margin-top: 16px; }
section#intro li:first-child { margin: 0; }
section#intro dt { font-size: 15px; font-weight: 500; line-height: 20px; }
section#intro dd { font-size: 14px; font-weight: 400; color: #6d6d6d; line-height: 20px; }
section#intro a.scroll { text-indent: -999999px; display: block; position: relative; bottom: -145px; border-radius: 9px; border: 1px solid #1a1d33; width: 18px; height: 32px; }
section#intro a.scroll:after { background: #1a1d33; width: 2px; height: 5px; border-radius: 2px; position: absolute; top: 7px; left: 50%; transform: translateX(-50%); display: block; content: ""; animation: scrollAni 1.3s infinite .2s; }
@keyframes scrollAni { 0% { transform: translateY(0); } 100% { transform: translateY(5px); opacity: 0; } }
section#contents { background: #fff; background-size: contain !important; width: 100%; margin-top: 50vh; min-width: 1400px; }
section#contents hr { display: none; }
section#contents div.wrap { position: relative; margin: 0 auto; min-width: 1400px; padding-bottom: 5vh; }
section#contents div.imgSlider { width: 796px; height: 467px; box-shadow: 0 0 54px 0 rgba(0, 0, 0, .2); position: sticky; top: 50%; transform: translateY(-50%); background: #fff; }
section#contents div.imgSlider div.nav { position: absolute; z-index: 99; right: -78px; bottom: 77px; background: #fff; height: 70px; width: 225px; transform: rotate(-90deg); }
section#contents div.imgSlider p.title { color: #313131; font-size: 12px; line-height: 70px; position: absolute; top: 0; left: 25px; text-align: left; }
section#contents div.imgSlider p.num { color: #b3b3b3; font-size: 11px; line-height: 70px; position: absolute; top: 0; right: 25px; text-align: left; letter-spacing: 1px; }
section#contents div.imgSlider button { display: none; }
section#contents div.imgSlider div.slider div.tns-item { width: 796px; height: 467px; }
section#contents div.title { padding: 50px 0 0 860px; width: 1400px; margin: 0 auto 80vh auto; box-sizing: border-box; position: relative; z-index: 0; }
section#contents div.title:nth-of-type(2) { margin-top: -17vh; }
section#contents div.title:last-child { margin: 0 auto 25vh auto; }
section#contents div.title:after { position: absolute; content: ""; font-size: 65px; font-weight: 800; color: #f1f1f1; display: block; top: 0; margin-left: -3px; z-index: -1; }
section#contents div.title.ideafestival:after { content: "MICRO WEB"; }
section#contents div.title.at:after { content: "UI WEB"; }
section#contents div.title.mobile:after { content: "MOBILE UIX"; }
section#contents div.title h2 { font-size: 35px; font-family: "NanumSquare", sans-serif; line-height: 44px; font-weight: 300; }
section#contents div.title h2 strong { font-weight: 800; display: block; }
section#contents div.title p { margin-top: 20px; font-size: 16px; line-height: 24px; color: #3e3e3e; font-weight: 400; }
section#contents div.title a { display: inline-block; margin-top: 35px; color: #fff; text-decoration: none; font-size: 14px; font-weight: 400; padding: 13px 18px 13px 72px; background: #121e54 url("../images/bg_arrow.svg") 18px 50% no-repeat; background-size: 41px; transition: background-color .3s; }
section#contents div.title a:hover, section#contents div.title a:focus { background: #1b2b72 url("../images/bg_arrow.svg") 18px 50% no-repeat; background-size: 41px; }
section#contents div.title ul.sliderControl { list-style: none; margin-top: 80px; text-align: right; padding-right: 120px; box-sizing: border-box; }
section#contents div.title ul.sliderControl li { display: inline-block; margin-right: 15px; }
section#contents div.title ul.sliderControl li:last-child { margin: 0; }
section#contents div.title ul.sliderControl li button { outline: 0; cursor: pointer; display: block; width: 12px; height: 20px; text-indent: -999999px; padding: 16px 10px; border: 0 none; background: none; opacity: .4; transition: opacity .2s; }
section#contents div.title ul.sliderControl li button:hover, section#contents div.title ul.sliderControl li button:active { opacity: 1; }
section#contents div.title ul.sliderControl li button:active { transform: translateY(1px); }
section#contents div.title ul.sliderControl li button.prev { background: url("../images/bg_leftArrow.svg") 50% 50% no-repeat; background-size: 11px; }
section#contents div.title ul.sliderControl li button.next { background: url("../images/bg_rightArrow.svg") 50% 50% no-repeat; background-size: 11px; }
footer { padding: 20px 10px; border-top: 1px solid #eee; font-size: 12px; color: #4d4d4d; }
footer address { width: 1400px; margin: 0 auto; display: block; box-sizing: border-box; padding: 0 50px; }
HTML
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>Dong-gri Handicraft</title>
<meta name="author" content="dongri.me" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,500,600,700,900&display=swap" />
<link rel="stylesheet" href="https://spoqa.github.io/spoqa-han-sans/css/SpoqaHanSans-kr.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/moonspam/NanumSquare@1.0/nanumsquare.css" />
<link rel="stylesheet" href="./common/css/style.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.1/tiny-slider.css" />
<script src="common/js/common.js"></script>
</head>
<body>
<section id="intro">
<div class="wrap">
<header>
<h1><span>DONGRI</span>HANDICRAFT</h1>
<p>— 2019.08.10</p>
</header>
<h2>주니어<br />디자이너,<br />동그리<span>dongri</span></h2>
<ul>
<li>
<dl><dt>MICRO WEB.</dt><dd>Ideafestival</dd></dl>
</li>
<li>
<dl><dt>PC WEB.</dt><dd>Audio-technica</dd></dl>
</li>
<li>
<dl><dt>MOBILE UI.</dt><dd>Shopick</dd></dl>
</li>
<li>
<dl><dt>MOBILE UI.</dt><dd>Brothumb<i>!</i></dd></dl>
</li>
</ul>
<a href="#contents" class="scroll">scroll</a>
</div>
</section>
<section id="contents">
<div class="wrap">
<div class="imgSlider">
<div class="nav"><p class="title">전경이미지</p><p class="num"><span class="current">1</span>/<span class="total">3</span></p></div>
<div class="slider">
<div><img src="./common/images/img_ideafestival01.jpg" alt="" /></div>
<div><img src="./common/images/img_ideafestival02.jpg" alt="" /></div>
<div><img src="./common/images/img_ideafestival03.jpg" alt="" /></div>
<div><img src="./common/images/img_ideafestival04.jpg" alt="" /></div>
</div>
</div>
<div class="title ideafestival">
<h2>더 나은 미래를 위한 <strong>IDEAFESTIVAL</strong></h2>
<p>SK 이노베이션이 기업을 대표하는 사회공헌 프로그램 <br />Ideafestival과 자사 홍보를 위한 <br />마이크로 웹 사이트 프로젝트를 진행했습니다.</p>
<a href="https://ideafestival.dongri.me/" target="_blank" rel="noopener">LAUNCH PROJECT</a>
<ul class="sliderControl">
<li><button class="prev">prev</button></li>
<li><button class="next">next</button></li>
</ul>
</div>
<hr />
<div class="title at">
<h2>음악에 대한 사랑과 열정으로 <strong>AUDIO-TECHNICA</strong></h2>
<p>일본을 대표하는 정통 오디오 명가인 audio-technica의 <br />한국어 PC·MOBILE UI WEB 컨셉 디자인 프로젝트를 진행했습니다.</p>
<a href="https://at.dongri.me/" target="_blank" rel="noopener">LAUNCH PROJECT</a>
<ul class="sliderControl">
<li><button class="prev">prev</button></li>
<li><button class="next">next</button></li>
</ul>
</div>
<hr />
<div class="title mobile shopick">
<h2>V COMMERCE SERVICE <strong>SHOPICK</strong></h2>
<p>SHOPICK은 SNS을 활용한 인플루언스 마케팅에 <br />착안해 비디오를 결합한 신개념 모바일 V 커머스 <br />쇼핑 플랫폼입니다.</p>
<a href="https://portfolio.dongri.me/uix/shopick/" target="_blank" rel="noopener">LAUNCH PROJECT</a>
<ul class="sliderControl">
<li><button class="prev">prev</button></li>
<li><button class="next">next</button></li>
</ul>
</div>
<hr />
<div class="title mobile brothumb">
<h2>V COMMERCE SERVICE <strong>Brothumb<i>!</i></strong></h2>
<p>SHOPICK은 SNS을 활용한 인플루언스 마케팅에 <br />착안해 비디오를 결합한 신개념 모바일 V 커머스 <br />쇼핑 플랫폼입니다.</p>
<a href="https://portfolio.dongri.me/uix/shopick/" target="_blank" rel="noopener">LAUNCH PROJECT</a>
<ul class="sliderControl">
<li><button class="prev">prev</button></li>
<li><button class="next">next</button></li>
</ul>
</div>
</div>
</section>
<footer>
<address>© Dong-gri All rights reserved.</address>
</footer>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.1/min/tiny-slider.js"></script>
<script src="./common/js/scrollMonitor.js"></script>
<script src="./common/js/common.js"></script>
</html>
원래 jQuery를 사용하다가, 제주도 코딩 베이스캠프(제코배) 이후 DOM만을 이용하려고 하다 보니 많이 어렵기도 하고, 적응이 안 되어 이미지 슬라이드 부분에 약간의 버그가 존재합니다. 그 점은 감안하고 봐주시면 감사하겠습니다.
CSS만으로도 효과적인 구동 효과(인터랙션)이 가능
이전에는 Javascript만을 사용해 하나하나 구현해야 했다면, 날이 갈수록 CSS의 성능과 다양한 속성들이 추가됨에 따라 CSS만으로 효과적인 구동 인터랙션을 줄 수 있게 되었습니다.
완전히 Javascript를 배제하긴 어렵고, 브라우저 하위 호환성도 고려해야 하지만 국내 에이전시에서 구축한 사이트와 해외 웹 사이트들의 화려한 인터랙션을 살펴보며 CSS로 대체할 수 있는 부분을 연구하면서 조금씩 활용한다면 좋을 것 같습니다.