Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- php
- beanstalk
- 도메인
- JavaScript
- Route53
- angular4
- node.js
- 웹뷰
- TypeScript
- ios
- nextjs
- hybrid
- angular
- 안드로이드
- react
- cors
- 네이티브
- node
- https
- NeXT
- Elastic Beanstalk
- fanzeel
- 알려줌
- 감사일기
- swift
- AWS
- Android
- 페이스북
- S3
- 카카오톡
Archives
- Today
- Total
쪼렙 as! 풀스택
18.10.05 리액트로 smooth scroll 되는 슬라이더 만들기. 본문
Netflix 비슷한 슬라이더를 만들어야 했다.
smooth 스크롤을 구현해야 했는데, 검색해보니 여러가지 방법이 있었는데,
편하게 쓸 수 있는 것 같은 방법들은 대부분 Edge 까지 지원하질 못했다.
그래서 표준 Javascript 로만 직접 만들기로 했다.
아래 코드는, 지금 진행하고 있는 프로젝트에만 적용되는 코드이긴 한데,
시간이 나면 smoothScroll 하는 라이브러리로 오픈소스로 만들어야겠다.
import {Component} from 'react'
import '../styles/Home.scss'
//스크롤 애니메이션 0.5초.
const ANIM_DURATION = 300
const FRAME_TIME = 10
export default class VideoSlider extends Component {
constructor(props){
super(props)
this.mSeriesData = props.seriesData
this.mSliderRef = React.createRef();
this.state = {isHideLeft:true, isHideRight: false}
this.mScrolled = 0;
}
componentDidMount() {
this.refreshButtonHide()
}
//슬라이더 내부의 양옆 패딩 값을 가져오기.
getSliderPadding() {
return this.mSliderRef.current.firstChild.offsetLeft
}
//페이징할 스크롤 양을 계산하기. 양옆에 padding 만큼을 빼준다.
getPageSize() {
const padding = this.getSliderPadding() * 2;
return this.mSliderRef.current.offsetWidth - padding
}
//진행률에 따라 속도를 늦춰주는 양을 계산하기.
getRecorrectScrollByProgress(stepSize, pageSize, scrolled){
const progress = scrolled / pageSize
if(progress > 0.98) { return Math.ceil(stepSize * 0.02) }
else if(progress > 0.96) { return Math.ceil(stepSize * 0.06) }
else if(progress > 0.93) { return Math.ceil(stepSize * 0.12) }
else if(progress > 0.9) { return Math.ceil(stepSize * 0.2) }
else if(progress > 0.8) { return Math.ceil(stepSize * 0.4) }
else if(progress > 0.7) { return Math.ceil(stepSize * 0.6) }
else if(progress > 0.6) { return Math.ceil(stepSize * 0.8) }
else {
return stepSize
}
}
// 오른쪽으로 버튼.
clickRight = () => {
//우측으로 총 이동해야 할 값
let pageSize = this.getPageSize()
//스크롤할 수 있는 최대 한도
const limit = this.mSliderRef.current.scrollWidth - this.mSliderRef.current.offsetWidth
//pageSize 만큼 이동했을 때, 위치 예상값.
let target = pageSize + this.mSliderRef.current.scrollLeft
//만약, 예상값이 liimt 보다 크다면, pageSize 에서 그 차이만큼 빼준다.
if(target > limit) {
// console.log("한도보다 목표값이 더 커서 pageSize 를 조정한다.")
const dist = target - limit
pageSize = pageSize - dist
target = limit
console.log("보정된 pageSize ", pageSize)
}
//pageSize 를 프레임으로 나눠서, 한프레임에 얼만큼 이동해야할지 계산한다.
const stepSize = Math.ceil(pageSize / (ANIM_DURATION / FRAME_TIME))
console.log("프레임 시간 당 움직일값. ", stepSize)
//mScrolled 를 초기화한다.
this.mScrolled = 0;
this.smoothScrollToRight(stepSize, target, pageSize)
}
smoothScrollToRight(stepSize, target, pageSize) {
//아직 목표치까지 도달하지 않았다면, 이동해준다.
if(this.mSliderRef.current.scrollLeft < target) {
const scroll = this.getRecorrectScrollByProgress(stepSize, pageSize, this.mScrolled)
const posX = this.mSliderRef.current.scrollLeft + scroll
// this.mSliderRef.current.scrollTo(posX, 0);
this.mSliderRef.current.scrollLeft = posX
this.mScrolled += scroll
//재귀함수 호출.
setTimeout(() => {
this.smoothScrollToRight(stepSize, target, pageSize)
}, FRAME_TIME);
}else{
//목표에 도달했다면, 좌우 버튼을 refresh
this.refreshButtonHide()
}
}
clickLeft = () => {
//좌측으로 총 이동해야 할 값은?
let pageSize = 0 - this.getPageSize()
//스크롤할 수 있는 최대 한도
const limit = 0;
//pageSize 만큼 이동했을 때, 위치 예상값.
let target = pageSize + this.mSliderRef.current.scrollLeft
//만약, 예상값이 liimt 보다 적다면, limit 로 보정해준다.
if(target < limit) {
const dist = limit - target
pageSize = pageSize + dist
target = limit
}
//프레임당 이동해야할 양 계산. 소수점 올림을 했는데, 좌측으로 이동해야 하므로, -1 해준다.
const stepSize = Math.ceil(pageSize / (ANIM_DURATION / FRAME_TIME)) - 1
this.mScrolled = 0;
this.smoothScrollToLeft(stepSize, target, pageSize)
}
smoothScrollToLeft(stepSize, target, pageSize) {
//아직 목표까지 도달하지 않았다면, 이동해준다.
if(this.mSliderRef.current.scrollLeft > target) {
// 이때 얻어온 scroll은 반올림한것이므로 -1 해준다.
const scroll = this.getRecorrectScrollByProgress(stepSize, pageSize, this.mScrolled) - 1
const posX = this.mSliderRef.current.scrollLeft + scroll
// this.mSliderRef.current.scrollTo(posX, 0);
this.mSliderRef.current.scrollLeft = posX
this.mScrolled += scroll
//재귀함수 호출.
setTimeout(() => {
this.smoothScrollToLeft(stepSize, target, pageSize)
}, FRAME_TIME);
}else{
//도달했다면, 버튼 refresh
this.refreshButtonHide()
}
}
refreshButtonHide() {
const scrollLeft = this.mSliderRef.current.scrollLeft
const isHideLeft = scrollLeft === 0
const isHideRight = (scrollLeft + this.mSliderRef.current.offsetWidth) >= this.mSliderRef.current.scrollWidth
this.setState({isHideLeft:isHideLeft, isHideRight: isHideRight})
}
render() {
const leftButtonStyle = this.state.isHideLeft ? {display:'none'} : {left:'0'}
const rightButtonStyle = this.state.isHideRight ? {display:'none'} : {right:'0'}
return (
<div className="VideoSlider">
<div className="wrap-series-videos padding-wrapper" ref={this.mSliderRef} >
{this.mSeriesData.videos.map((videoData, index) => (
<VideoItem key={index} data={videoData} />
))}
</div>
{/* 하단에 스크롤바 숨기는 div */}
<div className="hide-scroll" />
{/* 우 슬라이더 버튼. */}
<button onClick={this.clickRight} className="slider-btn" style={rightButtonStyle} >우</button>
{/* 좌 슬라이더 버튼. */}
<button onClick={this.clickLeft} className="slider-btn" style={leftButtonStyle} >좌</button>
</div>
)
}
}
const VideoItem = ({data}) => {
return (
<div className="VideoSliderItem">
<img className="img-video-cover" src={data.coverUrl} />
</div>
)
}
'개발 일지 > Web & Server' 카테고리의 다른 글
React - Next.js 에서 공통 레이아웃 _app.js 사용하기. (0) | 2018.10.12 |
---|---|
javascript, 브라우저가 android, iphone, ios 스마트폰인지 알아오기. (0) | 2018.10.11 |
Nexjs 에서 구글 애널리틱스 사용하기. (0) | 2018.09.06 |
React - 자식 Component 객체에 접근하기! (0) | 2018.08.22 |
Angular - img src = local에 있는 blob 주소를 이용하여 img 태그 보여주기. (0) | 2018.07.30 |
Comments