オブジェクト化し(いわゆる)クロージャーにして・・・「ボールを左から右へ」

yyr446 さんの、指示のもと作ってみた。
単純に左から右への移動だとつまらないので、
2次元の運動もできるように考えた。
そうしたら、3次元でも出来るようにと、あれこれ考えていたら・・・

思考手順

ボールとは、HTMLでいう要素、空間座標、運動エネルギー(運動を計算する関数)から成り立っている。
運動エネルギーは、自分が動ける空間を認知していなければならない。
空間を認知するためには、エリアの中なのか外なのかを理解できなければならない。


ボールの回転を含めないと、嘘っぽい。しかも重力の向きが逆! 残念。

<!DOCTYPE html>
<title></title>

<div style="position:relative">
  <img
    id="ball"
    onclick=""
    src="data:image/gif;base64,
      R0lGODlhGwAaALMAAO/v8I2Njry9wM7P0cXGyLK0tqmrrZ6foOTl5tjZ2np7ewoKCfv7+2Fh
      YTMzM////yH5BAEAAA8ALAAAAAAbABoAAAT/8MlJ30AoDVG7VwMjAgCGbF53FAUijiQiaEUq
      FUnikvxQKA4FgFDz4C6wkqFgCDQcAVmRoiAQAK8s6+B0LA4JwfQhGOiyWQNX8VwsDEQKljFH
      MwyNp3fhOBjUEgJ0JHZYBntuXz4/Egk8LnYnAgdtCmEFAk4zj2gAGgQXJQVWLAUHDgQ6CJ6j
      jgyfZho0Yn8CC6A6AEwHbA0JuDkDVga0BgMNZQMAA34BbAuxwVYES38FA8QDF1zObS0awgQC
      AktM49SYh3l6CwHL4eNiCsmjD5gDCnwOew0IVuOYBOhRMIqAvXEJGiTiMy8ewF5eFCCYwGKG
      QihlaPmhlGePmQl/UIgRCABKTKklevbxYVCBSTWXIf8QyIdIXId0fmKW25DIAbEUAhCYAiVO
      AChhDUAwGGDjQQM1CkhmMBlUhIKmE/K4QZaDRIIAWDvoA1HAiY0IADs="
  >
</div>

<script>

// (配列数)次元の範囲に存在すか調べる関数を返す
function isInsideSpace (start/*Array*/, end/*Array*/, offset/*Array*/) { //配列は次元の数だけ
  if (2 > arguments.length)
    return null;
  
  var min = [];
  var max = [];
  var len = start.length;
  var i;
  var o;
  
  for (i = 0; i < len; i++) {
    o = offset ? offset[i]: 0;
    min[i] = start[i] + o;
    max[i] = end[i] + o;
  }

  return function (d, position) { // d 次の座標を評価する。戻り値は配列で[評価値, 適正値]
    return (position < min[d])
           ? [ -2, min[d] ]           // エリアの外側(マイナス)
           : (position == min[d])
             ? [ -1, min[d] ]         // エリアの線上
             : (position == max[d])
               ? [ 1, max[d] ]        // エリアの線上
               : (position > max[d])
                 ? [ 2, max[d] ]      // エリアの外側(プラス)
                 : [ 0, position ];   // エリア内
  };
}


function createMover (motionList, area) {
  if (2 > arguments.length)
    return null;
  
  var counter = 0;

  return function (position) {
    var i;
    var motion;
    var p;
    var a = null;
    var result = [];
    
    for (i = 0; i < this.len; i++) {
      p = position[i];
      if (motion = motionList[i]) {
        p = motion.call (counter, p);
        a = area (i, p);
        if (a[0])
          p = motion.call (counter, p, a[0], a[1])
      }
      result.push (p);
    }
    counter += 1;
    return result;
  }
}


function Locator (p) {
  this.top = p[1] + 'px';
  this.left = p[0] + 'px';
}

function Stopper () {
  crearInterval (this.timerId);
  this.timerId = null;
}

function Loop () {
  Locator.call (this.target.style, this.position = this.mover (this.position));
}

//_________

function Ball (target, position, mover, interval) {
  this.target = target;
  this.position = position;
  this.mover = mover;
  this.interval = interval;
  this.timerId = null;
  this.len = position.length;
}


Ball.prototype.start = function () {
  var that = this;
  var cbFunc = function () { Loop.call (that); };

  if (! this.timerId)
    this.timerId = setInterval (cbFunc, this.interval);
};


Ball.prototype.stop = function () {
  if (this.timerId)
    Stopper.call (this);
};


Ball.create = function (ballId, position, mover, interval) {
  var target = document.getElementById (ballId);
  var obj = null;

  if (target) {
    target.style.position = 'absolute';
    target.style.top = position[1] + 'px';
    target.style.left = position[0] + 'px';
     
    obj = new Ball (target, position, mover, interval || 500);
    obj.start ();
  }
  return obj;
}

//_________
var bound = function (height, speed, gravity) {
  speed = speed || 0;
  gravity = gravity || 0.980665;
  
  return function (y, f, v) {
    if (f < 0) {
      speed = -speed;
      y = v;
    }
    else {
      speed = speed + gravity;
      y -= speed;
    }
    return Math.floor (y);
  };
};

var area = isInsideSpace ([0, 0], [700, 200]); // 2次元の動ける範囲
var motionList = [
  (function (v) { return function (x, f) { if (f) v = -v; v *=0.995; return (x + v) |0; };})(5), // x 座標の動き
  bound (400, 0) // y 座標の動き
];
var mover = createMover (motionList, area);

//_________

var BALL = Ball.create ('ball', [0, 100], mover, 10);

</script>