Skip to content

Instantly share code, notes, and snippets.

@aya-eiya
Created February 2, 2012 03:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aya-eiya/1721182 to your computer and use it in GitHub Desktop.
Save aya-eiya/1721182 to your computer and use it in GitHub Desktop.
HTML5のCanvasの練習です。テキストエリアの文字をCanvasに表示します。
<!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>
<!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>
<!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>
<!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>
<!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>
@aya-eiya
Copy link
Author

aya-eiya commented Feb 3, 2012

  • 主処理に関係の無い部分について補足

大域を汚さないようnameSpace関数を定義しています。

無名関数で主処理の開始点を書くことで、大域を保護する方法は良くある手段です。
この例ではそれに加えて、大域を示すthisとnameSpace関数を用いて作ったオブジェクトを引数として突っ込んでいます。そうすることで、この主処理を書く域内では、大域を指すものとしてglobalを、自分で作ったnameSpaceを_$として明示的に利用できています。

大域を保護する方法はいくらでもありますが、ボクはこの書き方が端的で分かりやすい気がしています。

もっと大域を保護したい場合は、nameSpace関数をも無名関数の域内に閉じ込めればよいのですが、普通、nameSpace関数は業務領域共通で利用するために定義します。よって、あえてnameSpace関数は大域に定義しています。
もっとも、一番の理由は「面倒だから」ですが。

@aya-eiya
Copy link
Author

aya-eiya commented Feb 3, 2012

  • 閉包の利用

選択機能を実装する際には、閉包を用いてその定義時点の環境を保存しています。
具体的には、charPointMapに含まれる文字列の位置判定関数を作り出す時です。
位置を判定する時に、描画した時点の環境を利用して関数を作成しています。

こうすることで、使用時にはx,yのみの単純な引数の関数で、その返り値が定義当時の変数の値によるという関数を大量に作り出せます。

また、イベント処理においても閉包を用いています。
どうやら、イベント処理関数では定義時のスコープと異なる関数が作成されるようです(ボクも初めて気がつきました)。
詳しく調べたわけではないので予想ですが、

function myClass(DOMObject){
  this.DOMObject = DOMObject;
  this.myMethod = function(){alert(this);};  // 1st
  this.DOMObject.onmousedown = function(e){alert(this);}; // 2nd
}

とした時、1st行を実行した場合は、myClassのインスタンス(?)がalert関数に渡されますが、2ndでは、DOMObjectがalert関数に渡されるようです。

今回はイベント処理にmyClassのインスタンス(?)を渡したかったので、定義時点のthisを保存する形で閉包を用いています。
いやぁ、驚きです。

まだまだ知らないことがいっぱいで楽しいです。

@aya-eiya
Copy link
Author

aya-eiya commented Feb 3, 2012

  • プロポーサル風に文字を表示してみる

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に描画をすると、縦書きにした文字がちょっとプロポーサル風に詰め込んで表示されている。という寸法です。

@aya-eiya
Copy link
Author

aya-eiya commented Feb 5, 2012

  • 横書きフォントで無理やり縦書きにする場合の記号への対応

横書きフォントを無理やり縦書きに使用する場合、括弧などの文字は90度回転させる必要があります。
5_howToPutSomeSpecialCharactorsForVerticalStyle.htmlではその対応をしています。
ついでに、改行への対応とフォントの設定をより自由度の高い実装に変更しました。
あと、ローカル関数の出現順が気になったので、ローカル関数は一番上に持っていっています。

文字を90度回転させての文字表示は、その文字を回転させて描画するよりも、コンテキストを90度回転→文字を90度回転させた座標に描画→コンテキストを-90度回転とすると実装が大変楽です。
横書きフォントなので、ズレがあります。これを考慮して微調整を行う必要があります。この微調整は、フォント(ファミリー&�サイズ)によるのでもし本気でやろうとするなら、フォントごとに微調整のために辞書データベースを作成しておくとよいでしょう。

@canhnht
Copy link

canhnht commented Jul 1, 2020

お疲れ様です。
@aya-eiya、おはようございます。
縦書きをキャンバスにレンダリングするデモを公開していただき、ありがとうございます。🙇‍♂️
彼らはとても役に立ちます。😊
私は縦書きをfabric.jsフレームワークに統合しています。🙂
Webアプリに縦書きを実装してみましたか。
できれば、縦書きについてのより高度な例を共有していただけませんか。
よろしくお願いいたします。🙇‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment