Jul 27, 2007
拡張機能でtext-shadowを実装できないものだろうか
CSS2からCSS3に移ったtext-shadowは、どうやらFirefox 3ではサポートされない事がほとんど確定したようだ。これでモダンブラウザでドロップシャドウを実現できないのはFirefoxだけになったな(Opera 10とSafariは対応、IEもfilterを使えば可能)。
ということで拡張機能でtext-shadowを実現するという可能性を勝手に模索してみる事にしたよ。
アルゴリズムとしては以前須藤さんがcairoで不透明度を下げた物をひたすらずらして並べるというアレです。
const SHADOW_QUALITY = 2; // 数値を上げれば上げるほど精度が落ちる
function drawShadow(aElement, aX, aY, aRadius, aColor)
{
var XMLNS = 'http://www.w3.org/1999/xhtml';
var nodes;
var d = aElement.ownerDocument;
try {
nodes = d.evaluate('descendant::text()', aElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
catch(e) {
nodes = document.evaluate('descendant::text()', aElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
var bases = [];
var node;
var wrapper = d.createElementNS(XMLNS, 'text-shadow-box');
wrapper.setAttribute('style', 'position: relative;');
var innerWrapper = d.createElementNS(XMLNS, 'text-shadow-base');
var newWrapper, newInnerWrapper;
for (var i = 0, maxi = nodes.snapshotLength; i < maxi; i++)
{
node = nodes.snapshotItem(i);
if (/^\s+$/.test(node.nodeValue)) continue;
newWrapper = wrapper.cloneNode(true);
newInnerWrapper = innerWrapper.cloneNode(true);
node.parentNode.insertBefore(newWrapper, node);
node.parentNode.removeChild(node);
newWrapper.appendChild(newInnerWrapper);
newInnerWrapper.appendChild(node);
bases.push(newWrapper);
}
var gap = Math.max(SHADOW_QUALITY, 1);
aRadius = Math.max(Math.max(aRadius, 1) / gap, 1);
var opacity = 1 / aRadius;
var shadow = d.createElementNS(XMLNS, 'text-shadow');
var newShadow;
var xOffset = Math.round(aX/2);
var yOffset = Math.round(aY/2);
var parentBox;
var display;
for (var i in bases)
{
parentBox = bases[i];
while ((display = d.defaultView.getComputedStyle(parentBox, null).getPropertyValue('display')) != 'block' && display != '-moz-box' && parentBox.parentNode)
{
parentBox = parentBox.parentNode;
}
if (parentBox == d) parentBox = d.documentElement;
var width = d.getBoxObjectFor(parentBox).width;
width -= Number(d.defaultView.getComputedStyle(parentBox, null).getPropertyValue('padding-left').match(/^[-0-9\.]+/));
width -= Number(d.defaultView.getComputedStyle(parentBox, null).getPropertyValue('padding-right').match(/^[-0-9\.]+/));
for (var j = 0, maxj = aRadius; j < maxj; j++)
{
for (var k = 0, maxk = aRadius; k < maxk; k++)
{
newShadow = shadow.cloneNode(true);
newShadow.appendChild(bases[i].firstChild.firstChild.cloneNode(true));
newShadow.setAttribute('style',
'display: block !important; margin: 0 !important; padding: 0 !important;'
+ 'width: ' + width + 'px !important;'
+ 'position: absolute !important;'
+ 'z-index: 1 !important;'
+ 'color: ' + aColor + ' !important;'
+ 'opacity: ' + opacity + ' !important;'
+ 'top: ' + (j+gap-yOffset) + 'px !important;'
+ 'left: ' + (k+gap-xOffset) + 'px !important;'
);
bases[i].appendChild(newShadow);
}
}
bases[i].firstChild.setAttribute('style', 'display: block;');
window.setTimeout(function(aNode) {
aNode.setAttribute('style', 'position: relative; z-index: 2;');
}, 0, bases[i].firstChild);
}
}
drawShadow(document.getElementsByTagName('h2')[0], 0, 0, 6, 'black');
あとSHADOW_QUALITYを1にした状態だとradiusが10とかになると相当重くなる(radiusの二乗分の個数のノードを新たに生成しているので)。
スタイルシートを解析する処理を書いたら一応ブチ込む事はできるわけですが……
追記。最初は折り返しがあるとうまく動かない物だったけど、工夫したら折り返しが合っても大丈夫にできた。
wikieditish message: Ready to edit this entry.