|
|
HTMLの表現で苦手なことの1つに「斜めの線を引く」ということがあります。縦横の線であればborderを使ってそれらしいものを表現することができますが、斜めはそうもいきません。
1ドットのdiv要素を斜めに並べて表現するという荒業もありますが、ここはきちんと図形を描くという方法でやりたいものです。ただ、HTMLにおける図形の扱いはHTML5が出てくるまでは紆余曲折あって、各プラットフォーム共通の方法があるわけではありませんでした。そして、HTML5に対応したブラウザが出てきた現状であっても、世の中の多くの人が古いブラウザを使っているという現実があるので、より多くの人を救うためにはSVG、VML、Canvasの3パターンを実装する必要があります。
以下では、ご覧のWebページ上でフリーハンドで落書きをするプログラムを作ってみました。3パターンに対応しているので、かなり長ったらしくなっています。
[ サンプルの実行]←クリックするとこの画面上でマウスを使った落書きができるようになります。何かキーを押すと落書きが消されて元に戻ります。bookmarklet化してあるので、ブックマーク(お気に入り)に登録しておくと、任意のWebページ上で実行可能です。 var grafitti = @INCLUDE(grafitti.js);
grafitti(document.body);
- grafitti.js
/**
* grafitti.js - 画面上に落書きをする
*/
function(panel){
var canvas = {};
//イベントを他の要素に取られないように透明のマスクを被せておきます。
var mask = panel.appendChild(document.createElement('div'));
mask.style.position = "absolute";
mask.style.left = 0;
mask.style.top = 0;
mask.style.width = panel.offsetWidth + "px";
mask.style.height = panel.offsetHeight + "px";
mask.style.cursor = "hand";
mask.style.backgroundColor = "#FFFFFF";
mask.style.opacity = 0;
mask.style.filter = 'alpha(opacity=0)';
if (document.createElementNS) { //FireFoxのSVG
var canvas = panel.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'svg'));
canvas.style.position = "absolute";
canvas.style.left = 0;
canvas.style.top = 0;
canvas.style.width = panel.offsetWidth + 'px';
canvas.style.height = panel.offsetHeight + 'px';
canvas.style.cursor = "hand";
canvas.drawStart = function(evt) {
evt = fixEvent(evt);
window.status = 'start[' + evt.offsetX + ',' + evt.offsetY + ']';
var s = canvas.shape = canvas.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "polyline"));
s.setAttribute('points', evt.offsetX + ',' + evt.offsetY + ' ');
s.setAttribute('stroke', 'black');
s.setAttribute('fill', 'none');
s.setAttribute('stroke-width', '5');
s.setAttribute('stroke-linecap', 'round');
panel.onmousemove = canvas.draw;
return false;
}
canvas.draw = function(evt) {
evt = fixEvent(evt);
window.status = 'draw[' + evt.offsetX + ',' + evt.offsetY + ']';
canvas.shape.setAttribute('points', canvas.shape.getAttribute('points') + ' ' + evt.offsetX + ',' + evt.offsetY + ' ');
return false;
}
canvas.drawEnd = function(evt) {
if (!panel.onmousemove) return;
evt = fixEvent(evt);
window.status = 'end[' + evt.offsetX + ',' + evt.offsetY + ']';
panel.onmousemove = null;
}
} else if (document.namespaces) { //IEのVML
var left = 0, top = 0;
var p = panel;
while (p) {
left += p.offsetLeft;
top += p.offsetTop;
p = p.offsetParent;
}
if (!document.namespaces.v) {
document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
document.createStyleSheet().addRule("v\\:*", "behavior: url(#default#VML);");
}
canvas = panel.appendChild(mask.cloneNode());
canvas.style.backgroundColor = 'transparent';
canvas.style.filter = '';
canvas.drawStart = function(evt) {
evt = fixEvent(evt);
window.defaultStatus = 'start[' + evt.offsetX + ',' + evt.offsetY + ']';
//v:shapeで線の先端の形状を指定する方法がわからないので、ちょっと角ばってしまいます。
var s = canvas.shape = document.createElement('v:shape');
s.style.position = 'absolute';
s.style.left = left + 'px';
s.style.top = top + 'px';
s.style.width = canvas.offsetWidth + 'px';
s.style.height = canvas.offsetHeight + 'px';
s.setAttribute('strokeweight', 5);
s.setAttribute('strokecolor', 'black');
s.setAttribute('filled', 'false');
s.setAttribute('path', 'm ' + evt.offsetX + ',' + evt.offsetY);
s.setAttribute('coordsize', canvas.offsetWidth + ',' + canvas.offsetHeight);
s.onmousemove = function(evt) {
evt = fixEvent();
return false;
}
canvas.appendChild(s);
canvas.onmousemove = canvas.draw;
return false;
}
canvas.draw = function(evt) {
evt = fixEvent(evt);
window.defaultStatus = 'draw[' + evt.offsetX + ',' + evt.offsetY + ']';
var s = canvas.shape;
s.path.value = s.path.value.replace(/ e$/, '') + ' l ' + evt.offsetX + ',' + evt.offsetY + ' e';
return false;
}
canvas.drawEnd = function(evt) {
if (!canvas.onmousemove) return;
evt = fixEvent(evt);
window.status = 'end[' + evt.offsetX + ',' + evt.offsetY + ']';
canvas.onmousemove = null;
return false;
}
} else { //HTMLのCanvas
var canvas = document.createElement('canvas');
canvas.setAttribute('width', panel.clientWidth + 'px');
canvas.setAttribute('height', panel.clientHeight + 'px');
canvas.style.position = "absolute";
canvas.style.left = 0;
canvas.style.top = 0;
canvas.style.cursor = "hand";
panel.appendChild(canvas);
canvas.drawStart = function(evt) {
evt = fixEvent(evt);
window.status = 'start[' + evt.offsetX + ',' + evt.offsetY + ']';
var ctx = canvas.getContext('2d');
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(evt.offsetX, evt.offsetY);
panel.onmousemove = canvas.draw;
}
canvas.draw = function(evt) {
evt = fixEvent(evt);
window.status = 'draw[' + evt.offsetX + ',' + evt.offsetY + ']';
var ctx = canvas.getContext('2d');
ctx.lineTo(evt.offsetX, evt.offsetY);
ctx.stroke();
}
canvas.drawEnd = function(evt) {
if (!canvas.onmousemove) return;
evt = fixEvent(evt);
window.status = 'end[' + evt.offsetX + ',' + evt.offsetY + ']';
panel.onmousemove = null;
}
}
panel.onmousedown = canvas.drawStart;
document.onmouseup = canvas.drawEnd;
document.onkeypress = function(evt) {
panel.onmousedown = null;
document.onmouseup = null;
document.onkeypress = null;
document.onmousemove = null;
panel.removeChild(canvas);
panel.removeChild(mask);
}
//以下のコードは http://shorindo.com/research:1308704774 からの切り貼りで、本質ではありません。
//ただし、offsetXXの値を持たないsvg要素に対応するために改変しています。
var fixEvent = function(evt) {
if (!window.event) {
if (typeof evt.offsetX != 'number') { //not safari
evt.offsetX = evt.pageX;
evt.offsetY = evt.pageY;
var el = evt.target;
while (el) {
evt.offsetX -= el.offsetLeft ? el.offsetLeft : 0;
evt.offsetY -= el.offsetTop ? el.offsetTop : 0;
el = el.offsetParent ? el.offsetParent : el.parentNode;
}
}
} else {
evt = window.event;
evt.target = evt.srcElement;
evt.preventDefault = function() { this.returnValue = false; };
evt.stopPropagation = function() { this.cancelBubble = true; };
switch(evt.type) {
case 'click':
case 'mousedown':
case 'mouseup':
case 'mousemove':
evt.which = evt.button==4 ? 3 : evt.button;
if (document.compatMode == 'CSS1Compat') {
evt.pageX = evt.clientX + document.documentElement.scrollLeft;
evt.pageY = evt.clientY + document.documentElement.scrollTop;
} else {
evt.pageX = evt.clientX + document.body.scrollLeft;
evt.pageY = evt.clientY + document.body.scrollTop;
}
break;
}
}
evt.preventDefault();
evt.stopPropagation();
evt.returnValue = false;
return evt;
}
}
2011/07/06 初版リリース
2011/07/29 bookmarklet化
|
|
|