1)
풍뎅이뎅이..의 휴재편이 나와있는데,
혹시나 스크롤 애니메이션화하면 어떨까..라는 생각에 만들어봤습니다만..

*참고*
22화 이후 휴재공지 - 풍뎅이뎅이 - 다음 만화속세상..
(이미지 직접 끌어다 써보고 있습니다 = =;..)

...

2)

<a href=http://cartoon.media.daum.net/webtoon/viewer/18189 style=color:red; target=_blank>http://cartoon.media.daum.net/webtoon/viewer/18189</a>

<div id=mon121003_1 ></div>

<div id=output121003_1 >
<div id=backpo121003_1 ><div id=back121003_1></div></div>
<div id=frontpo121003_1 ><div id=front121003_1 ></div></div>
<div id=scrollpo121003_1 >
</div>
</div>

<br/><br/>
<div id=originalZone></div>

<script>
// <'+'div id=scrollpo ><'+'div id=scrollDiv ><'+'div id=scrollBoard ><'+'/div><'+'/div><'+'/div>

// z-index: 999 !important;
// <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

(function(){

function ele(v) {
  return document.getElementById(v);
  }

String.prototype.replaceAll=function() {
  var ar=arguments, arl=ar.length, i, ov=this;

  for (i=0; i<arl; i+=2) {
    ov=ov.replace(ar[i], ar[i+1]);
    }

  return ov;
  }

// 이미지의 세로위치를 기준으로, 표시기준위치값 계싼

function getpoyA(poy, poyN, divedZone) {
  var
    ov=[]
    , i, j, poyl=poy.length, tp, bp, ep;

  j=0; tp=bp=0;
  for (i=0; i<poyl; i++) {
    if (i>=poyN[j]) {
      tp=bp;
      j++;
      ep=divedZone.getElementsByTagName('img')[j];
//      if (ep) bp-=ep.offsetTop;
      if (ep) bp=-ep.offsetTop;  // 기존에는 이미지 크기로 정했지만, offsetTop 으로 하는 이상, 이미지 크기를 처리할 필요가 없음.
      }
    ov[i]=tp+poy[i];
    }

//  alert(divedZone.getElementsByTagName('img')[0].offsetTop);
//  alert(divedZone.offsetTop);
//  alert(bp);
  return ov;
  }

/*
// image 들의 크기를 기준으로 스크롤값 추출. 이 역시 offsetTop 으로 하는 이상, 후처리할 필요 없음.
// 단, 이미지가 들어오기 전이므로, 선처리용 값으로 사용할 경우는 있었음.

function getscrollHeight(ilist) {
  var ov, i, ilistl=ilist.length;

  ov=0;
  for (i=0; i<ilistl; i+=3) {
    ov+=ilist[i+2];
    }

  return ov;
  }

*/

function getimageAll(ilist) {
  var ov, i, ilistl=ilist.length;

  ov='';
  for (i=0; i<ilistl; i+=3) {
    ov+=('<img src={v} width={w} height={h} /><'+'br/>').replaceAll(/{v}/, ilist[i]
//, /{w}/, '400', /{h}/, 'inherit');
 , /{w}/, ilist[i+1], /{h}/, ilist[i+2]);
    }

  return ov;
  }

var
  vWidth=760, vHeight=760, sWidth=23
, poy=[
// ilist 0*3
0, // 0
-662, -1334, -2012, -2665, -3328, -3967, -4615, -5281, -5929

// ilist 1*3
, 59 // 10
, -560, -1190, -1814, -2445, -3078, -3702, -4337, -4934, -5562, -6196

// ilist 2*3
, -66 // 21
, -727, -1403, -2098, -2780, -3827
]
, poyN=[0, 10, 21, 27] // 27==poy.length

, monn=poy.length-1 // front's initializing position
// , monn=0
, mon=ele('mon121003_1')

, ilist=[ // 'image_url', image_width, image_height
  'http://i1.cartoon.daumcdn.net/svc/image/U03/cartoon/506B6FC60451960001', 760, 6584
, 'http://i1.cartoon.daumcdn.net/svc/image/U03/cartoon/506B6FC70450010001', 760, 6778
, 'http://i1.cartoon.daumcdn.net/svc/image/U03/cartoon/506B6FC804707D0001', 760, 4746
]
, poyA, poyAl
  , back=ele('back121003_1'), backpo=ele('backpo121003_1'), front=ele('front121003_1'), frontpo=ele('frontpo121003_1')
  , output=ele('output121003_1'), scrollDiv, scrollpo=ele('scrollpo121003_1')
//  , scrollBoard=ele('scrollBoard')
  , originalZone=ele('originalZone')
//  , scrollHeight=getscrollHeight(ilist)
  , scrollHeight
  , imageAll=getimageAll(ilist)

  , backpn=0, frontpn=1
;

 

/*
// patching for point's up and down..
function keydownFunction(e) {
  if (!e) e=event;
  var c=e.keyCode, y=poy[monn];

  switch(c) {
    case 38 :
      poy[monn]--;
      break;
    case 40 :
      poy[monn]++;
      break;
    default: return true;
    }
    mon.innerHTML=poy[monn];
    locSet(frontpo, 0, poy[monn]);
   
  }

 document.body.onkeydown=keydownFunction;
*/

// 위치 설정. position:absolute 포함.

function locSet(e, l, t) {
  e.style.left=l+'px';
  e.style.top=t+'px';
  e.style.position='absolute';
  }

function locFixSet(e, l, t) {
  e.style.left=l+'px';
  e.style.top=t+'px';
//  e.style.position='fixed !important';
  }

// 스크롤위치 지정.

function scrollSet(e, t) {
  e.scrollTop=-t;
  }

// e(n)의 위치를 좌측위 구석으로 설정. position:absolute 포함.

function lefttopAll() {
  var ar=arguments, arl=ar.length, i;
  for (i=0; i<arl; i++)
    locSet(ar[i], 0, 0);
  }

function lefttopFixAll() {
  var ar=arguments, arl=ar.length, i;
  for (i=0; i<arl; i++)
    locSet(ar[i], 0, 0);
//    locFixSet(ar[i], 0, 0);
  }

// relative 설장

function relativeSet(e) {
  e.style.position='relative';
  }

// 크기지정 : overflow:hidden; position:relative; 설정 포함

function zoneSet(e, w, h) {
  e.style.width=w+'px';
  e.style.height=h+'px';
  e.style.overflow='hidden';
  relativeSet(e)
  }

// e(n)의 크기 지정 : overflow:hidden; position:relative; 설정 포함.

function zoneAll(w, h) {// w, h, e1, e2, ..
  var ar=arguments, arl=ar.length, i;

  for (i=2; i<arl; i++)
    zoneSet(ar[i], w, h);
  }

// 배경색 지정

function backSet(e, c) {
  e.style.backgroundColor=c;
  }

// scrollBar 의 컨트롤 설정

function setscrollLeader(scrollDiv, front, back) {

  var scrollP, opav, lbackpn=-1, lfrontpn=-1, lvs;

  function scrollLeader() {
//    back.scrollTop=scrollP=scrollDiv.scrollTop;
    scrollP=scrollDiv.scrollTop;
// alert(scrollP);

/*
// fixed 설정 안되어있을 때 내용
if (scrollP+vHeight<scrollDiv.scrollHeight) {
backpo.style.top=scrollP+'px';
frontpo.style.top=scrollP+'px';
}
else {
backpo.style.top=(scrollDiv.scrollHeight-vHeight)+'px';
frontpo.style.top=(scrollDiv.scrollHeight-vHeight)+'px';
}
*/

    while (backpn>0) {
      if (scrollP<-poyA[backpn]) backpn--; else break;
      }
    while (backpn<poyAl) {
      if (scrollP>-poyA[backpn]) backpn++; else break;
      }

    if (backpn==poyAl) backpn--;
    frontpn=backpn+1;
    if (frontpn==poyAl) frontpn--;

    if (backpn==frontpn) {
      scrollSet(back, poyA[backpn]);
      scrollSet(front, poyA[frontpn]);
      front.style.filter='Alpha(opacity=0)';
      front.style.opacity='0';
      return;
      }

    opav=1-((scrollP+poyA[backpn])/(poyA[frontpn]-poyA[backpn])*6/1-3);
    if (opav<0) opav=0; else if (opav>1) opav=1;

    if (((backpn==lfrontpn)||(frontpn==lbackpn))&&(backpn!=frontpn)) {
      lvs=backpn; backpn=frontpn; frontpn=lvs;
      opav=1-opav;

      }

    if (opav==1) {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(opav*100));
      front.style.opacity=opav;
      }
    else if (opav==0) {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(opav*100));
      front.style.opacity=opav;
      }
    else if (lbackpn!=backpn) {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(1*100));
      front.style.opacity=1;
      }
    else if (lfrontpn!=frontpn) {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(0*100));
      front.style.opacity=0;
      }
    scrollSet(back, poyA[backpn]);
    scrollSet(front, poyA[frontpn]);
    front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(opav*100));
    front.style.opacity=opav;

    lbackpn=backpn;
    lfrontpn=frontpn;

    }

  if (scrollDiv==document.body) window.onscroll=scrollLeader;
  else if (scrollDiv==document.documentElement) window.onscroll=scrollLeader; // for IE
  else
  scrollDiv.onscroll=scrollLeader;

  scrollDiv.onclick=function(){
//    scrollP=scrollDiv.scrollTop;
//
//    alert(scrollP+' / '+backpn+' '+frontpn+' / '+back.scrollTop+' '+front.scrollTop+' '+front.style.filter+' '+front.opacity);
    if (opav<0.5) {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(0*100));
      front.style.opacity=0;
      }
    else {
      front.style.filter='Alpha(opacity={})'.replaceAll(/{}/, parseInt(1*100));
      front.style.opacity=1;
      }

    };

  }
// end of scrollLeader

  if (navigator.userAgent.match(/mobile/i)) {
    scrollpo.innerHTML='<textarea style="font-size:100px;" width=100% height=100% readonly=readonly id=scrollDiv121003_1 ></textarea>';
    }
  else {
    scrollpo.innerHTML='<'+'div id=scrollDiv121003_1 ><'+'div id=scrollBoard ><'+'/div><'+'/div>';
    }

  scrollDiv=ele('scrollDiv121003_1');


  zoneAll(vWidth+sWidth, vHeight, output, scrollDiv);
//  zoneAll(vWidth+sWidth, vHeight, scrollDiv);
//  zoneAll(vWidth+sWidth, vHeight);

  zoneAll(vWidth, vHeight, back, front);

  back.innerHTML=imageAll;
  front.innerHTML=imageAll;

  scrollHeight=back.scrollHeight;

//  scrollDiv.style.overflow='auto';
  scrollDiv.style.overflowY='scroll';
  scrollDiv.style.filter='Alpha(opacity=0.01)';
//  scrollDiv.style.filter='Alpha(opacity=0)';
  scrollDiv.style.opacity='0.01';
//  scrollDiv.style.opacity='0';
  scrollDiv.style.backgroundColor='inherit';
//  scrollDiv.style.backgroundColor='white';
  function fillscrollDiv(scrollDiv, h) {
    var ov='\n\n\n\n\n';

    scrollDiv.innerHTML=ov;

    while (scrollDiv.scrollHeight<h) {
      ov+=ov; ov+=ov;
      scrollDiv.innerHTML=ov;
      }

    scrollDiv.innerHTML=ov.substring(0, parseInt(
      ov.length/scrollDiv.scrollHeight*h
      )+2);
// alert(scrollDiv.scrollHeight+' '+h);
//alert(scrollDiv.outerHTML+' '+h);
    }

  if (navigator.userAgent.match(/mobile/i)) {
    fillscrollDiv(scrollDiv, back.scrollHeight);
    }
  else {
//  scrollBoard.width=vWidth;
//  scrollBoard.height=scrollHeight;
  scrollBoard.style.backgroundColor='white';
  zoneSet(scrollBoard, vWidth, scrollHeight);

  scrollBoard.style.filter='Alpha(opacity=0)';
  scrollBoard.style.opacity='0';

// scrollBoard.style.visibility='hidden'; // 요건 실패.. IE8 <!DOCTYPE> 에서 작동 안함..
    }


//  lefttopAll(backpo, frontpo, scrollpo);
  lefttopAll(scrollpo);
  lefttopFixAll(backpo, frontpo);

  front.style.filter='Alpha(opacity=50)';
  front.style.opacity='0.5';

poyA=getpoyA(poy, poyN, back); // back 과 front 에 표시되는 이미지들의 사이 간격이 같다는 가정을 하고..
poyAl=poyA.length;

// alert(poyA);


// with ending message test..
//  scrollSet(front, poyA[monn]);

// init scroll's prevalue
  scrollSet(back, poyA[backpn]);
  scrollSet(front, poyA[frontpn]);

  setscrollLeader(scrollDiv, front, back); // 스크롤 컨트롤을 설정함
// if (document.all)  setscrollLeader(document.documentElement, front, back); // IE
// else  setscrollLeader(document.body, front, back); // 스크롤 컨트롤을 설정함

// under screen, sampled images..

//  relativeSet(originalZone);
//  originalZone.innerHTML=imageAll;

// originalZone.innerHTML=poyA+' /*a,b*/'.replaceAll(/a/, poyA[backpn], /b/, poyA[frontpn]);


})();


</script>




...

3)

http://cartoon.media.daum.net/webtoon/viewer/18189





-- 새 창에 샘플 보여주기.. --

...

4)
설명을 하자면..



**
// <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

사실, 모바일용을 염두에 두고 만든 내용이었거든요.
그런데, 스케일 조정을 못하면 이미지가 모두 나오지 않는 문제를 해결하지 못해서요..



**
function getpoyA(poy, poyN, divedZone) { .. } 

이미지의 세로 위치를 array 로 반환하는 함수입니다. 

이미지 하나하나를 쓰는 게 아니라, 
이미지를 나열해놓은 div 를 쓰는 거라서요.. 

그래서, 미세(?)하게 위치가 다를 이미지의 스크롤 위치를 받아놓을 필요가 있는거죠. 



**
function getscrollHeight(ilist) { .. } 

이미지 element 를 추적할 수 없다고 생각했을 때 사용한 함수입니다. 
.. 위에서 위치를 받아, 정확도(?)를 높인 이상, 딱히 쓸 일이 없어서 주석처리 했습니다.



**
function getimageAll(ilist) { .. } 

div 내에 넣을 이미지 list 를 준비하는 함수입니다. 
.. 너무 세분화 시킨걸까요 -.-?



** 
  vWidth=760, vHeight=760, sWidth=23

뷰 가로세로폭과, 스크롤 폭입니다.
.. 자동화 시킬 수 있겠지만, .. 일단 기본 값이 필요할 것 같아서요.. (자동화 못시켰습니다 = =;..)



**
, poy=[ .. ]
, poyN=[0, 10, 21, 27] // 27==poy.length

이미지들의 세로표시될 지점과 (가로값은 따로 안뽑았어요..)
각 이미지들의 세로 표시지점을 저장한 위치입니다.

각각 만들지 않았는데요, =.=.. 좀 귀찮아서랄지..
(생각해보니, 각각 뽑는게 코드 보기에 이로울 것 같네요..)



**
, ilist=[ // 'image_url', image_width, image_height 
.. ] 

쓰일 이미지입니다. 
가능하다면 직접 이미지 등록해서 쓰고 싶긴 한데, .. 
.. 다음웹툰쪽에 일단 이미지 쓰고 있다고 얘기는 올려봐야겠군요. 



**
function keydownFunction(e) { .. } 

이벤트를 모니터링 하려고 만든 내용입니다. 
스크롤 끝부분에서 어떤 이벤트가 있는지 확인해보려고 했습니다만, 
= =;.. 잘 안됐서요.. 



**
function locSet(e, l, t) { .. }
function locFixSet(e, l, t) {..}
element 의 위치를 조정한다는 내용입니다.

function scrollSet(e, t) { .. }
div 의 스크롤값을 조정한다는 내용입니다.

function lefttopAll() { .. }
function lefttopFixAll() { .. }
리스트들에 locSet(0,0) 또는 locFixSet(0,0) 을 적용한다는 내용입니다.

function backSet(e, c) { .. }
function relativeSet(e) { .. } 
일일이 style[]=.. 형식으로 값 넣기가 귀찮아서요 = =;..

function zoneSet(e, w, h) { .. } 
크기 지정하고, 위치기준을 자신으로 맞춘다는 내용입니다.

function zoneAll(w, h) { .. }
w, h 이후에 나오는 리스트들에 zoneSet 을 지정한다는 내용입니다.



**
function setscrollLeader(scrollDiv, front, back) { .. } 

scroll 이벤트가 지정될 element 를 정한다는 내용입니다. 



** 
  function scrollLeader() { .. }

scroll 이벤트가 동작될 내용입니다. 



**
    while (backpn>0) {
      if (scrollP<-poyA[backpn]) backpn--; else break;
      }
    while (backpn<poyAl) {
      if (scrollP>-poyA[backpn]) backpn++; else break;
      }

스크롤위치와 이미지 표시위치값을 찾는다는 내용입니다. 
2진트리화하면 속도는 빠르겠습니다만, 
= =;.. 이거 만들 땐 방법이 생각나지 않아서요.. 



** 
    if (backpn==frontpn) {
      scrollSet(back, poyA[backpn]);
      scrollSet(front, poyA[frontpn]);
      front.style.filter='Alpha(opacity=0)';
      front.style.opacity='0';
      return;
      }

요 부분은 조금(?) 실수했군요. 
이미지의 앞쪽 값과 뒤쪽 값이 같을 때, 앞쪽이 투명하도록 지정한다는 내용인데요, 

초기에는 코드 만들 땐
앞쪽 이미지가 먼저 갈 내용을 기준으로 하고, 뒤쪽 이미지가 지나갈 이미지를 기준으로 서술했는데, 
.. 그게 깜박임 현상이 있어서

나중에 코드 수정할 땐
연속성을 염두에 두고 만들었죠. .. 수정을 해야할 내용 같습니다. 
(물론, 연속성쪽 문제도 해결해야겠죠..) 



**
    opav=1-((scrollP+poyA[backpn])/(poyA[frontpn]-poyA[backpn])*6/1-3);
    if (opav<0) opav=0; else if (opav>1) opav=1;

스크롤을 하다보면 이미지와 이미지 사이에 
살짝 흐림처리한 부분이 있을겁니다. 

setTimeout 넣어서 좋은쪽(?) 이미지를 보여주도록 하는 것도 좋겠습니다만, 
.. 일단 그냥 냅뒀습니다. 


**
    if (((backpn==lfrontpn)||(frontpn==lbackpn))&&(backpn!=frontpn)) { .. } 

요부분이 연속성 처리과정입니다. 
이전(?)에 표시한 내용과 비교해서 준비한 위치가 반대쪽과 같다면 
준비한 값들 자리바꿔 표시한다는 내용이죠. 



** 
    else if (lbackpn!=backpn) { .. }
    else if (lfrontpn!=frontpn) { .. }

진짜(?) 표시될 위치가 이전과 다를 때, 
우선, 표시될 투명도를 조정한다는 내용입니다. 

이 방법론보다 더 진지(?)해야 합니다만, 
= =;.. 딱히 생각나는 방법이 없어서요.. 



** 
  if (scrollDiv==document.body) window.onscroll=scrollLeader;
  else if (scrollDiv==document.documentElement) window.onscroll=scrollLeader; // for IE
  else
  scrollDiv.onscroll=scrollLeader;

scrollDiv 에 적용될 내용에 관련해 서술한 내용입니다. 

가능하다면 document.body 나 document.documentElement 에 적용하고 싶었습니다만, 
(실제 onscroll 이벤트를 받는 element 입니다..) 
모바일에서는 그게 사치(?)라서요.. = =;.. 



**
  scrollDiv.onclick=function(){ .. }

스크롤되던 내용을 클릭하면 어떤 것을 보여줄까..에 대한 내용입니다.
훨씬(?) 잘 보이는 쪽을 보여주는게 정답이더라구요.



** 
  if (navigator.userAgent.match(/mobile/i)) {
    scrollpo.innerHTML='<textarea style="font-size:100px;" width=100% height=100% readonly=readonly id=scrollDiv ></textarea>';
    }
  else {
    scrollpo.innerHTML='<'+'div id=scrollDiv ><'+'div id=scrollBoard ><'+'/div><'+'/div>';
    }

모바일용과 일반 브라우져용을 구별해놨습니다. 
브라우져용으로는 textarea 를, 일반 브라우져용으로는 div 를 쓰기로 한거죠. 

처음에는 div 를 써봤습니다만, 
모바일에서 스크롤이 표시 안되는 문제가 있어 찾다찾다 textarea 를 선택했죠. 

그러다가, pc 용에서 클릭 한번이 더 소모된다는 것을 알고(?) 
일반 브라우져용 기준으로만 다시 div 로 복귀시킨 내용입니다. 



**
  function fillscrollDiv(scrollDiv, h) { .. } 

  if (navigator.userAgent.match(/mobile/i)) {
    fillscrollDiv(scrollDiv, back.scrollHeight);
    }
  else { .. }

scrollDiv 의 스크롤크기를 지정한다는 내용입니다.
fillscrollDiv 내용을 보시면 \n 이 여러개 있는데요,
textarea 의 scrollHeight 를 최대한 맞추려고 loop 돌리게 되어있죠..

= =;.. 이 부분때문에 PC 가 멈출 수도 있을겁니다.. (가능성의 부분에서요..)
loop 횟수를 제한해야하는 것 아닐까..라는 생각도 드네요.



...

5)
대충 이런 내용으로 작성한거죠.

가끔씩 animation by script 관련 기술을 구사해보면서
페이징은 단순 flip 만 시도했습니다만,

이번에는 참 간만에(?) smooth flip 까지 적용해봤군요.
(이전에 가입한 클럽에서 유사한 기술을 구사해본 적이 있긴 했죠..)

가장 힘들었던 부분이 모바일이었습니다.
scroll 이벤트를 어떻게든 받아쓴다고, 고생 좀 했죠.

.. 가능하다면 minimul-scale:1.0, maximu-scale:1.0 환경에서도
쓸 수 있도록 만들고 싶다는 욕심도 드는군요..

*참고*
원본 이미지 쿼리티..를 남겨두고 싶어서
이미지 크기 조정 하지 않았습니다.

*참고2*
풍뎅이뎅이 연재관라하는 쪽에서 이미지 못쓴다고 한다면
.. 딱히 쓸수 이미지는 없고.. 그냥 이미지 없이 넣어두는 게 맞겠네요.
.. 숫자라도 집어넣을까..라는 생각도 해보구요.

*참고3*
이쪽에 올리기 전에 올렸던 내용입니다.
http://www.cyworld.com/kimkiewoong/7013006

ㅜ.ㅜ.. 모바일에서는 안나오는군요.. (무슨 문제인지는 파악을 못하고 있구요..)
댓글쪽에 첨부파일 주소 있긴 합니다. 

*update*  ('16.06.03 10:45) 

opacity:0; => display:none; .. so, opacity:0.01; background-color:inherit;


easyBow killofki@.




Posted by killofki
,