オブジェクトを勉強しなおす

雪を降らせるスクリプトを過去のHDDから拾ってきた。

こういうスクリプトを見たとき感動したことを思い出す。
じっくりと眺めている。
基本というかこのテーマが「オブジェクトを移譲しまくる!」なのだが、
未だにこんな書き方の発想を1から作ることができない。
というか忘れてしまっている。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<title>Snow</title>
<style type="text/css">
body {
  background-color:black;
  overflow: hidden;
}
</style>
<body>
<script type="text/javascript">

if ('undefined' != typeof addEventListener || 'undefined' != typeof attachEvent &&
    'undefined' != typeof setInterval &&
    'undefined' != typeof Function.prototype &&
    'undefined' != typeof Function.prototype.call)

/*@cc_on @if (1) attachEvent('on' + @else@*/ addEventListener(/*@end@*/ 'load', function () {
//______________________________________________________________________


var Math2 = new function () {
  var PI     = Math.PI;
  var sin    = Math.sin;
  var floor  = Math.floor;
  var random = Math.random;
  
  var sintab = [ ];
  var i;
  
  for (i = 0; i < 360; i++)
    sintab[i] = sin(i * PI/180);
  
  this.sinDeg = function (deg) {
    return sintab[deg];
  };
  
  this.randomInt = function (n) {
    return floor(random() * n);
  };
};


//______________________________________________________________________


function Starter (callbackfn) {
  this.timerID = (function (o) {
    return setInterval(function () { return callbackfn.call(o); }, o.interval);
  })(this);
}


function Stopper () {
  clearInterval(this.timerID);
}


function Timer () {
  this.timerID = null;
  this.interval = Math2.randomInt(30) + 16;
}


// Timer.prototype.__proto__ = null;


Timer.prototype.start = function (listener) {
  return Starter.call(this, listener);
};


Timer.prototype.stop = function () {
  return Stopper.call(this);
};


//______________________________________________________________________


function Decorator (alpha, size) {
  //@ this.filter = 'Alpha(opacity=' + alpha + ')';
  this.opacity  = alpha / 100 + '';
  this.fontSize = size + 'px';
}


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


function Mover (element) {
  Timer.call(this);
  this.element = element;
}


// Mover.prototype.__proto__ = Timer.prototype;
Mover.prototype = new Timer;
Mover.prototype.constructor = Mover;


Mover.prototype.move = function (x, y) {
  return Locator.apply(this.element.style, arguments);
};


Mover.prototype.view = (function () {
  var C = document /*@if (1) [document.compatMode == 'CSS1Compat' ?
    'documentElement' : 'body'] /*@else@*/ .defaultView /*@end@*/;
  
  return function () {
    return {
      innerWidth : /*@if (1) C.clientWidth  @else@*/ C.innerWidth  /*@end@*/,
      innerHeight: /*@if (1) C.clientHeight @else@*/ C.innerHeight /*@end@*/
    };
  };
})();


//______________________________________________________________________


function Snow (element) {
  Mover.apply(this, arguments);
  this.p = 0;
  this.x = 0;
  this.y = 0;
  this.z = 0;
  this.init(1);
}


// Snow.prototype.__proto__ = Mover.prototype;
Snow.prototype = new Mover;
Snow.prototype.constructor = Snow;


Snow.prototype.init = (function (randomInt) {
  return function (n) {
    var v = this.view();
    
    this.x = randomInt(v.innerWidth);
    this.y = randomInt(v.innerHeight) * Boolean(n);
    this.z = randomInt(5) + 1;
    
    Decorator.call(this.element.style, randomInt(80) + 21, this.z * 7);
  };
})(Math2.randomInt);


Snow.prototype.fall = (function (floor, sinDeg) {
  var PI = Math.PI;
  
  return function () {
    var v = this.view();
    var x;
    var y;
    
    this.y += this.z / 2 + 2;
    this.p += 2;
    
    if (this.y > v.innerHeight)
      this.init();
    
    x = this.x + floor(sinDeg(this.p * 2 % 360) * 6 * this.z);
    y = this.y + floor(sinDeg(this.p     % 360) * 6 * this.z);
    
    this.move(x, y);
  };
})(Math.floor, Math2.sinDeg);

Snow['#instances'] = [ ];

Snow.create = function () {
  var d = document;
  var e = d.createElement('p');
  var s = e.style;
  var o;
  
  e.appendChild(d.createTextNode('*'));
  s.color = '#fff';
  s.position = 'absolute';
  o = new this(d.body.appendChild(e));
  Snow['#instances'].push(e, o);
  o.start(o.fall);
  
  return o;
};

document./*@if (1) attachEvent('on' + @else@*/ addEventListener(/*@end@*/ 'click', function (evt) {
  var x = Snow['#instances'];
  var snow; 
  var i = 0;
  var e = evt.target || evt.srcElement;

  while (snow = x[i++]) {
  	if (snow == e) {
        x[i].stop();
  		break;
  	}
  }
}, false);

//______________________________________________________________________


Snow.demo = function (n) {
  for (var i = 0; i < n; i++) this.create();
};


Snow.demo (200);


//______________________________________________________________________
}, false);

</script>

ちょっと変更

もうIEは捨てて、まずは従来通りに書き直してみる。
クリックして落下を止める機能に再開機能を付加する。
まだ思慮の最中。

細かいことだが悩み出すとキリがない。

<!DOCTYPE html>
<meta charset="utf-8">
<title>Snow</title>

<style>
body {
  background-color:black;
  overflow: hidden;
}
</style>

<body>

<script>

if ('undefined' !== typeof addEventListener &&
    'undefined' !== typeof setInterval &&
    'undefined' !== typeof Function.prototype &&
    'undefined' !== typeof Function.prototype.call &&
    'undefined' !== typeof Function.prototype.apply
    )

/* window. */ addEventListener('load', function () {

//__

function floor (n) {
  return Math.floor (n);
}

function randomInt (n) {
  return floor (Math.random () * n);
}


var sinDeg = new function () {
  var sintab = [ ];
  var sin    = Math.sin;
  var rad    = Math.PI / 180;
  var val = 0;
  
  for (var i = 0; i < 360; i++) {
    sintab[i] = sin (val);
    val += rad;
  }

  return function sinDeg (deg) {
    return sintab[deg % 360];
  };
};
//__


function Starter (callbackfn) {
  this.timerID = setInterval (callbackfn.bind (this), this.interval);
}


function Stopper () {
  clearInterval (this.timerID);
  this.timerID = null;
}


function Timer () {
  this.timerID = null;
  this.interval = randomInt (30) + 16;
}



function start () {
  return Starter.call (this, this.motion);
}


function stop () {
  return Stopper.call (this);
}

Timer.prototype.start = start;
Timer.prototype.stop = stop;

//__


function Decorator (alpha, size) {
  this.opacity  = alpha / 100 + '';
  this.fontSize = size + 'px';
}


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


function Mover (element) {
  Timer.call (this);
  this.element = element;
}


function move (x, y) {
  return Locator.apply (this.element.style, arguments);
}


function view () {
  var C = this.element.ownerDocument.defaultView;
  return {
    innerWidth : C.innerWidth,
    innerHeight: C.innerHeight
  };
}


Mover.prototype = new Timer;
Mover.prototype.constructor = Mover;
Mover.prototype.move = move;
Mover.prototype.view = view;
//__


function Snow (element) {
  Mover.apply (this, arguments);
  this.p = 0;
  this.x = 0;
  this.y = 0;
  this.z = 0;
}



function init (n) {
  var v = this.view ();
  
  this.x = randomInt (v.innerWidth);
  this.y = randomInt (v.innerHeight) * Boolean (n);
  this.z = randomInt (5) + 1;
  this.p = randomInt (360);
  
  Decorator.call (this.element.style, randomInt (80) + 21, this.z * 7);
}



//実験的な例としてクリックしたら落下を止める
document.addEventListener('click', clickStopper, false);
function clickStopper (event) {
  var x = Snow['#instances'];
  var snow; 
  var i = 0;
  var e = event.target;

  while (snow = x[i++]) {
  	if (snow.element === e) {
      snow[snow.timerID ? 'stop': 'start']();
  		break;
  	}
  }
}


//__

function fall () {
  var h = this.view ()['innerHeight'];
  var x;
  var y;
  
  this.y += this.z / 2 + 2;
  this.p += 1;
  
  if (this.y > h)
    this.init ();
  
  x = this.x + floor(sinDeg(this.p * 2) * 6 * this.z);
  y = this.y + floor(sinDeg(this.p    ) * 6 * this.z);
  
  this.move(x, y);
}


//__
function create () {
  var d = document;
  var e = d.createElement ('p');
  var s = e.style;
  var obj;
  
  e.appendChild (d.createTextNode ('*'));
  s.color = '#fff';
  s.position = 'absolute';
  obj = new this (d.body.appendChild (e));
  obj.init (true);
  obj.start ();

  Snow['#instances'].push (obj);
  
  return obj;
};


function demo (n) {
  if (0 < n)
    while (n--)
      this.create ();
}

//__

Snow.prototype = new Mover;
Snow.prototype.constructor = Snow;

Snow.prototype.fall = fall;
Snow.prototype.init = init;
Snow.prototype.motion = fall;

//__

Snow['#instances'] = [ ];
Snow.create = create;
Snow.demo   = demo;

//__

Snow.demo (100);

}, false);

</script>

Object.create で書き直す(思案中)

今ひとつ統一感がないな。
最初が Timer ではなく、new Object だな〜

<!DOCTYPE html>
<meta charset="utf-8">
<title>Snow</title>

<style>
body {
  background-color:black;
  overflow: hidden;
}
</style>

<body>

<script>

if ('undefined' !== typeof addEventListener &&
    'undefined' !== typeof setInterval &&
    'undefined' !== typeof Function.prototype &&
    'undefined' !== typeof Function.prototype.call &&
    'undefined' !== typeof Function.prototype.apply &&
    'undefined' !== typeof Function.prototype.bind
    
    )

/* window. */ addEventListener('load', function () {

//__

// 整数化
function floor (n) {
  return Math.floor (n);
}

// 乱数を整数(0からn未満)を返す
function randomInt (n) {
  return floor (Math.random () * n);
}


// Sin関数は前もって計算しておく
var sinDeg = new function () {
  var sintab = [ ];
  var sin    = Math.sin;
  var rad    = Math.PI / 180;
  var val    = 0;
  
  for (var i = 0; i < 360; i++) {
    sintab[i] = sin (val);
    val += rad;
  }

  return function sinDeg (deg) {
    return sintab[deg % 360];
  };
};

//__

function Timer (interval) {
  this.interval = interval;
}


function Starter (cbFunc) {
  this.timerID = setInterval (cbFunc.bind (this), this.interval);
}


function Stopper () {
  clearInterval (this.timerID);
  this.timerID = null;
}


Object.defineProperty (Timer.prototype,
  'timerID', {
    value       : null,
    writable    : true,
    enumerable  : false,
    configurable: false
  });


Object.defineProperty (Timer.prototype,
  'interval', {
    value       : null,
    writable    : true,
    enumerable  : true,
    configurable: false
  });


Object.defineProperty (Timer.prototype,
  'start', {
    value       : Starter,
    writable    : false,
    enumerable  : true,
    configurable: false
  });


Object.defineProperty (Timer.prototype,
  'stop', {
    value       : Stopper,
    writable    : false,
    enumerable  : true,
    configurable: false
  });



//var p =[];for(var o in Timer.prototype) p.push(o); alert(p.join("\n"));
//__


function Decorator (alpha, size) {
  this.opacity  = alpha / 100 + '';
  this.fontSize = size + 'px';
}


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


function Mover (element) {
  Timer.call (this);
  this.element = element;
}


function move (x, y) {
  return Locator.apply (this.element.style, arguments);
}


function view () {
  var C = this.element.ownerDocument.defaultView;
  return {
    innerWidth : C.innerWidth,
    innerHeight: C.innerHeight
  };
}


Mover.prototype = Object.create (
  Timer.prototype, {
    
    constructor: {
      value: Mover
    },
    
    view: {
      value       : view,
      writable    : false,
      enumerable  : false,
      configurable: false
    },
    
    move: {
      value       : move,
      writable    : false,
      enumerable  : true,
      configurable: false
    }
  });


//__


function Snow (element) {
  Mover.apply (this, arguments);
  this.p = 0;
  this.x = 0;
  this.y = 0;
  this.z = 0;
}



function init (n) {
  var v = this.view ();
  
  this.x = randomInt (v.innerWidth);
  this.y = randomInt (v.innerHeight) * Boolean (n);
  this.z = randomInt (5) + 1;
  this.p = randomInt (360);
  this.interval = randomInt (30) + 16;
  
  Decorator.call (this.element.style, randomInt (80) + 21, this.z * 10);
}



//実験的な例としてクリックしたら落下を止める
document.addEventListener('click', clickStopper, false);
function clickStopper (event) {
  var x = Snow['#instances'];
  var snow; 
  var i = 0;
  var e = event.target;

  while (snow = x[i++]) {
  	if (snow.element === e) {
      snow.timerID ? snow.stop (): snow.start (fall);
  		break;
  	}
  }
}


//__

function fall () {
  var h = this.view ()['innerHeight'];
  var x;
  var y;
  
  this.y += this.z / 2 + 2;
  this.p += 1;
  
  if (this.y > h)
    this.init ();
  
  x = this.x + floor(sinDeg(this.p * 2) * 6 * this.z);
  y = this.y + floor(sinDeg(this.p    ) * 6 * this.z);
  
  this.move(x, y);
}


//__
function create () {
  var d = document;
  var e = d.createElement ('p');
  var s = e.style;
  var obj;
  
  e.appendChild (d.createTextNode ('*'));
  s.color = '#fff';
  s.position = 'absolute';
  obj = new this (d.body.appendChild (e));
  obj.init (true);
  obj.start (fall);

  Snow['#instances'].push (obj);
  
  return obj;
};


function demo (n) {
  if (0 < n)
    while (n--)
      this.create ();
}

//__

Snow.prototype = new Mover;
Snow.prototype.constructor = Snow;

Snow.prototype.fall = fall;
Snow.prototype.init = init;
Snow.prototype.motion = fall;

//__

Snow['#instances'] = [ ];
Snow.create = create;
Snow.demo   = demo;

//__

Snow.demo (100);

}, false);

</script>

new Object じゃなくて new Function だったか…

<!DOCTYPE html>
<meta charset="utf-8">
<title>Snow</title>

<style>
body {
  background-color:black;
  overflow: hidden;
}
</style>

<body>

<script>

if ('undefined' !== typeof addEventListener &&
    'undefined' !== typeof setInterval &&
    'undefined' !== typeof Function.prototype &&
    'undefined' !== typeof Function.prototype.call &&
    'undefined' !== typeof Function.prototype.apply &&
    'undefined' !== typeof Function.prototype.bind
    
    )

/* window. */ addEventListener('load', function () {

//__

// 整数化
function floor (n) {
  return Math.floor (n);
}


// 乱数を整数(bからe未満)を返す
function randomInt (b, e) {
  switch (arguments.length) {
  case 2 : return floor (Math.random () * (e - b)) + b;
  case 1 : return floor (Math.random () * b);
  default : throw new Error;
  }
}


// Sin関数は前もって計算しておく
var sinDeg = new function () {
  var sintab = [ ];
  var sin    = Math.sin;
  var rad    = Math.PI / 180;
  var val    = 0;
  
  for (var i = 0; i < 360; i++) {
    sintab[i] = sin (val);
    val += rad;
  }

  return function sinDeg (deg) {
    return sintab[deg % 360];
  };
};

//__
var obj = new Function;

function Timer (interval) {
  this.interval = interval;
}


function Starter (cbFunc) {
  this.timerID = setInterval (cbFunc.bind (this), this.interval);
}


function Stopper () {
  clearInterval (this.timerID);
  this.timerID = null;
}


Timer.prototype = Object.create (
  obj.prototype, {

    constructor: {
      value: Timer
    },
    
    timerID: {
      value       : null,
      writable    : true,
      enumerable  : false,
      configurable: false
    },

    interval: {
      value       : null,
      writable    : true,
      enumerable  : true,
      configurable: false
    },
    
    start: {
      value       : Starter,
      writable    : false,
      enumerable  : true,
      configurable: false
    },
    
    stop: {
      value       : Stopper,
      writable    : false,
      enumerable  : true,
      configurable: false
    }

  });


//var p =[];for(var o in Timer.prototype) p.push(o); alert(p.join("\n"));
//__


function Decorator (alpha, size) {
  this.opacity  = alpha / 100 + '';
  this.fontSize = size + 'px';
}


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


function Mover (element) {
  Timer.call (this);
  this.element = element;
}


function move (x, y) {
  return Locator.apply (this.element.style, arguments);
}


function view () {
  var C = this.element.ownerDocument.defaultView;
  return {
    innerWidth : C.innerWidth,
    innerHeight: C.innerHeight
  };
}


Mover.prototype = Object.create (
  Timer.prototype, {
    
    constructor: {
      value: Mover
    },
    
    view: {
      value       : view,
      writable    : false,
      enumerable  : false,
      configurable: false
    },
    
    move: {
      value       : move,
      writable    : false,
      enumerable  : true,
      configurable: false
    }
  });


//__


function Snow (element) {
  Mover.apply (this, arguments);
  this.p = 0;
  this.x = 0;
  this.y = 0;
  this.z = 0;
}


function init (n) {
  var v = this.view ();
  
  this.x = randomInt (v.innerWidth);
  this.y = randomInt (v.innerHeight) * Boolean (n);
  this.z = randomInt (1, 6);
  this.p = randomInt (360);
  this.interval = randomInt (40, 80);
  
  Decorator.call (this.element.style, randomInt (21, 80), this.z * 7);
}

//__

Snow.prototype = Object.create (
  Mover.prototype, {
    
    constructor: {
      value: Snow
    },
    
    init: {
      value       : init,
      writable    : false,
      enumerable  : true,
      configurable: false
    }
  });;





//実験的な例としてクリックしたら落下を止める
document.addEventListener('click', clickStopper, false);
function clickStopper (event) {
  var x = Snow['#instances'];
  var snow; 
  var i = 0;
  var e = event.target;

  while (snow = x[i++]) {
  	if (snow.element === e) {
      snow.timerID ? snow.stop (): snow.start (fall);
  		break;
  	}
  }
}


//__

function fall () {
  var h = this.view ()['innerHeight'];
  var x;
  var y;
  
  this.y += this.z / 2 + 2;
  this.p += 1;
  
  if (this.y > h)
    this.init ();
  
  x = this.x + floor(sinDeg(this.p * 2) * 6 * this.z);
  y = this.y + floor(sinDeg(this.p    ) * 6 * this.z);
  
  this.move(x, y);
}


//__
function create () {
  var d = document;
  var e = d.createElement ('p');
  var s = e.style;
  var obj;
  
  e.appendChild (d.createTextNode ('*'));
  s.color = '#fff';
  s.position = 'absolute';
  obj = new this (d.body.appendChild (e));
  obj.init (true);
  obj.start (fall);

  Snow['#instances'].push (obj);
  
  return obj;
};


function demo (n) {
  if (0 < n)
    while (n--)
      this.create ();
}

//__


Snow['#instances'] = [ ];
Snow.create = create;
Snow.demo   = demo;

//__

Snow.demo (100);

}, false);

</script>