角丸

rsky2006-03-16

http://pro.html.it/esempio/nifty/ がすごく面白い。そしてそれを参考にしつつ境界線が引けるようにしたものを作ってみました。
下のサンプルは Acid2 Test の BASE64 エンコードされた PNG 画像を JavaScript で生成・複製するテストも兼ねていて(というかこのネタを先にやろうとしていた)、Safari 2.0.3 ではスクリーンショットのようにレンダリングされます。
h1 要素などを一括で指定するときは

var h = document.getElementsByTagName('h1');
for (var i = 0; i < h.length; h++) {
  Rounder.round(h[i]);
}

みたいな感じで。

rounder.js

var Rounder = new function()
{
  var self = this;

  this._bgcolor = '#fff';
  this._color   = '#ccc';
  this._border  = null;
  this._size = 5;
  this._step = 1;

  var css = '<style type="text/css">'
      + 'span.rounderElement {'
      + 'display:block;'
      + 'margin:0;'
      + 'padding:0;'
      + 'overflow:hidden;'
      + 'overflow-x:hidden;'
      + 'overflow-y:hidden;'
      + '}'
      + 'span.rounderCorner {'
      + 'height:' + this._size.toString() + 'px;'
      + 'background-color:' + this._color + ';'
      + '}'
      + 'span.rounderLine {'
      + 'height:' + this._step.toString() + 'px;'
      + 'background-color:' + this._bgcolor + ';'
      + '}'
      + '</style>';
  document.write(css);

  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 size = args[3];
    if (target.childNodes && target.childNodes.length) {
      if ((typeof size != 'number') || size < 1) {
        size = self._size;
      }
      var padding = size.toString() + 'px';
      if (args[5]) {
        var border = args[4].toString() + 'px solid ' + args[5];
        for (var i = 0; i < target.childNodes.length; i++) {
          if (target.childNodes[i].style) {
            target.childNodes[i].style.borderLeft = border;
            target.childNodes[i].style.borderRight = border;
            target.childNodes[i].style.paddingLeft = padding;
            target.childNodes[i].style.paddingRight = padding;
          }
        }
      } else {
        for (var i = 0; i < target.childNodes.length; i++) {
          if (target.childNodes[i].style) {
            target.childNodes[i].style.paddingLeft = padding;
            target.childNodes[i].style.paddingRight = padding;
          }
        }
      }
    }
    self.roundTop.apply(this, arguments);
    self.roundBottom.apply(this, arguments);
  }

  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] : self._bgcolor;
    params[2] = (args.length > 2) ? args[2] : 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[5] = (args.length > 5) ? args[5] : self._border
    return params;
  }

  this.calcMargin = function(z, y)
  {
    return z - Math.round(Math.sqrt(Math.pow(z, 2) - Math.pow(y, 2)));
  }

  this.createCorner = function(color, size)
  {
    var corner = document.createElement('span');
    corner.className = 'rounderElement rounderCorner';
    if (color && color != self._color) {
      corner.style.backgroundColor = color;
    }
    if (size && size != self._size) {
      corner.style.height = size.toString() + 'px';
    }
    return corner;
  }

  this.createLine = function(bgcolor, step, margin)
  {
    var line = document.createElement('span');
    line.className = 'rounderElement rounderLine';
    if (bgcolor && bgcolor != self._bgcolor) {
      line.style.backgroundColor = bgcolor;
    }
    if (step && step != self._step) {
      line.style.height = step.toString() + 'px';
    }
    line.style.marginLeft = line.style.marginRight = margin.toString() + 'px';
    return line;
  }

  this.createTop = function(bgcolor, color, size, step, border)
  {
    var top = self.createCorner(color, size);
    var i, line;
    if (border) {
      var margin1, margin2, subline;
      for (i = 0; i < size - step; i += step) {
        margin1 = self.calcMargin(size, i);
        margin2 = Math.max(step, self.calcMargin(size, i + step) - margin1);
        line = self.createLine(border, step, margin1);
        subline = self.createLine(bgcolor, step, margin2);
        top.insertBefore(line, top.firstChild).appendChild(subline);
      }
      line = self.createLine(border, step, self.calcMargin(size, i));
      top.insertBefore(line, top.firstChild);
    } else {
      for (i = 0; i < size; i += step) {
        line = self.createLine(bgcolor, step, self.calcMargin(size, i));
        top.insertBefore(line, top.firstChild);
      }
    }
    return top;
  }

  this.createBottom = function(bgcolor, color, size, step, border)
  {
    var bottom = self.createCorner(color, size);
    var i, line;
    if (border) {
      var margin1, margin2, subline;
      for (i = 0; i < size - step; i += step) {
        margin1 = self.calcMargin(size, i);
        margin2 = Math.max(step, self.calcMargin(size, i + step) - margin1);
        line = self.createLine(border, step, margin1);
        subline = self.createLine(bgcolor, step, margin2);
        bottom.appendChild(line).appendChild(subline);
      }
      line = self.createLine(border, step, self.calcMargin(size, i));
      bottom.appendChild(line);
    } else {
      for (i = 0; i < size; i += step) {
        line = self.createLine(bgcolor, step, self.calcMargin(size, i));
        bottom.appendChild(line);
      }
    }
    return bottom;
  }
}

test.html

<html>
<head>
<title>test</title>
<script type="text/javascript" src="rounder.js"></script>
<script type="text/javascript">
var b64
  = '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';

window.onload = function() {
  var txt = document.createTextNode(b64.replace(/(.{72})/g, '$1?n'));
  try {
    var obj1 = document.createElement('object');
    obj1.setAttribute('data', 'data:image/png;base64,' + b64);
    var obj2 = obj1.cloneNode();
    obj2.style.width = '276px';
    obj2.style.height = '72px';
    obj2.style.opacity = '0.5';
  } catch (e) {
    window.alert(e.toString());
    if (!obj1) { obj1 = document.createTextNode('Error1'); }
    if (!obj2) { obj2 = document.createTextNode('Error2'); }
  }

  document.getElementById('pre').appendChild(txt);
  document.getElementById('flt').appendChild(obj1);
  document.getElementById('abs').appendChild(obj2);

  Rounder.roundAndPad(document.body, '#fff', '#8af', 15, 1, '#000');
}
</script>
<style type="text/css">
body { margin:10px; padding:0px; background-color:#8af; }
#pre { position:relative; white-space:pre; margin:0px; background-color:#fff;
  font-family:monospace; font-size:12px; line-height:100%; }
#abs { position:absolute; bottom:10%; left:10%; z-index:1; font:36px bold; color:red; }
#flt { float:left; font:36px bold; color:red; }
</style>
</head>
<body>
<div id="pre"><span id="abs"></span><span id="flt"></span></div>
</body>
</html>

そういえば角丸で思い出した。
昔、角丸の名刺配ってたときがあって、今はN天堂にいるF野にカドマルというあだ名を付けられたっけ。