Skip to content

Instantly share code, notes, and snippets.

@ethertank
Last active December 10, 2015 03:28
Show Gist options
  • Save ethertank/4374590 to your computer and use it in GitHub Desktop.
Save ethertank/4374590 to your computer and use it in GitHub Desktop.
CanvasRenderingContext2D.prototype を拡張
// var 0.1.6
(function() {
"use strict";
if(!HTMLCanvasElement) return;
var CRC2DP = CanvasRenderingContext2D.prototype,
PI_PER_180 = Math.PI / 180;
// コンテキストの内容を全消去
CRC2DP.clearAll = function() {
var c = this;
c.save();
cc.setTransform(1, 0, 0, 1, 0, 0); //変形対応
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
c.restore();
// another way: c.canvas.width = c.canvas.width;
};
// 中心座標を指定してコンテキストを回転
CRC2DP.rotateContext = function(deg, centerX, centerY) {
var c = this;
c.translate(centerX, centerY);
c.rotate(deg * PI_PER_180);
c.translate(-centerX, -centerY);
};
// 引数に指定した関数内で描画した図形で既存のコンテキストを切り抜く。要らんっちゃ要らん。
CRC2DP.clipWithFunction = function(fn) {
var c = this;
c.save();
c.globalCompositeOperation = "destination-out";
fn();
c.restore();
};
// 多角形の座標オブジェクトを持つ配列を返す
CRC2DP.getPolygonPoints = function(x, y, size, edgesNum, offset) {
var deg, points = [],
i = 0,
Z = 360 / edgesNum;
offset = (offset ? offset : 0) - 90;
for (; i < edgesNum; ++i) {
deg = (Z * i + offset) * PI_PER_180;
points[i] = [Math.cos(deg) * size + x, Math.sin(deg) * size + y];
}
return points;
};
// 多角形のパスを作成
CRC2DP.polygon = function(x, y, size, edgesNum, offset) {
var c = this,
points = c.getPolygonPoints(x, y, size, edgesNum, offset),
pl = points.length,
i = 0,
ps;
c.moveTo(points[0].x, points[0].y);
for (; i < pl; ++i) {
ps = points[i];
c.lineTo(ps[0], ps[1]);
}
};
// 塗りつぶし多角形を描画
CRC2DP.fillPolygon = function(x, y, size, edgesNum, offset) {
var c = this;
c.beginPath();
c.polygon(x, y, size, edgesNum, offset);
c.closePath();
c.fill();
};
// 多角形の線画を描画
CRC2DP.strokePolygon = function(x, y, size, edgesNum, offset) {
var c = this;
c.beginPath();
c.polygon(x, y, size, edgesNum, offset);
c.closePath();
c.stroke();
};
// 塗りと線を持つ多角形を描画
CRC2DP.drawPolygon = function(x, y, size, edgesNum, offset) {
var c = this;
c.beginPath();
c.polygon(x, y, size, edgesNum, offset);
c.closePath();
c.fill();
c.stroke();
};
// 左、上、右、下 の座標を指定し、楕円パスを作成
// http://spphire9.wordpress.com/2010/03/08/ベジェ曲線で楕円を描く3/ より
// おそらくよりシンプルに出来るが、まだ実現できていない(数学むずい)
// こんなんきた : http://www.w3.org/html/wg/drafts/2dcontext/master/#dom-context-2d-ellipse
// start/end Angle か…ポリフィるのきつくね?
CRC2DP.ellipse = function(L, T, R, B) {
var c = this,
halfW = (R - L) / 2,
halfH = (B - T) / 2,
C = L + halfW,
M = T + halfH,
cw = 4 * (Math.sqrt(2) - 1) * halfW / 3,
ch = 4 * (Math.sqrt(2) - 1) * halfH / 3,
p1 = C + cw,
p2 = C - cw,
p3 = M - ch,
p4 = M + ch;
c.moveTo(C, T);
c.bezierCurveTo(p1, T, R, p3, R, M);
c.bezierCurveTo(R, p4, p1, B, C, B);
c.bezierCurveTo(p2, B, L, p4, L, M);
c.bezierCurveTo(L, p3, p2, T, C, T);
};
// 楕円をストローク
CRC2DP.strokeEllipse = function(L, T, R, B) {
var c = this;
c.beginPath();
c.ellipse(L, T, R, B);
c.closePath();
c.stroke();
};
// 楕円をフィル
CRC2DP.fillEllipse = function(L, T, R, B) {
var c = this;
c.beginPath();
c.ellipse(L, T, R, B);
c.closePath();
c.fill();
};
// 楕円の塗りと線を描画
CRC2DP.drawEllipse = function(L, T, R, B) {
var c = this;
c.beginPath();
c.ellipse(L, T, R, B);
c.closePath();
c.fill();
c.stroke();
};
// 矩形の塗りと線を描画
CRC2DP.drawRect = function(sx, sy, ex, ey) {
var c = this;
c.fillRect(sx, sy, ex, ey);
c.strokeRect(sx, sy, ex, ey);
};
// arc() メソッドと同様の引数で線の描画まで行う
CRC2DP.strokeArc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
var c = this;
c.beginPath();
c.arc(x, y, radius, startAngle, endAngle, anticlockwise);
c.closePath();
c.stroke();
};
// arc() メソッドと同様の引数で塗りの描画まで行う
CRC2DP.fillArc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
var c = this;
c.beginPath();
c.arc(x, y, radius, startAngle, endAngle, anticlockwise);
c.closePath();
c.fill();
};
// arc() メソッドと同様の引数で塗りと線の描画まで行う
CRC2DP.drawArc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
var c = this;
c.beginPath();
c.arc(x, y, radius, startAngle, endAngle, anticlockwise);
c.closePath();
c.fill();
c.stroke();
};
// arc() メソッドの角度指定を度数法で行う
CRC2DP.arcByDeg = function(x, y, radius, startAngle, endAngle, anticlockwise) {
arc(x, y, radius, startAngle * PI_PER_180, endAngle * PI_PER_180, anticlockwise);
};
// arcByDeg() メソッドと同様の引数で線の描画まで行う
CRC2DP.strokeArcByDeg = function(x, y, radius, startAngle, endAngle, anticlockwise) {
var c = this;
c.beginPath();
c.arcByDeg(x, y, radius, startAngle, endAngle, anticlockwise);
c.closePath();
c.stroke();
};
// arcByDeg() メソッドと同様の引数で塗りの描画まで行う
CRC2DP.fillArcByDeg = function(x, y, radius, startAngle, endAngle, anticlockwise) {
var c = this;
c.beginPath();
c.arcByDeg(x, y, radius, startAngle, endAngle, anticlockwise);
c.closePath();
c.fill();
};
})();

##CanvasRenderingContext2D のメソッド

ctx.clearAll()

コンテキストの内容をすべて消去する。

ctx.rotateContext( 回転角度, 回転の中心軸のx座標, y座標 );

ネイティブメソッドの rotate は左上を中心座標として回転する為、扱いづらい。 この関数を使えば中心座標を指定して回転する事が出来る。

ctx.clipWithFunction( 関数 );

指定関数内で描画した図形で既存コンテキストを切り抜く。要らん気もする。

var points = ctx.getPolygonPoints( 中心のx座標, 〃y座標, 半径, 何角形?, 回転角度 );

多角形の座標配列 [x, y] を子とする二次元配列を返す。「回転角度」は省略可。省略時は 0 となり、0 の場合、必ず頂点が上になる。

ctx.polygon( x, y, radius, edgesNum, deg );

引数は getPolygonPoints と同じ。多角形パスを作成。

ctx.fillPolygon( x, y, radius, edgesNum, deg );

引数は getPolygonPoints と同じ。塗りつぶしまで行う。

ctx.strokePolygon( x, y, radius, edgesNum, deg );

同上。但し塗りつぶしではなく線を描画。

ctx.drawPolygon( x, y, radius, edgesNum, deg );

同上。コンテキストの設定に従い塗りと線を描画。

ctx.ellipse( 左上座標x、左上座標y、右下座標x、右下座標y );

座標を指定し、楕円のパスを作成。自作では無い。 (※ http://spphire9.wordpress.com/2010/03/08/ベジェ曲線で楕円を描く3/ )

ctx.strokeEllipse( 左上座標x、左上座標y、右下座標x、右下座標y );

座標を指定し、楕円の線画を描画。

ctx.fillEllipse( 左上座標x、左上座標y、右下座標x、右下座標y );

座標を指定し、楕円の塗りつぶしを描画。

ctx.drawEllipse( 左上座標x、左上座標y、右下座標x、右下座標y );

座標を指定し、楕円を描画。コンテキストの設定に従い、塗り、線ともに描画する


メモ

test: http://jsdo.it/ethertank/2w02

多角形

とりあえずどのメソッドも上からに固定。 引数に指定する座標は中心座標。開始地点か、図形を包む画面に平行な長方形の左上でもいいかも。 途中で止めて変な多角形が作れるってのもできてもいいかも。いや要らんかも。 fill と stroke を両方やるのもあるけど、中身殆ど一緒だからアレした方がいい。 引数名、変数名、関数名がちょっと。 「一角形~二角形作れ」って言われたら代わりにう○こを表示。 多角形をアニメーションで回したいとかの時は、直接描画メソッド 3 種よりも getPolygonPoint で得た座標入り配列を別関数で加工しながら使うとかの方が毎回多角形作るより多分効率がいいと思う。もしくはコンテキストを回すか。そういうメソッドまでここに足すととっ散らかるし、冗長な気がする。 getPolygonPoints() でサイズの違う多角形座標配列を二つ作ってアレすれば星型とかもできる。 じゃあ一筆書きの星も…となるが、頂点が偶数の時に二筆書きに処理を変える事も考えないといけない(大きくなってしまう)。

全般

型チェックとかまったくしてない。 来世でやる。

(function(){"use strict";if(!HTMLCanvasElement)return;var f=CanvasRenderingContext2D.prototype,PI_PER_180=Math.PI/180;f.clearAll=function(){var c=this;c.save();cc.setTransform(1,0,0,1,0,0);c.clearRect(0,0,c.canvas.width,c.canvas.height);c.restore()};f.rotateContext=function(a,b,d){var c=this;c.translate(b,d);c.rotate(a*PI_PER_180);c.translate(-b,-d)};f.clipWithFunction=function(a){var c=this;c.save();c.globalCompositeOperation="destination-out";a();c.restore()};f.getPolygonPoints=function(x,y,a,b,c){var d,points=[],i=0,Z=360/b;c=(c?c:0)-90;for(;i<b;++i){d=(Z*i+c)*PI_PER_180;points[i]=[Math.cos(d)*a+x,Math.sin(d)*a+y]}return points};f.polygon=function(x,y,a,b,d){var c=this,points=c.getPolygonPoints(x,y,a,b,d),pl=points.length,i=0,ps;c.moveTo(points[0].x,points[0].y);for(;i<pl;++i){ps=points[i];c.lineTo(ps[0],ps[1])}};f.fillPolygon=function(x,y,a,b,d){var c=this;c.beginPath();c.polygon(x,y,a,b,d);c.closePath();c.fill()};f.strokePolygon=function(x,y,a,b,d){var c=this;c.beginPath();c.polygon(x,y,a,b,d);c.closePath();c.stroke()};f.drawPolygon=function(x,y,a,b,d){var c=this;c.beginPath();c.polygon(x,y,a,b,d);c.closePath();c.fill();c.stroke()};f.ellipse=function(L,T,R,B){var c=this,halfW=(R-L)/2,halfH=(B-T)/2,C=L+halfW,M=T+halfH,cw=4*(Math.sqrt(2)-1)*halfW/3,ch=4*(Math.sqrt(2)-1)*halfH/3,p1=C+cw,p2=C-cw,p3=M-ch,p4=M+ch;c.moveTo(C,T);c.bezierCurveTo(p1,T,R,p3,R,M);c.bezierCurveTo(R,p4,p1,B,C,B);c.bezierCurveTo(p2,B,L,p4,L,M);c.bezierCurveTo(L,p3,p2,T,C,T)};f.strokeEllipse=function(L,T,R,B){var c=this;c.beginPath();c.ellipse(L,T,R,B);c.closePath();c.stroke()};f.fillEllipse=function(L,T,R,B){var c=this;c.beginPath();c.ellipse(L,T,R,B);c.closePath();c.fill()};f.drawEllipse=function(L,T,R,B){var c=this;c.beginPath();c.ellipse(L,T,R,B);c.closePath();c.fill();c.stroke()};f.drawRect=function(a,b,d,e){var c=this;c.fillRect(a,b,d,e);c.strokeRect(a,b,d,e)};f.strokeArc=function(x,y,a,b,d,e){var c=this;c.beginPath();c.arc(x,y,a,b,d,e);c.closePath();c.stroke()};f.fillArc=function(x,y,a,b,d,e){var c=this;c.beginPath();c.arc(x,y,a,b,d,e);c.closePath();c.fill()};f.drawArc=function(x,y,a,b,d,e){var c=this;c.beginPath();c.arc(x,y,a,b,d,e);c.closePath();c.fill();c.stroke()};f.arcByDeg=function(x,y,a,b,c,d){arc(x,y,a,b*PI_PER_180,c*PI_PER_180,d)};f.strokeArcByDeg=function(x,y,a,b,d,e){var c=this;c.beginPath();c.arcByDeg(x,y,a,b,d,e);c.closePath();c.stroke()};f.fillArcByDeg=function(x,y,a,b,d,e){var c=this;c.beginPath();c.arcByDeg(x,y,a,b,d,e);c.closePath();c.fill()}})();
function test() {
// テスト用変数
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cW = canvas.width;
var cH = canvas.height;
var i, dl, pl;
// CanvasRenderingContext2D.prototype.getPolygonPoints
// 色々アレしたい時には ctx.getPolygonPoints で座標入り配列を作ってアレする。
// 座標オブジェクト入り配列を返すだけのメソッドだから別の事にも使える
// あとふたつのメソッドはこれに依存してる
// 汎用性をうたうなら CanvasRenderingContext2D のメソッドにしたらダメな気がする
function setDraw(ctx, cX, cY) {
var points = ctx.getPolygonPoints(cX, cY, 100, 7);
console.log(points); //値チェック
ctx.beginPath();
ctx.moveTo(points[0][0], points[0][1]);
for (i = 1, pl = points.length; i < pl; i++) {
ctx.lineTo(points[i][0], points[i][1]);
}
ctx.closePath();
ctx.fillStyle = "yellow";
ctx.fill();
// clipWithFunction
// 線描く前に塗りだけ切り抜き
ctx.clipWithFunction(function() {
ctx.fillStyle = "#000";
ctx.fillRect(200, 200, 200, 200);
});
ctx.stroke();
}
setDraw(ctx, cW / 2, cH / 2);
// CanvasRenderingContext2D.prototype.fillPolygon
// メソッド一発で塗りつぶし多角形を描きたい時には ctx.fillPolygon
//メソッドの中で beginPath/fill までやってる
ctx.fillStyle = "red";
ctx.fillPolygon(100, 100, 100, 3);
// CanvasRenderingContext2D.prototype.storkePolygon
ctx.strokeStyle = "blue";
ctx.strokePolygon(380, 380, 100, 4);
// CanvasRenderingContext2D.prototype.drawPolygon
ctx.fillStyle = "green";
ctx.strokeStyle = "red";
ctx.lineWidth = "10";
ctx.drawPolygon(50, 300, 50, 5);
// CanvasRenderingContext2D.prototype.drawPolygon(傾き付き)
ctx.lineWidth = "10";
ctx.drawPolygon(300, 50, 80, 5, 15); // 15度傾け
// strokeEllipse
ctx.strokeEllipse(10, 360, 100, 380);
// fillEllipse
ctx.fillEllipse(10, 380, 100, 400);
// drawEllipse
ctx.drawEllipse(10, 400, 100, 420);
}
window.onload = test;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment