「スクロールしてもついてくる(iPhone版)」その2.「あんた馬鹿ぁ〜?」と言われるちょっと手前。

<!DOCTYPE html>
<html lang="ja">
<head>
  <title></title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=320">
  <style>
ol {
  list-style:none;
}
ol > li {
  width : 50px;
  height: 50px;
  border:6px red solid;
  padding:2px;
  text-align:center;
}
p {
  margin: 20em 0;
}
  </style>
</head>

<body>
<h1>Test</h1>
<p>a<p>b<p>c<p>d<p>e<p>f<p>g<p>h<p>i<p>j<p>k<p>l
<ol>
  <li>左上
  <li>中央上
  <li>右上
  <li>右中央
  <li>右下
  <li>中央下
  <li>左下
  <li>左中央
  <li>ど真ん中
<ol>

<script>

(function () {

  var DOC = document;
  var VIEW = DOC.defaultView;

//_______________________________________

  function Singleton () {
    var target_stock = [];
    var option_stock = [];
    
    return new function () {
      this.contains =
        (function (/*target, target2, ..*/) {
           return Array.prototype.some.call (arguments, function (target) {
             return (-1 < this.indexOf (target)); }, target_stock); });
      
      this.add =
        (function add (target, option) {
           if (! this.contains (target)) {
             target_stock.push (target);
             option_stock.push (option);
           }});
      
      this.getOption =
        (function (target) {
           var index = target_stock.indexof (target);
           return (-1 < index) ? option_stock[index]: null; });
      
      this.getAllOptions =
        (function () { return option_stock.slice (0); });
    };
  }
  
//_______________________________________

  function Controller (obj) {
    if (1 > arguments.length)
      return null;
    
    return new function () {
      this.getAttribute =
        (function (attr) { return (obj.hasOwnProperty (attr)) ? obj[attr]: undefined; });
      
      this.setAttribute =
        (function (attr, val) {
           if (obj.hasOwnProperty (attr))
             obj[attr] = val; });
      
      this.method =
        (function (name/*,arg0, arg1,..*/) {
           if ('function' === typeof obj[name])
             return obj[name].apply (obj, Array.prototype.slice.call (arguments, 1)); });
    };
  }
//_______________________________________

  function Locator (x, y) {
    this.top = y + 'px';
    this.left = x + 'px';
  }
  
//_______________________________________

  function Elevator (target, parm, cbFunc, cbObject) {
    this.target     = target;
    this.accell   = parm.accell;
    this.interval = parm.interval;
    this.cuddleHeight = parm.cuddleHeight;
    this.cuddleWidth = parm.cuddleWidth;
    this.offsetX   = parm.offsetY;
    this.offsetY   = parm.offsetX;
    this.timerId  = null;
    this.cbFunc = cbFunc;
    this.cbObject = cbObject;
  }
//_______________________________________

  function Elevator_start () {
    if (! this.timerId)
      this.timerId = setInterval (function (that) { Elevator_onTimer.call (that) }, this.interval, this);
  };


  function Elevator_stop () {
    if (this.timerId) {
      clearInterval (this.timerId);
      this.timerId = null;
    }
  };


  function Elevator_onTimer () {
    var e = this.target;
    var s = e.style;
    var a = this.accell;
    
    var py = window.pageYOffset - this.offsetY;
    var px = window.pageXOffset - this.offsetX;

    switch (this.cuddleHeight) {
    case 1 : py += Math.floor (window.innerHeight / 2 - e.offsetHeight / 2); break;
    case 2 : py += Math.floor (window.innerHeight - e.offsetHeight); break;
    }
    
    switch (this.cuddleWidth) {
    case 1 : px += Math.floor (window.innerWidth / 2 - e.offsetWidth / 2); break;
    case 2 : px += Math.floor (window.innerWidth - e.offsetWidth); break;
    }

    var diffX = px - e.offsetLeft;
    var diffY = py - e.offsetTop;
    
    if (0 === diffX)
      if (0 === diffY)
        Elevator_stop.call (this);

    var x = e.offsetLeft + Math.floor (diffX / a + (0 < diffX));
    var y = e.offsetTop + Math.floor (diffY / a + (0 < diffY));

    Locator.call (s, x, y);
  };

  
  function Elevator_cuddleHeight (position) {
    var ary_cuddle_height = ['top', 'middle', 'bottom'];
    var type_no;
    
    if (! position)
      position = 'top';
    
    type_no = ary_cuddle_height.indexOf (position.toLowerCase ());
    if (0 > type_no)
      throw new Error ('指定できない値');
    
    return type_no;
  }


  function Elevator_cuddleWidth (position) {
    var ary_cuddle_width = ['left', 'center', 'right'];
    var type_no;
    
    if (! position)
      position = 'left';
    
    type_no = ary_cuddle_width.indexOf (position.toLowerCase ());
    if (0 > type_no)
      throw new Error ('指定できない値');
    
    return type_no;
  }

  
  Elevator.prototype.start       = Elevator_start;
  Elevator.prototype.stop        = Elevator_stop;
//_______________________________________
  
  var Manager = new function () {
    var stocker = new Singleton;
    var ary_cuddle_height = ['top', 'middle', 'bottom'];
    var ary_cuddle_width = ['left', 'center', 'right'];
    
    
    this.handleEvent =
      (function (event) {
         switch (event.type) {
         case 'scroll' :
           stocker.getAllOptions().forEach (function (obj) { obj.method ('start'); });
           break;
      
         case 'unload' :
           document.removeEventListener ('scroll', this, false);
           window.removeEventListener ('unload', this, false);
           break;
         }
       });


    this.create =
      (function (target, parm, cbFunc, cbObject) {
         if (1 > arguments.length)
           return null;
         
         if (stocker.contains (target))
           return stocker.getOption (target);
         
         var elevator = new Elevator (target, {
             'accell'      : parm.accell || 2,
             'interval'    : parm.interval || 50,
             'cuddleHeight': Elevator_cuddleHeight (parm.cuddleHeight),
             'cuddleWidth' : Elevator_cuddleWidth (parm.cuddleWidth),
             'offsetY'     : parm.offsetY || 0,
             'offsetX'     : parm.offsetX || 0
           }, cbFunc, cbObject);

          target.style.position = 'absolute';
          elevator.start ();
          
          var controller = Controller (elevator);

          stocker.add (target, controller);
          return controller;
      });

    document.addEventListener ('scroll', this, false);
    window.addEventListener ('unload', this, false);
  };
  
  this.Elevator = Manager;
})();

var li = document.querySelectorAll ('li');

Elevator.create (li[0], {accell:4,  interval: 10 });
Elevator.create (li[1], {accell:8, interval: 10, cuddleWidth: 'center' });
Elevator.create (li[2], {accell:12, interval: 10, cuddleWidth: 'right' });
Elevator.create (li[3], {accell:16, interval: 10, cuddleHeight: 'middle', cuddleWidth: 'right' });
Elevator.create (li[4], {accell:20, interval: 10, cuddleHeight: 'bottom', cuddleWidth: 'right' });
Elevator.create (li[5], {accell:24, interval: 10, cuddleHeight: 'bottom', cuddleWidth: 'center' });
Elevator.create (li[6], {accell:28, interval: 10, cuddleHeight: 'bottom'});
Elevator.create (li[7], {accell:32, interval: 10, cuddleHeight: 'middle'});
Elevator.create (li[8], {accell:36, interval: 10, cuddleHeight: 'middle', cuddleWidth: 'center' });

</script>