Created
February 2, 2012 03:11
-
-
Save aya-eiya/1721182 to your computer and use it in GitHub Desktop.
HTML5のCanvasの練習です。テキストエリアの文字をCanvasに表示します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html /> | |
<html> | |
<head> | |
<title>1_howToPutCharToCanvas.html</title> | |
<style> | |
canvas#vwrMain{ | |
border:1px solid black; | |
} | |
div#main{ | |
margin:5px; | |
} | |
</style> | |
<script> | |
function nameSpace(glb,spc){ | |
var ns = spc.split('.'); | |
var spcs = glb; | |
for(var i=0;i<ns.length;i++){ | |
spcs = spcs[ns[i]] = new Object(); | |
} | |
return spcs; | |
} | |
(function(global,_$){ | |
_$.textCanvas = function(canvasId){ | |
this.Canvas = global.document.getElementById(canvasId); | |
this.putChar = function(val){ | |
var _cxt = this.Canvas.getContext("2d"); | |
_cxt.clearRect(0,0,300,300); | |
_cxt.font = "20pt Arial"; | |
_cxt.fillText(val, 10, 50); | |
} | |
} | |
})( | |
this, | |
nameSpace(this,'aya.eiya.test') | |
); | |
</script> | |
</head> | |
<body> | |
<h1>Canvasに文字を打ち込む</h1> | |
<p>HTML5のCanvasの練習です。テキストエリアの文字をCanvasに表示します。</p> | |
<div id="main"> | |
<canvas id="vwrMain"></canvas> | |
</div> | |
<form> | |
<input type="text" onkeyup="(new aya.eiya.test.textCanvas('vwrMain')).putChar(this.value);" /> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html /> | |
<html> | |
<head> | |
<title>2_howToWriteVerticallyToCanvas.html</title> | |
<style> | |
canvas#vwrMain{ | |
border:1px solid black; | |
} | |
div#main{ | |
margin:5px; | |
} | |
</style> | |
<script> | |
function nameSpace(glb,spc){ | |
var ns = spc.split('.'); | |
var spcs = glb; | |
for(var i=0;i<ns.length;i++){ | |
spcs = spcs[ns[i]] = new Object(); | |
} | |
return spcs; | |
} | |
(function(global,_$){ | |
_$.textCanvas = function(canvasId){ | |
this.Canvas = global.document.getElementById(canvasId); | |
this.Canvas.width = 300; | |
this.Canvas.height = 300; | |
this.fontSize = 20; | |
this.putCharVertically = function(val){ | |
var _cxt = this.Canvas.getContext("2d"); | |
var hp = 0; | |
var hps = 0; | |
var vp = 0; | |
var vps = 0; | |
var chr = ''; | |
_cxt.clearRect(0,0,300,300); | |
_cxt.font = this.fontSize+"px Arial"; | |
for(var i=0;i<val.length;i++){ | |
chr = val.charAt(i); | |
if( chr.match(/[、。]/) ){ | |
hp = 0; | |
}else{ | |
hp = _cxt.measureText(chr).width; | |
} | |
vp = this.fontSize; | |
_cxt.fillText(chr, 260 - hps + this.fontSize - hp/2 , this.fontSize + vps ); | |
vps = vps + vp; | |
if(vps > this.Canvas.height - this.fontSize*2){ | |
hps += this.fontSize; | |
vps = 0; | |
} | |
} | |
} | |
} | |
})( | |
this, | |
nameSpace(this,'aya.eiya.test') | |
); | |
</script> | |
</head> | |
<body onload="cvs = new aya.eiya.test.textCanvas('vwrMain');cvs.putCharVertically(document.getElementById('inMain').value);"> | |
<h1>Canvasに縦書きで文字を打ち込む</h1> | |
<p>HTML5のCanvasの練習です。テキストエリアの文字を縦書きでCanvasに表示します。</p> | |
<p>禁則処理および字の縦方向の大きさは考慮されません。</p> | |
<div id="main"> | |
<canvas id="vwrMain"></canvas> | |
</div> | |
<form> | |
<input type="text" id="inMain" onkeyup="cvs.putCharVertically(this.value);" value="HTML5のCanvasの練習です。テキストエリアの文字を縦書きでCanvasに表示します。" /> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html /> | |
<html> | |
<head> | |
<title>3_howToSelectVerticalTextFromCanvas.html</title> | |
<style> | |
canvas#vwrMain{ | |
border:1px solid black; | |
} | |
div#main{ | |
margin:5px; | |
} | |
</style> | |
<script> | |
//nameSpace | |
function nameSpace(glb,spc){ | |
var ns = spc.split('.'); | |
var spcs = glb; | |
for(var i=0;i<ns.length;i++){ | |
spcs = spcs[ns[i]] = new Object(); | |
} | |
return spcs; | |
} | |
(function(global,_$){ | |
_$.textCanvas = function(canvasId){ | |
this.Canvas = global.document.getElementById(canvasId); | |
this.Canvas.width = 300; | |
this.Canvas.height = 300; | |
this.fontSize = 20; | |
this.charPointMap = new Array(); | |
this.text = ""; | |
this.putCharVertically = function(val){ | |
var _cxt = this.Canvas.getContext("2d"); | |
var hp = 0; | |
var hps = 0; | |
var vp = 0; | |
var vps = 0; | |
var chr = ''; | |
var stx = 0; | |
this.text = val; | |
_cxt.clearRect(0,0,300,300); | |
_cxt.font = this.fontSize+"px Arial"; | |
this.charPointMap = new Array(); | |
for(var i=0;i<val.length;i++){ | |
chr = val.charAt(i); | |
if( chr.match(/[、。]/) ){ | |
hp = 0; | |
}else{ | |
hp = _cxt.measureText(chr).width; | |
} | |
vp = this.fontSize; | |
stx = 260 - hps; | |
sty = this.fontSize + vps; | |
if( | |
( this._selectArea.st <= i && i <=this._selectArea.ed ) | |
|| | |
( this._selectArea.ed <= i && i <=this._selectArea.st ) | |
) | |
{ | |
_cxt.strokeStyle = "blue"; | |
_cxt.fillStyle = "blue"; | |
}else{ | |
_cxt.strokeStyle = "black"; | |
_cxt.fillStyle = "black"; | |
} | |
_cxt.fillText(chr, stx + this.fontSize - hp/2, sty ); | |
this.charPointMap[this.charPointMap.length]=( | |
function(stx,sty,size,c){ | |
return function(x,y){ | |
return (stx+size/2 <= x && x <= stx+size*3/2 && sty-size <= y && y <= sty)?c:-1; | |
}; | |
})(stx,sty,this.fontSize,i); | |
vps = vps + vp; | |
if(vps > this.Canvas.height - this.fontSize*2){ | |
hps += this.fontSize; | |
vps = 0; | |
} | |
} | |
} | |
this._selectArea={st:-1,ed:-1}; | |
this.Canvas.onmouseup = ( | |
function(parent){ | |
return function(e1){ | |
var st,ed; | |
parent.Canvas.onmousemove = function(e2){}; | |
if(parent._selectArea.st >= 0){ | |
if(parent._selectArea.st < parent._selectArea.ed){ | |
st = parent._selectArea.st; | |
ed = parent._selectArea.ed+1; | |
}else{ | |
st = parent._selectArea.ed; | |
ed = parent._selectArea.st+1; | |
} | |
alert(parent.text.substring(st,ed)); | |
console.log(parent.text.substring(st,ed)); | |
} | |
parent._selectArea={st:-1,ed:-1}; | |
parent.putCharVertically(parent.text); | |
} | |
})(this); | |
this.selectText = function(num){ | |
if(num < 0) return; | |
if(this._selectArea.st < 0) | |
this._selectArea.st = num; | |
this._selectArea.ed = num; | |
} | |
this.Canvas.onmousedown =( | |
function(parent){ | |
return function(e1){ | |
parent.selectText(_getCharPointFromMousePosition(e1,parent)); | |
parent.Canvas.onmousemove = function(e2){ | |
parent.putCharVertically(parent.text); | |
parent.selectText(_getCharPointFromMousePosition(e2,parent)); | |
} | |
} | |
} | |
)(this); | |
_getCharPointFromMousePosition = function(e,parent){ | |
var rect = e.target.getBoundingClientRect(); | |
var x = e.clientX - rect.left; | |
var y = e.clientY - rect.top; | |
var hits=parent.charPointMap; | |
var hit=-1; | |
for(var i=0;i<hits.length;i++){ | |
hit = hits[i](x,y); | |
if(hit>=0){ | |
return hit; | |
} | |
} | |
return -1; | |
}; | |
} | |
})( | |
this, | |
nameSpace(this,'aya.eiya.test') | |
); | |
</script> | |
</head> | |
<body onload="cvs = new aya.eiya.test.textCanvas('vwrMain');cvs.putCharVertically(document.getElementById('inMain').value);"> | |
<h1>Canvasの縦書きの文字を選択する</h1> | |
<p>HTML5のCanvasの練習です。Canvasに表示された縦書きの文字を選択します。</p> | |
<p>文字列のドラッグした範囲をアラートで表示します。</p> | |
<div id="main"> | |
<canvas id="vwrMain"></canvas> | |
</div> | |
<form> | |
<input type="text" id="inMain" onkeyup="cvs.putCharVertically(this.value);" value="HTML5のCanvasの練習です。テキストエリアの文字を縦書きでCanvasに表示します。" /> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html /> | |
<html> | |
<head> | |
<title>4_howToPutSomeVerticalTextLikeProposalFontToCanvas.html</title> | |
<style> | |
canvas#vwrMain{ | |
border:1px solid black; | |
} | |
div#main{ | |
margin:5px; | |
} | |
</style> | |
<script> | |
// お名前付けるときの約束ごと: | |
// * newできるものは大英字ではじめるよね。 | |
// * 複数の単語で構成される名前の場合はcamelCaseでつなぐよね。 | |
// * 外からアクセスして欲しくないメンバは_で始めるよね。 | |
// * ローカル変数を明示したいときは_で始めるよね。 | |
// * 英単語を略記するのはローカル変数だけだよね。 | |
// * したがって略記してある変数はあえて_で始めなくてもいいよね。 | |
// * 引数をとるメソッドは他動詞だよね。 | |
// * isかhasで始まるメソッドは、明示的にtrue or falseで値を返すよね。 | |
function nameSpace(glb,spc){ | |
var ns = spc.split('.'); | |
var spcs = glb; | |
for(var i=0;i<ns.length;i++){ | |
spcs = spcs[ns[i]] = new Object(); | |
} | |
return spcs; | |
} | |
(function(_global,_$){ | |
// フォントメジャーは、実測した文字の幅と長さを取得します。 | |
_$.FontMeasure = function (){ | |
this._unitMeasure = _global.document.createElement('div'); | |
this._unitMeasure.style.position = 'absolute'; | |
this._unitMeasure.style.top = '0'; | |
this._unitMeasure.style.left = '0'; | |
this._unitMeasure.style.overflow = 'hidden'; | |
_global.document.body.appendChild(this._unitMeasure); | |
this._canvas = _global.document.createElement('canvas'); | |
this._canvas.height = 1200; | |
this._canvas.width = 1200; | |
this._sightScale = 5; | |
this._sizeDictionary = {}; | |
this.measure = function(_font,chr){ | |
// memoize | |
if(this._sizeDictionary[_font] && this._sizeDictionary[_font][chr]){ | |
return this._sizeDictionary[_font][chr]; | |
} | |
var cxt = this._canvas.getContext('2d'); | |
var fsz = 1; | |
var uni = '1px'; | |
var rct = null; | |
var _fontSettingSize = 1; | |
var _writedImage = null; | |
var _fontMeasuredSize = 1 | |
if(_font.match(/([0-9]+(\.[0-9]+)?)(px|pt|em)/)){ | |
fsz = RegExp.$1; | |
cxt.font = | |
this._unitMeasure.style.font = _font; | |
this._unitMeasure.style.width = | |
this._unitMeasure.style.height = '1'+RegExp.$3; | |
rct = this._unitMeasure.getBoundingClientRect(); | |
uni = (rct.right - rct.left); | |
_fontSettingSize = fsz * uni; | |
cxt.fillStyle = "#000000"; | |
cxt.fillRect(0,0,this._canvas.width,this._canvas.height); | |
cxt.textBaseline = 'top'; | |
cxt.fillStyle = cxt.strokeStyle = '#ff0000'; | |
cxt.fillText(chr, 0 , 0 ); | |
_writedImage = cxt.getImageData(0,0,_fontSettingSize , _fontSettingSize); | |
_fontMeasuredSize = _measureImage(_writedImage); | |
// 調整 | |
_fontMeasuredSize.width += _fontSettingSize/20; | |
_fontMeasuredSize.height += _fontSettingSize/10; | |
_fontMeasuredSize.unit = _fontSettingSize; | |
if( !this._sizeDictionary[_font] ){ | |
this._sizeDictionary[_font] = {}; | |
} | |
this._sizeDictionary[_font][chr] = _fontMeasuredSize; | |
console.log(chr+":("+_fontMeasuredSize.width+","+_fontMeasuredSize.height+")"); | |
return this._sizeDictionary[_font][chr]; | |
} | |
return { | |
width:void(0), | |
height:void(0), | |
unit:void(0) | |
}; | |
}; | |
_measureImage = function(_writedImage){ | |
var w = _writedImage.width; | |
var x = 0; | |
var y = 0; | |
var minX = 0; | |
var maxX = 1; | |
var minY = 0; | |
var maxY = 1; | |
for(var i=0;i<_writedImage.data.length;i+=4){ | |
if( | |
_writedImage.data[i+0] > 128 // 閾値がいまいち不安 | |
){ | |
if(x<minX) minX = x; | |
if(x>maxX) maxX = x; | |
if(y<minY) minY = y; | |
if(y>maxY) maxY = y; | |
} | |
x++; | |
if(x>=w){ | |
x=0; | |
y++; | |
} | |
} | |
return { | |
width : maxX - minX + 1, | |
height: maxY - minY + 1 | |
}; | |
} | |
}; | |
// テキストキャンバスは、キャンバスのIDを指定して、そこに文字列を縦書きします。 | |
_$.TextCanvas = function(_canvasId){ | |
this._fontMeasure = new _$.FontMeasure(); | |
this._canvas = _global.document.getElementById(_canvasId); | |
this._fontSize = 20; | |
this._charPointMap = new Array(); | |
this._text = ''; | |
this.putCharVertically = function(val,fsize){ | |
if(fsize) this._fontSize = fsize; | |
var cxt = this._canvas.getContext('2d'); | |
var hp = 0; | |
var hps = 0; | |
var vp = 0; | |
var vps = 0; | |
var chr = ''; | |
var stx = 0; | |
var _font = this._fontSize+'px Arial'; | |
var msr = null | |
this._text = val; | |
cxt.clearRect(0,0,this._canvas.width,this._canvas.height); | |
cxt.font = _font; | |
this._charPointMap = new Array(); | |
for(var i=0;i<val.length;i++){ | |
chr = val.charAt(i); | |
msr = this._fontMeasure.measure(_font,chr); | |
un = msr.unit; | |
if(chr.match(/[、。]/)){ | |
hp = -un/10; | |
vps = vps - un/2; | |
}else{ | |
hp = msr.width; | |
vp = msr.height; | |
} | |
stx = this._canvas.width - un*2 - hps; | |
sty = un + vps; | |
if( | |
( this._selectArea.st <= i && i <=this._selectArea.ed ) | |
|| | |
( this._selectArea.ed <= i && i <=this._selectArea.st ) | |
) | |
{ | |
cxt.strokeStyle = 'blue'; | |
cxt.fillStyle = 'blue'; | |
}else{ | |
cxt.strokeStyle = 'black'; | |
cxt.fillStyle = 'black'; | |
} | |
cxt.textBaseLine = 'top'; | |
cxt.fillText(chr, stx + un - hp/2, sty ); | |
this._charPointMap[this._charPointMap.length]=( | |
function(stx,sty,s,c){ | |
return function(x,y){ | |
return (stx+s/2 <= x && x <= stx+s*3/2 && sty-s <= y && y <= sty)?c:-1; | |
}; | |
})(stx,sty,un,i); | |
vps = vps + vp; | |
if(vps > this._canvas.height - un*2){ | |
hps += un; | |
vps = 0; | |
} | |
} | |
} | |
this._selectArea={st:-1,ed:-1}; | |
this._canvas.onmouseup = ( | |
function(prn){ | |
return function(e1){ | |
var st,ed; | |
prn._canvas.onmousemove = function(e2){}; | |
if(prn._selectArea.st >= 0){ | |
if(prn._selectArea.st < prn._selectArea.ed){ | |
st = prn._selectArea.st; | |
ed = prn._selectArea.ed+1; | |
}else{ | |
st = prn._selectArea.ed; | |
ed = prn._selectArea.st+1; | |
} | |
console.log(prn._text.substring(st,ed)); | |
} | |
prn._selectArea={st:-1,ed:-1}; | |
prn.putCharVertically(prn._text); | |
} | |
})(this); | |
this._selectText = function(num){ | |
if(num < 0) return; | |
if(this._selectArea.st < 0) | |
this._selectArea.st = num; | |
this._selectArea.ed = num; | |
} | |
this._canvas.onmousedown =( | |
function(prn){ | |
return function(e1){ | |
prn._selectText(_getCharPointFromMousePosition(e1,prn)); | |
prn._canvas.onmousemove = function(e2){ | |
prn.putCharVertically(prn._text); | |
prn._selectText(_getCharPointFromMousePosition(e2,prn)); | |
} | |
} | |
} | |
)(this); | |
_getCharPointFromMousePosition = function(e,prn){ | |
var rect = e.target.getBoundingClientRect(); | |
var x = e.clientX - rect.left; | |
var y = e.clientY - rect.top; | |
var _hits = prn._charPointMap; | |
var _hit = -1; | |
for(var i=0;i<_hits.length;i++){ | |
_hit = _hits[i](x,y); | |
if(_hit>=0){ | |
return _hit; | |
} | |
} | |
return -1; | |
}; | |
} | |
})( | |
this, | |
nameSpace(this,'aya.eiya.test') | |
); | |
</script> | |
</head> | |
<body onload="cvs = new aya.eiya.test.TextCanvas('vwrMain');cvs.putCharVertically(document.getElementById('inMain').value);"> | |
<h1>Canvasに縦書きで文字をプロポーサル風に表示する。</h1> | |
<p>HTML5のCanvasの練習です。Canvasに縦書きの文字を、文字の高さを確認しながら表示します。</p> | |
<p>割と面倒でした。</p> | |
<div id="main"> | |
<canvas id="vwrMain" width="300" height="300"></canvas> | |
</div> | |
<form> | |
<input type="text" id="inMain" onkeyup="cvs.putCharVertically(this.value);" | |
value="HTML5のCanvasの練習です。Canvasに縦書きの文字を、文字の高さを確認しながら表示します。" /> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html /> | |
<html> | |
<head> | |
<title>5_howToPutSomeSpecialCharactorsForVerticalStyle.html</title> | |
<style> | |
canvas#vwrMain{ | |
border:1px solid black; | |
} | |
div#main{ | |
margin:5px; | |
} | |
</style> | |
<script> | |
// お名前付けるときの約束ごと: | |
// * newできるものは大英字ではじめるよね。 | |
// * 複数の単語で構成される名前の場合はcamelCaseでつなぐよね。 | |
// * 外からアクセスして欲しくないメンバは_で始めるよね。 | |
// * ローカル変数を明示したいときは_で始めるよね。 | |
// * 英単語を略記するのはローカル変数だけだよね。 | |
// * したがって略記してある変数はあえて_で始めなくてもいいよね。 | |
// * 引数をとるメソッドは他動詞だよね。 | |
// * isかhasで始まるメソッドは、明示的にtrue or falseで値を返すよね。 | |
function nameSpace(glb,spc){ | |
var ns = spc.split('.'); | |
var spcs = glb; | |
for(var i=0;i<ns.length;i++){ | |
spcs = spcs[ns[i]] = new Object(); | |
} | |
return spcs; | |
} | |
(function(_global,_$){ | |
// フォントメジャーは、実測した文字の幅と長さを取得します。 | |
_$.FontMeasure = function (){ | |
this._unitMeasure = _global.document.createElement('div'); | |
this._unitMeasure.style.position = 'absolute'; | |
this._unitMeasure.style.top = '0'; | |
this._unitMeasure.style.left = '0'; | |
this._unitMeasure.style.overflow = 'hidden'; | |
_global.document.body.appendChild(this._unitMeasure); | |
this._canvas = _global.document.createElement('canvas'); | |
this._canvas.height = 1200; | |
this._canvas.width = 1200; | |
this._sightScale = 3; | |
this._sizeDictionary = {}; | |
this.measure = function(_font,chr){ | |
// memoize | |
if(this._sizeDictionary[_font] && this._sizeDictionary[_font][chr]){ | |
return this._sizeDictionary[_font][chr]; | |
} | |
var cxt = this._canvas.getContext('2d'); | |
var fsz = 1; | |
var uni = '1px'; | |
var rct = null; | |
var _fontSettingSize = 1; | |
var _writedImage = null; | |
var _fontMeasuredSize = 1 | |
if(_font.match(/([0-9]+(\.[0-9]+)?)(px|pt|em)/)){ | |
fsz = RegExp.$1; | |
cxt.font = | |
this._unitMeasure.style.font = _font; | |
this._unitMeasure.style.width = | |
this._unitMeasure.style.height = '1'+RegExp.$3; | |
rct = this._unitMeasure.getBoundingClientRect(); | |
uni = (rct.right - rct.left); | |
_fontSettingSize = fsz * uni; | |
cxt.fillStyle = "#000000"; | |
cxt.fillRect(0,0,this._canvas.width,this._canvas.height); | |
cxt.textBaseline = 'top'; | |
cxt.fillStyle = cxt.strokeStyle = '#ff0000'; | |
cxt.fillText(chr, _fontSettingSize , _fontSettingSize ); | |
_writedImage = cxt.getImageData( 0 , 0 ,_fontSettingSize * this._sightScale , _fontSettingSize * this._sightScale); | |
_fontMeasuredSize = _measureImage(_writedImage); | |
_fontMeasuredSize.unit = _fontSettingSize; | |
if(_fontMeasuredSize.startX > 0)_fontMeasuredSize.startX = _fontMeasuredSize.startX -_fontSettingSize; | |
if(_fontMeasuredSize.startY > 0)_fontMeasuredSize.startY = _fontMeasuredSize.startY -_fontSettingSize; | |
console.log( chr + ":" + _fontMeasuredSize.startX + "," +_fontMeasuredSize.startY); | |
if( !this._sizeDictionary[_font] ){ | |
this._sizeDictionary[_font] = {}; | |
} | |
this._sizeDictionary[_font][chr] = { | |
width :_fontMeasuredSize.width , | |
height:_fontMeasuredSize.height, | |
startX:_fontMeasuredSize.startX, | |
startY:_fontMeasuredSize.startY, | |
unit :_fontMeasuredSize.unit | |
}; | |
return this._sizeDictionary[_font][chr]; | |
} | |
return { | |
width :void(0), | |
height:void(0), | |
startX:void(0), | |
startY:void(0), | |
unit :void(0) | |
}; | |
}; | |
_measureImage = function(_writedImage){ | |
var w = _writedImage.width; | |
var h = _writedImage.height; | |
var x = 0; | |
var y = 0; | |
var minX = w; | |
var maxX = 1; | |
var minY = h; | |
var maxY = 1; | |
var i=0; | |
var j=0; | |
for(i=0;i<_writedImage.data.length;i+=4){ | |
if( | |
_writedImage.data[i+0] > 0 | |
){ | |
if(x<minX){ minX = x; } | |
if(x>maxX){ maxX = x; } | |
if(y<minY){ minY = y; } | |
if(y>maxY){ maxY = y; } | |
} | |
x++; | |
if(x>=w){ | |
x=0; | |
y++; | |
} | |
} | |
return { | |
width : (maxX >= minX)? maxX - minX : w, | |
height: (maxY >= minY)? maxY - minY : 1, | |
startX: (maxX >= minX)? minX : -1, | |
startY: (maxY >= minY)? minY : -1 | |
}; | |
} | |
}; | |
// テキストキャンバスは、キャンバスのIDを指定して、そこに文字列を縦書きします。 | |
_$.TextCanvas = function(_canvasId,_font){ | |
_initFont = function (_font){ | |
if(!_font) return "12pt Arial"; | |
if(!_font.match(/([0-9]+(\.[0-9]+)?)(px|pt|em)/)){ | |
return "12pt "+_font; | |
}else{ | |
return _font; | |
} | |
}; | |
_getCharPointFromMousePosition = function(e,prn){ | |
var rect = e.target.getBoundingClientRect(); | |
var x = e.clientX - rect.left; | |
var y = e.clientY - rect.top; | |
var _hits = prn._charPointMap; | |
var _hit = -1; | |
for(var i=0;i<_hits.length;i++){ | |
_hit = _hits[i](x,y); | |
if(_hit>=0){ | |
return _hit; | |
} | |
} | |
return -1; | |
}; | |
this._fontMeasure = new _$.FontMeasure(); | |
this._canvas = _global.document.getElementById(_canvasId); | |
this._font = _initFont(_font); | |
this._charPointMap = new Array(); | |
this._text = ''; | |
this.putCharVertically = function(val,_font){ | |
if(_font) this._font = _initFont(_font); | |
var cxt = this._canvas.getContext('2d'); | |
var hp = 0; | |
var hps = 0; | |
var vp = 0; | |
var vps = 0; | |
var chr = ''; | |
var stx = 0; | |
var premsr = null; | |
var msr = null; | |
var tmp = 0; | |
this._text = val; | |
cxt.clearRect(0,0,this._canvas.width,this._canvas.height); | |
cxt.font = this._font; | |
this._charPointMap = new Array(); | |
for(var i=0;i<val.length;i++){ | |
chr = val.charAt(i); | |
premsr = msr; | |
msr = this._fontMeasure.measure(this._font,chr); | |
un = msr.unit; | |
// small Charactors | |
if(chr.match(/[、。.,]/)){ | |
hp = msr.width - un - msr.startX; | |
vp = (premsr)?msr.height + premsr.height/2 : msr.height; | |
vps = (premsr)?vps - premsr.height/2 : vps; | |
}else{ | |
hp = msr.width + msr.startX; | |
vp = msr.height + msr.startY; | |
} | |
if( | |
( this._selectArea.st <= i && i <=this._selectArea.ed ) | |
|| | |
( this._selectArea.ed <= i && i <=this._selectArea.st ) | |
) | |
{ | |
cxt.strokeStyle = 'blue'; | |
cxt.fillStyle = 'blue'; | |
}else{ | |
cxt.strokeStyle = 'black'; | |
cxt.fillStyle = 'black'; | |
} | |
cxt.textBaseLine = 'top'; | |
// 90度回転表示する必要がある文字種 | |
if("{}()[]「」『』()【】[]{}…─━ー==~||".indexOf(chr) > -1){ | |
stx = this._canvas.width - un*2 - hps; | |
sty = un/4 + vps - msr.startX; | |
cxt.rotate(Math.PI / 2); | |
cxt.fillText(chr, sty , - (stx + un - hp/2)); | |
cxt.rotate(-Math.PI / 2); | |
hp = msr.height; | |
vp = msr.width; | |
}else{ | |
stx = this._canvas.width - un*2 - hps; | |
sty = un + vps; | |
cxt.fillText(chr, stx + un - hp/2, sty ); | |
} | |
this._charPointMap[this._charPointMap.length]=( | |
function(stx,sty,s,c){ | |
return function(x,y){ | |
return (stx+s/2 <= x && x <= stx+s*3/2 && sty-s <= y && y <= sty)?c:-1; | |
}; | |
})(stx,sty,un,i); | |
vps = vps + vp; | |
if(chr.charCodeAt(0) == 10 || vps > this._canvas.height - un*2){ | |
hps += un*1.5; | |
vps = 0; | |
} | |
} | |
} | |
this._selectArea={st:-1,ed:-1}; | |
this._canvas.onmouseup = ( | |
function(prn){ | |
return function(e1){ | |
var st,ed; | |
prn._canvas.onmousemove = function(e2){}; | |
if(prn._selectArea.st >= 0){ | |
if(prn._selectArea.st < prn._selectArea.ed){ | |
st = prn._selectArea.st; | |
ed = prn._selectArea.ed+1; | |
}else{ | |
st = prn._selectArea.ed; | |
ed = prn._selectArea.st+1; | |
} | |
console.log(prn._text.substring(st,ed)); | |
} | |
prn._selectArea={st:-1,ed:-1}; | |
prn.putCharVertically(prn._text); | |
} | |
})(this); | |
this._selectText = function(num){ | |
if(num < 0) return; | |
if(this._selectArea.st < 0) | |
this._selectArea.st = num; | |
this._selectArea.ed = num; | |
} | |
this._canvas.onmousedown =( | |
function(prn){ | |
return function(e1){ | |
prn._selectText(_getCharPointFromMousePosition(e1,prn)); | |
prn._canvas.onmousemove = function(e2){ | |
prn.putCharVertically(prn._text); | |
prn._selectText(_getCharPointFromMousePosition(e2,prn)); | |
} | |
} | |
} | |
)(this); | |
} | |
})( | |
this, | |
nameSpace(this,'aya.eiya.test') | |
); | |
</script> | |
</head> | |
<body onload="cvs = new aya.eiya.test.TextCanvas('vwrMain');cvs.putCharVertically(document.getElementById('inMain').value);"> | |
<h1>Canvasに縦書きで特殊な文字も自然に表示する。</h1> | |
<p>HTML5のCanvasの練習です。Canvasに縦書きで括弧などの文字を自然に表示します。</p> | |
<p>せっかくなので改行にも対応しました。</p> | |
<div id="main"> | |
<canvas id="vwrMain" width="300" height="300"></canvas> | |
</div> | |
<form> | |
Font : <input type="text" id="font" value="12pt Arial" /><br /> | |
Text <br /><textarea id="inMain" | |
style="width:280px;height:120px" | |
onkeyup="cvs.putCharVertically(this.value,document.getElementById('font').value);"> | |
HTML5のCanvasの練習です。Canvasに縦書きで括弧などの文字を90度回転させて自然な形で表示します。 | |
「このような(括弧)が(自然な)形で{表示されていると}」 | |
{成功と『言え』ます…} | |
</textarea> | |
</form> | |
</body> | |
</html> |
- 横書きフォントで無理やり縦書きにする場合の記号への対応
横書きフォントを無理やり縦書きに使用する場合、括弧などの文字は90度回転させる必要があります。
5_howToPutSomeSpecialCharactorsForVerticalStyle.htmlではその対応をしています。
ついでに、改行への対応とフォントの設定をより自由度の高い実装に変更しました。
あと、ローカル関数の出現順が気になったので、ローカル関数は一番上に持っていっています。
文字を90度回転させての文字表示は、その文字を回転させて描画するよりも、コンテキストを90度回転→文字を90度回転させた座標に描画→コンテキストを-90度回転とすると実装が大変楽です。
横書きフォントなので、ズレがあります。これを考慮して微調整を行う必要があります。この微調整は、フォント(ファミリー&�サイズ)によるのでもし本気でやろうとするなら、フォントごとに微調整のために辞書データベースを作成しておくとよいでしょう。
お疲れ様です。
@aya-eiya、おはようございます。
縦書きをキャンバスにレンダリングするデモを公開していただき、ありがとうございます。🙇♂️
彼らはとても役に立ちます。😊
私は縦書きをfabric.jsフレームワークに統合しています。🙂
Webアプリに縦書きを実装してみましたか。
できれば、縦書きについてのより高度な例を共有していただけませんか。
よろしくお願いいたします。🙇♂️
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Canvasが提供する機能では、文字の高さを得ることはできないらしいです。縦書きするときに縦をきっちり詰めて文字を表示したい場合は、自分で文字の高さを測るしかないようです。
というわけで、4_howToPutSomeVerticalTextLikeProposalFontToCanvas.htmlで実装して見ました。
内容の前に、4ではやることが多いので流石に長くなってきました。
自分で書いていて混乱してきそうなので、命名だけでもお約束ごとを定義しておきます。
お約束ごとを適用したので、3と比較すると意味は同じでも行が変わっている部分が出ています。対比して見ている人はご注意ください。
aya.eiya.test.TextCanvasの実装はほとんど変更がありません。ただ、描画の際に実測した文字の高さや幅を使うようになっています。
大きく変わったのは、aya.eiya.test.FontMeasureを追加したことです。
実装すると長くなってしまいましたが、このクラス(?)の機能はごく単純です。FontMeasureは、文字の大きさを測るメソッドmeasure()のみを公開します。他のメンバは非公開です。JavaScriptにはprivateはないので、お約束ごとにすぎませんけれども。
measureの中身をかいつまんで説明すると、
1ptや1emが何pxか調べる。これを調べるために、ダミーのdiv要素を作成しています。
求めた単位あたりのpxで、Fontに設定されている文字の大きさ(大きさの指定が必須です)が何pxかを調べる。
実際に描画して、その描画した範囲から高さと幅を求める。描画先は、ダミーで作成している十分に大きな(1200平方)のCanvasです。
Fontに設定されている大きさをunit、実測した高さと幅をそれぞれheight,widthとした表を返します。(実際は返す前に少し調整をしています)
この値を利用してCanvasに描画をすると、縦書きにした文字がちょっとプロポーサル風に詰め込んで表示されている。という寸法です。