Skip to content

Instantly share code, notes, and snippets.

@Dinir
Last active September 12, 2017 07:46
Show Gist options
  • Save Dinir/a5c912650366d49f75c0592f4fdb7434 to your computer and use it in GitHub Desktop.
Save Dinir/a5c912650366d49f75c0592f4fdb7434 to your computer and use it in GitHub Desktop.
adds a fullscreen slideshow on a webpage
/**
* @typedef {object} stoppableTimer
* @desc 일시정지가 가능한 인터벌 타이머입니다.
* @method stop 타이머를 멈춥니다.
* @method start 타이머를 시작합니다. 새로운 타이머 값을 줄 수 있습니다.
* @method reset 타이머를 리셋합니다. 새로운 타이머 값을 줄 수 있습니다.
*/
/**
* 일시정지가 가능한 인터벌 타이머를 만듭니다.
* 지정해준 시간이 지날 때마다 생성 시에 지정해준 함수를 실행합니다.
*
* @class
* @param {function} fn 실행할 함수
* @param {number} t 실행할 시간
* @returns {stoppableTimer}
*/
var stoppableTimer = function(fn, t) {
var timer = setInterval(fn, t);
return {
stop: function() {
if(timer) {
clearInterval(timer);
timer = null;
}
return this;
},
start: function(newT) {
if(!timer) {
t = newT?newT:t;
timer = setInterval(fn, t);
}
return this;
},
reset: function(newT) {
return this.stop().start(newT);
}
}
}
/**
* resize가 발생할 때마다 >66ms 이후에 `handler`를 실행합니다.
* 큐에 실행 스케쥴이 잡혀있을 경우 실행하지 않습니다.
* @param {function} handler
*/
var resizeHandler = function(handler) {
window.addEventListener("resize", resizeThrottler, false);
var resizeTimeout;
function resizeThrottler() {
if ( !resizeTimeout ) {
resizeTimeout = setTimeout(function() {
resizeTimeout = null;
handler();
}, 66);
}
}
handler();
}
/**
* @typedef {Object} fullscreenSlideshow
* @desc 슬라이드쇼 조작에 필요한 메소드를 담습니다.
* @method changePlayState
* 슬라이드쇼 재생 여부를 바꿉니다.
* @method arrange
* 현재 창 크기에 맞춰 슬라이드 이미지의 위치를 조절해, 창의 한 가운데에 오도록 합니다.
* @method on
* 슬라이드쇼 화면을 현재 창에 띄웁니다.
* @method off
* 슬라이드쇼 화면을 현재 창에서 감춥니다.
* @method play
* 슬라이드쇼 재생을 시작합니다.
* @method stop
* 슬라이드쇼 재생을 멈춥니다.
* @method move
* 지정한 슬라이드가 화면에 나타나도록 합니다.
*/
/**
* 창 전체에 나타나는 슬라이드쇼를 만듭니다.
* @class
* @param {string[][]} images
* 슬라이드쇼에서 표시할 이미지의 주소와 설명 문구를 담는 배열입니다.
* [[src, alt], [src, alt], ...] 의 형태를 받습니다.
* @param {number} interval 각 슬라이드를 보여줄 시간을 설정합니다.
* @param {number} transitionDuration 슬라이드 전환에 걸리는 시간을 설정합니다.
* @returns {fullscreenSlideshow}
*/
var fullscreenSlideshow = function(images, interval, transitionDuration) {
// 화면 전체에 표시할 슬라이드 쇼의 컨테이너를 만듭니다.
var container = document.createElement("div");
container.id = "fullscreenSlideshowContainer";
/**
* X 자 모양을 만듭니다.
* CSS: `/kor/css/index.css`, `#fullscreenSlideshowContainer .close`
*/
var close = (function() {
var dom = document.createElement("div");
dom.className = "close";
var x1 = document.createElement("span");
var x2 = document.createElement("span");
dom.appendChild(x1);
dom.appendChild(x2);
return dom;
})();
container.appendChild(close);
/**
* @typedef {object} slides
* @desc 슬라이드쇼에 쓰일 이미지와 이에 접근할 수 있는 프로퍼티를 담습니다.
* @property {HTMLElement} dom 이미지를 담은 요소입니다.
* @property {array} array 이미지를 담은 img 요소를 모아둔 어레이입니다.
* @property {function} order
* 화면에 표시되는 이미지의 순서를 구하거나 지정할 수 있는 함수입니다.
* 순서는 0부터 시작되며 순서 값에 따라 슬라이드 내의 이미지 표시 위치를
* `.style.right`를 설정해 바꿉니다.
*/
/**
* 이미지를 담을 요소를 만듭니다.
*
* @type {slides}
*/
var slides = (function() {
var doms = document.createElement("div");
var array = [];
// 이미지를 담을 요소를 만듭니다.
doms.className = "slides";
doms.style.width = images.length * 100 + "%";
doms.style.right = "-100%";
// 만든 요소 안에 이미지를 넣습니다.
images.map(function(v) {
var dom = document.createElement("img");
dom.className = "slide";
dom.src = v[0];
dom.alt = v[1];
dom.style.width = 100/images.length + "%";
doms.appendChild(dom);
array.push(dom);
});
/**
* 순서 값을 받아 이를 실제 존재하는 순서 값으로 바꿔서 반환합니다.
*
* @param {number} order
* @param {number} length
* @returns {number}
*/
var wrapOrder = function(order, length) {
if(order >= length || order < 0) {
var n = order % length;
if (n < 0) n += length;
return n;
} else
return order;
}
return {
dom: doms,
array: array,
/**
* 화면에 표시되는 이미지의 순서를 구하거나 지정합니다.
* 인자를 넣지 않을 경우 현재 순서를 반환하고,
* 인자를 넣을 경우 이를 실제 존재하는 순서 값으로 바꿔서 적용합니다.
* @param {number} order
* @param {boolean} force 참 값을 넣어주면 실제 존재하지 않는 순서를 적용합니다.
* @returns {number|undefined}
*/
order: function(order, force) {
if(typeof order !== "undefined") {
doms.style.right =
(force? order: wrapOrder(order, images.length)) * 100 + "%";
} else {
return parseInt(doms.style.right)/100;
}
}
};
})();
container.appendChild(slides.dom);
/**
* @typedef {object} buttons
* @desc 슬라이드쇼에서 사용할 버튼 요소들입니다.
* @property {HTMLElement} dom 버튼 요소들을 담은 요소입니다.
* @property {HTMLElement} play 재생 버튼입니다.
* @property {HTMLElement} stop 정지 버튼입니다.
* @property {HTMLElement} slide00, slide01, ... 각 슬라이드에 대한 버튼입니다.
*/
/**
* 슬라이드쇼에 표시될 버튼을 만들고 이를 컨테이너에 담은 뒤,
* 버튼들을 모아 오브젝트 형태로 반환합니다.
*
* @type {buttons}
*/
var navigator = (function() {
var dom = document.createElement("div");
dom.className = "nav";
var play = document.createElement("div");
play.className = "flowButton play";
dom.appendChild(play);
var stop = document.createElement("div");
stop.className = "flowButton stop";
dom.appendChild(stop);
var buttons = {
dom: dom,
play: play,
stop: stop
};
images.map(function(v,i) {
var slide = document.createElement("a");
slide.className = "slideButton slide"+("0"+i).substr(-2);
slide.innerHTML = "●";
dom.appendChild(slide);
buttons["slide"+("0"+i).substr(-2)] = slide;
});
return buttons;
})();
container.appendChild(navigator.dom);
document.body.appendChild(container);
var playState = false;
var controller = {
changePlayState: function(setValue) {
if(typeof setValue === "boolean")
playState = setValue;
else if(typeof setValue === "undefined")
playState = !playState;
},
arrange: function() {
var screenSize = {
x: window.innerWidth,
y: window.innerHeight
};
slides.array.map(function(v) {
if(v.getBoundingClientRect().height) {
var top = ( screenSize.y - v.getBoundingClientRect().height ) / 2;
v.style.top = top + "px";
} else
v.style.top = 0;
});
return this;
},
on: function() {
controller.move(0, true);
container.style.display = "block";
var show = setInterval(function(){
container.style.left = "0";
clearInterval(show);
}, 16);
controller.play();
controller.arrange();
return this;
},
off: function() {
container.style.left = "100%";
var hide = setInterval(function(){
container.style.display = "none";
clearInterval(hide);
}, 500);
controller.stop();
return this;
},
play: function() {
timer.reset();
controller.changePlayState(true);
if(navigator.play.className.substr(-3) !== " on") {
navigator.play.className += " on";
navigator.stop.className = navigator.stop.className.replace(" on", "");
}
return this;
},
stop: function() {
timer.stop();
controller.changePlayState(false);
if(navigator.stop.className.substr(-3) !== " on") {
navigator.stop.className += " on";
navigator.play.className = navigator.play.className.replace(" on", "");
}
return this;
},
move: function(order, force) {
if(parseInt(order) !== slides.order()) {
var o = typeof order !== "undefined"? order: slides.order()+1;
if (force)
slides.dom.style.transitionDuration = "0s";
slides.order(o, force);
slides.dom.style.transitionDuration = transitionDuration + "s";
if(playState)
controller.play();
var oString = ("0" + slides.order()).substr(-2);
for (var button in navigator) {
if (button.indexOf("slide") !== -1) {
if (navigator[button].className.substr(-2) === oString)
navigator[button].className += " on";
else
navigator[button].className =
navigator[button].className.replace(" on", "");
}
}
}
return this;
},
/*inspect: function() {
return {
cont: container,
close: close,
slides: {
dom: slides.dom,
array: slides.array,
move: controller.move,
timer: timer
}
}
}*/
}
var timer = new stoppableTimer(controller.move, interval);
timer.stop();
close.addEventListener("click", function() {
controller.
off().
move(-1, true);
});
navigator.dom.addEventListener("click", function(e) {
switch(e.target.className.split(" ")[1]) {
case "play":
controller.play();
break;
case "stop":
controller.stop();
break;
default:
if(e.target.className.split(" ")[0] === "slideButton") {
controller.move(e.target.className.split(" ")[1].substr(-2));
}
break;
}
});
return controller;
};
var slideshow = new fullscreenSlideshow(
[
['/kor/images/01.jpg','alt1'],
['/kor/images/02.jpg','alt2'],
['/kor/images/03.jpg','alt3'],
['/kor/images/04.jpg','alt4'],
],
5000,
.3
);
var fullscreenSlideshowAnchor = document.querySelector("#slideshowLink");
fullscreenSlideshowAnchor.addEventListener("click", slideshow.on);
resizeHandler(slideshow.arrange);
#fullscreenSlideshowContainer {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 100%;
overflow: hidden;
transition: left .3s ease-in-out;
background: url(/kor/images/bgp.gif) left top repeat;
z-index: 50;
display: none;
.close {
display: block;
width: 8em;
height: 8em;
position: absolute;
top: 0;
right: 0;
background-color: rgba(255, 255, 255, .5);
cursor: pointer;
z-index: 2;
span {
display: inline-block;
width: 8em;
border: 1px solid rgba(0, 0, 0, .5);
position: absolute;
top: 3.8em;
&:nth-child(1) {
transform: rotate(45deg);
}
&:nth-child(2) {
transform: rotate(-45deg);
}
}
}
.slides {
position: relative;
/*right: -100%;*/
transition: right .0s ease-in-out;
.slide {
position: relative;
}
}
.nav {
position: fixed;
top: 22px;
width: 100%;
text-align: center;
z-index: 1;
.flowButton,
.slideButton {
cursor: pointer;
margin-right: 6.5px;
}
.flowButton {
display: inline-block;
width: 13px;
height: 15px;
&.play {
background: url(/kor/images/play.png);
&.on {
background: url(/kor/images/play_on.png);
}
}
&.stop {
background: url(/kor/images/stop.png);
&.on {
background: url(/kor/images/stop_on.png);
}
}
}
.slideButton {
position: relative;
top: -2.5px;
color: #c6c6c6;
&.on {
color: #bbe2a8;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment