角丸#

rsky2006-03-17


角丸角丸++ ときて、さすがに Objective-角丸は無理があるだろうということで、不本意ながらも#。
と、そんなことはどうでもいいんですが、昨日のやつをさらに改良しました。

  • ボーダーの幅を指定できるようになった。
  • 角丸に加えて角の面取り・切り欠きができるようになった。(コンストラクタの第一引数で指定)
  • コンストラクタで各種デフォルト値を設定できるようになった。

角丸自体は Safari 2.0.3 / IE 6.0 / Firefox 1.5.0.1 / Opera 8.5.3 でちゃんと動作することを確認していますが、例によって下のサンプルは Safari 2.0.3 でしか期待通りにレンダリングされません。

test.html

<html>
<head>
<title>test</title>
<script type="text/javascript" src="mynifty.js"></script>
<script type="text/javascript">
var Eclipse = new MyNiftyCorner(-1, 'gray', '#ccc', 64, 4, '#000');
var Linear  = new MyNiftyCorner(0, '#aff', 'transparent', 20, 1, '#00002a', 2);
var Rounder = new MyNiftyCorner(1, 'white', 'gray', 12);

var b64
	= 'iVBORw0KGgoAAAANSUhEUgAAADwAAABQCAYAAABFyhZTAAAABHNCSVQICAgIfAhk'
	/* snip */
	+ '+Bfj/wJAv+2N5Ub+YQAAAABJRU5ErkJggg==';

window.onload = function() {
	var div0 = document.getElementById('pre');
	var div1 = document.getElementById('flt');
	var div2 = document.getElementById('abs');

	var txt = document.createTextNode(b64.replace(/(.{144})/g, '$1?n'));
	div0.appendChild(txt);
	try {
		var obj1 = document.createElement('object');
		obj1.setAttribute('data', 'data:image/png;base64,' + b64);
		div1.appendChild(obj1);
		var obj2 = obj1.cloneNode();
		obj2.style.width = '240px';
		obj2.style.height = '320px';
		div2.appendChild(obj2);
	} catch (e) {
		window.alert(e.toString());
		if (!obj1) { div1.appendChild(document.createTextNode('Error1')); }
		if (!obj2) { div2.appendChild(document.createTextNode('Error2')); }
	}

	Eclipse.roundAndPad(document.body);
	Rounder.roundAndPad(div0);
	Linear.roundAndPad(div2);
}
</script>
<style type="text/css">
body { margin:10px; padding:0px; background-color:#ccc; }
#pre { position:relative; white-space:pre; margin:0px; background-color:#ffc;
	font-family:monospace; font-size:8px; line-height:100%; }
#abs { position:absolute; bottom:10%; left:10%; z-index:1;
	font:36px bold; color:red; opacity:0.5; }
#flt { float:left; font:36px bold; color:red; }
</style>
</head>
<body>
<div id="pre"><div id="abs"></div><div id="flt"></div></div>
</body>
</html>

mynifty.js

if (!Number.square) { Number.prototype.square = function() { return Math.pow(this, 2); }}
if (!Number.cube)   { Number.prototype.cube   = function() { return Math.pow(this, 3); }}
if (!Number.sqrt)   { Number.prototype.sqrt   = function() { return Math.sqrt(this);   }}
if (!Number.floor)  { Number.prototype.floor  = function() { return Math.floor(this);  }}
if (!Number.ceil)   { Number.prototype.ceil   = function() { return Math.ceil(this);   }}
if (!Number.round)  { Number.prototype.round  = function() { return Math.round(this);  }}
if (!Math.hypot) { Math.hypot = function(x, y) { return (x.square() + y.square()).sqrt(); }}
if (!Math.base)  { Math.base  = function(z, y) { return (z.square() - y.square()).sqrt(); }}

var MyNiftyStyle
  = '<style type="text/css">'
  + 'span.NiftyElement {'
  + 'display:block;'
  + 'margin:0;'
  + 'padding:0;'
  + 'overflow:hidden;'
  + 'overflow-x:hidden;'
  + 'overflow-y:hidden;'
  + '}'
  + '</style>';
document.write(MyNiftyStyle);

function MyNiftyCorner()
{
  // {{{ 初期化

  var self = this;
  var mode = 'round';
  if (arguments.length > 0) {
    mode = arguments[0].toString();
    switch (arguments[0]) {
    case 0:
      mode = 'linear';
      break;
    case -1:
      mode = 'flipped';
      break;
    }
  }

  this._bgcolor = (arguments.length > 1) ? arguments[1].toString() : '#fff';
  this._color   = (arguments.length > 2) ? arguments[2].toString() : '#ccc';
  this._size    = (arguments.length > 3 && parseInt(arguments[3]) > 0) ? parseInt(arguments[3]) : 5;
  this._step    = (arguments.length > 4 && parseInt(arguments[4]) > 0) ? parseInt(arguments[4]) : 1;
  this._size    = Math.max(this._size, this._step);
  this._bdcolor = (arguments.length > 5) ? arguments[5].toString() : null;
  this._bdwidth = (arguments.length > 6 && parseInt(arguments[6]) > 0) ? parseInt(arguments[6]) : 1;
  this._bdwidth = Math.min(Math.floor(this._size / this._step), this._bdwidth);

  // }}}
  // {{{ 角丸化メソッド

  this.round = function()
  {
    self.roundTop.apply(this, arguments);
    self.roundBottom.apply(this, arguments);
  }

  this.roundTop = function()
  {
    var args = self.parseArguments(arguments);
    var target = args.shift();
    target.insertBefore(self.createTop.apply(this, args), target.firstChild);
  }

  this.roundBottom = function()
  {
    var args = self.parseArguments(arguments);
    var target = args.shift();
    target.appendChild(self.createBottom.apply(this, args));
  }

  this.roundAndPad = function()
  {
    var args = self.parseArguments(arguments);
    var target  = args[0];
    var bgcolor = args[1];
    var color   = args[2];
    var size    = args[3];
    var step    = args[4];
    var bdcolor = args[5];
    var bdwidth = args[6];
    if (target.childNodes && target.childNodes.length) {
      var container = document.createElement('div');
      container.style.backgroundColor = bgcolor;
      container.style.margin = '0';
      if (bdcolor) {
        var bdsize = step * bdwidth;
        container.style.padding = self.intToPixelsHorizontal(size - bdsize);
        container.style.borderColor = bdcolor;
        container.style.borderStyle = 'solid';
        container.style.borderWidth = self.intToPixelsHorizontal(bdsize);
      } else {
        container.style.padding = self.intToPixelsHorizontal(size);
      }
      while (target.childNodes.length) {
        container.appendChild(target.removeChild(target.firstChild));
      }
      target.appendChild(container);
    }
    self.roundTop.apply(this, arguments);
    self.roundBottom.apply(this, arguments);
  }

  // }}}
  // {{{ 要素作成メソッド

  this.createCorner = function(color, height)
  {
    var corner = document.createElement('span');
    corner.className = 'NiftyElement';
    corner.style.backgroundColor = color;
    corner.style.height = self.intToPixels(height);
    corner.unshiftChild = function(newChild)
    {
      return this.insertBefore(newChild, this.firstChild);
    }
    return corner;
  }

  this.createLine = function(color, height, margin)
  {
    var line = document.createElement('span');
    line.className = 'NiftyElement';
    line.style.backgroundColor = color;
    line.style.height = self.intToPixels(height);
    line.style.margin = self.intToPixelsHorizontal(margin);
    return line;
  }

  this.createLineBordered = function(color, height, margin, bdcolor, bdwidth)
  {
    var line = document.createElement('span');
    line.className = 'NiftyElement';
    line.style.backgroundColor = color;
    line.style.height = self.intToPixels(height);
    line.style.margin = self.intToPixelsHorizontal(margin);
    line.style.borderColor = bdcolor;
    line.style.borderStyle = 'solid';
    line.style.borderWidth = self.intToPixelsHorizontal(bdwidth);
    return line;
  }

  this.createTop = function()
  {
    if (arguments[4]) {
      if (arguments[5] > 1) {
        return self._createTopBordered2.apply(this, arguments);
      }
      return self._createTopBordered.apply(this, arguments);
    }
    return self._createTopSimple.apply(this, arguments);
  }

  this.createBottom = function()
  {
    if (arguments[4]) {
      if (arguments[5] > 1) {
        return self._createBottomBordered2.apply(this, arguments);
      }
      return self._createBottomBordered.apply(this, arguments);
    }
    return self._createBottomSimple.apply(this, arguments);
  }

  this._createTopSimple = function(bgcolor, color, size, step)
  {
    var top = self.createCorner(color, size);
    var i, line;
    for (i = 0; i < size; i += step) {
      line = self.createLine(bgcolor, step, self.calcMargin(size, i, step));
      top.unshiftChild(line);
    }
    return top;
  }

  this._createBottomSimple = function(bgcolor, color, size, step)
  {
    var bottom = self.createCorner(color, size);
    var i, line;
    for (i = 0; i < size; i += step) {
      line = self.createLine(bgcolor, step, self.calcMargin(size, i, step));
      bottom.appendChild(line);
    }
    return bottom;
  }

  this._createTopBordered = function(bgcolor, color, size, step, bdcolor)
  {
    var top = self.createCorner(color, size);
    var i, line, margin1, bdsize;
    for (i = 0; i < size - step; i += step) {
      margin = self.calcMargin(size, i, step);
      bdsize = Math.max(step, self.calcMargin(size, i + step, step) - margin);
      line = self.createLineBordered(bgcolor, step, margin, bdcolor, bdsize);
      top.unshiftChild(line);
    }
    line = self.createLine(bdcolor, step, self.calcMargin(size, i, step));
    top.unshiftChild(line);
    return top;
  }

  this._createBottomBordered = function(bgcolor, color, size, step, bdcolor)
  {
    var bottom = self.createCorner(color, size);
    var i, line, margin, bdsize;
    for (i = 0; i < size - step; i += step) {
      margin = self.calcMargin(size, i, step);
      bdsize = Math.max(step, self.calcMargin(size, i + step, step) - margin);
      line = self.createLineBordered(bgcolor, step, margin, bdcolor, bdsize);
      bottom.appendChild(line);
    }
    line = self.createLine(bdcolor, step, self.calcMargin(size, i, step));
    bottom.appendChild(line);
    return bottom;
  }

  this._createTopBordered2 = function(bgcolor, color, size, step, bdcolor, bdwidth)
  {
    var top = self.createCorner(color, size);
    var bdsize = step * bdwidth;
    var insize = size - bdsize;
    var i, line, margins;
    for (i = 0; i < insize; i += step) {
      margins = self.calcMargin2(size, insize, bdsize, i, step);
      line = self.createLineBordered(bgcolor, step, margins[0], bdcolor, margins[1]);
      top.unshiftChild(line);
    }
    while (i < size) {
      line = self.createLine(bdcolor, step, self.calcMargin2(size, insize, bdsize, i, step)[0]);
      top.unshiftChild(line);
      i += step;
    }
    return top;
  }

  this._createBottomBordered2 = function(bgcolor, color, size, step, bdcolor, bdwidth)
  {
    var bottom = self.createCorner(color, size);
    var bdsize = step * bdwidth;
    var insize = size - bdsize ;
    var i, line, margins;
    for (i = 0; i < insize; i += step) {
      margins = self.calcMargin2(size, insize, bdsize, i, step);
      line = self.createLineBordered(bgcolor, step, margins[0], bdcolor, margins[1]);
      bottom.appendChild(line);
    }
    while (i < size) {
      line = self.createLine(bdcolor, step, self.calcMargin2(size, insize, bdsize, i, step)[0]);
      bottom.appendChild(line);
      i += step;
    }
    return bottom;
  }

  // }}}
  // {{{ ユーティリティメソッド

  this.parseArguments = function(args)
  {
    var params = [];
    if (args.lengs == 0) {
      window.alert('argument #1 is required.');
      return [];
    }
    params[0] = (typeof args[0] == 'string') ? document.getElementById(args[0]) : args[0];
    if (!params[0] || !params[0].appendChild) {
      window.alert('argument #1 must be a DOM Element or an existing Element ID.');
      return [];
    }
    params[1] = (args.length > 1) ? args[1].toString() : self._bgcolor;
    params[2] = (args.length > 2) ? args[2].toString() : self._color;
    params[3] = (args.length > 3 && parseInt(args[3]) > 0) ? parseInt(args[3]) : self._size;
    params[4] = (args.length > 4 && parseInt(args[4]) > 0) ? parseInt(args[4]) : self._step;
    params[3] = Math.max(params[3], params[4]);
    params[5] = (args.length > 5) ? args[5].toString() : self._bdcolor;
    params[6] = (args.length > 6 && parseInt(args[6]) > 0) ? parseInt(args[6]) : self._bdwidth;
    params[6] = Math.min(Math.floor(params[3] / params[4]), params[6]);
    return params;
  }

  switch (mode) {
  case 'line':
  case 'linear':
  case 'straight':
    this.calcMargin = function(size, level, step)
    {
      return level;
    }
    this.calcMargin2 = function(size, insize, bdsize, level, step)
    {
      return [level, bdsize];
    }
    break;
  case 'flip':
  case 'flipped':
  case 'eclipse':
    this.calcMargin = function(size, level, step)
    {
      return Math.floor(Math.base(size, size - level) / step) * step;
    }
    this.calcMargin2 = function(size, insize, bdsize, level, step)
    {
      var m0 = 0, m1 = 0;
      if (level >= bdsize) {
        m0 = self.calcMargin(insize, level - bdsize, step);
      }
      if (level < insize) {
        m1 = Math.max(self.calcMargin(size, level, step), self.calcMargin(insize, level, step));
      }
      return [m0, Math.max(bdsize, m1 - m0)];
    }
    break;
  case 'round':
  default:
    this.calcMargin = function(size, level, step)
    {
      return size - Math.floor(Math.base(size, level + step) / step) * step;
    }
    this.calcMargin2 = function(size, insize, bdsize, level, step)
    {
      var m0 = 0, m1 = 0;
      m0 = self.calcMargin(size, level, step);
      if (level < insize) {
        m1 = Math.max(self.calcMargin(insize, level, step), self.calcMargin(size, level + bdsize, step));
      }
      return [m0, Math.max(bdsize, m1 - m0)];
    }
  }

  this.intToPixels = function(num)
  {
    return parseInt(num).toString() + 'px';
  }

  this.intToPixelsVertical = function(num)
  {
    return self.intToPixels(num) + ' 0';
  }

  this.intToPixelsHorizontal = function(num)
  {
    return '0 ' + self.intToPixels(num);
  }

  // }}}
}