ステンドグラス

ステンドグラスは、ドラッグできます。
右端の画像は、ステンドグラスの元になった画像です。
[no canvas]
[no canvas]
ソースコード:
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<title>Javascript CANVAS</title>
<link href="file:///D:/common_css/common2.css" rel="stylesheet" type="text/css">
<link href="/common_css/common2.css" rel="stylesheet" type="text/css">

<style>
</style>
<script>
function $( id ) {
	
return document.getElementById( id );
	
}

var canvasEL, canvas;
var tens = new Array();
var masuW;
var masuH;



//--- ユーザーによる調整 ここから
/*
変更して直せなくなったときのため、このファイルのバックアップを取ってください。
各値を、行の末尾等に記載した説明をもとに変更して、ブラウザの更新ボタンを押してください。

たとえば、下記一番最初の設定の stendType の値を1から2に変更すると画像ファイルを読み込み、ステンドグラス化します。

注意: すべて半角文字で書くこと
*/

//---■■■ ↓ 共通設定 ■■■

var stendType = 2;
			
//「ステンドグラスの種類」
			
1:ランダム
	
2:画像描写、初期値: 1


//外観
var borderWidth = 4;
		
//「ステンドグラスの外枠の太さ」
		
0~16程度、初期値: 4

var borderRadius = "0px";
	
//「ステンドグラスの外枠の丸み」
		
"0px"~"16px" 程度、初期値: "0px"

var initX = 500;
			
//「ステンドグラスの位置(横方向)」
	
0~画面の横幅程度、初期値: 500

var initY = 0;
				
//「ステンドグラスの位置(縦方向)」
	
0~画面の縦幅程度、初期値: 0

var hanare = 32;
			
//「本体と影の離れ」
					
0~画面の縦幅の半分程度まで。初期値: 32


//(備考: もしプログラムがわかる場合は、initX = innerWidth / 2; としてセンタリングになる)


//---■■■ ↓ stendType を 1(ランダム) にしたときに有効な設定 ■■■

var stendWidth = 240;
	
//「ステンドグラスの横幅」
	
32程度~画面横幅程度、初期値: 240

var stendHeight = 300;
	
//「ステンドグラスの縦幅」
	
32程度~画面縦幅の半分程度、初期値: 300

var xmasu = 16;
			
//「横方向分割数」
			
1~上記横幅の1/10程度、初期値: 16

var ymasu = 16;
			
//「縦方向分割数」
			
1~下記縦幅の1/10程度、初期値: 16

var yure = 16;
			
//「ゆれぐあい」
			
0~適度。初期値: 16

var lineWidth = 2;
		
//「黒線の太さ」
			
0~。初期値: 2


//「ステンドグラス本体に使用する色の設定」
//三原色と不透明度を数値で記入する
//1行の意味↓
//[赤成分0~255, 緑成分0~255, 青成分0~255, 不透明度0~1],
	
//コメント

//行は増やしたり減らしたりしてよいですが、必ず1行以上必要です。
var colors = [
[0,
	
0,
		
255,
	
0.5
	
],
	
//1:blue
[255,
	
0,
		
0,
		
0.5
	
],
	
//2:red
[255,
	
0,
		
255,
	
0.5
	
],
	
//3:magenta
[0,
		
255,
	
0,
		
0.5
	
],
	
//4:green
[0,
		
255,
	
255,
	
0.5
	
],
	
//5:cyan
[255,
	
255,
	
0,
		
0.5
	
],
	
//6:yellow
];


//---■■■ ↓ stendType を 2(画像描写) にしたときに有効な設定 ■■■

imageSRC
		
= "Sheet1.png";
		
//「画像ファイル名」
	
(htmlファイルと同じフォルダに置く)

xmasu_tp2
		
= 48;
				
//「横方向分割数」
		
1~画像のサイズ程度、初期値: 48

ymasu_tp2
		
= 48;
				
//「縦方向分割数」
		
1~画像のサイズ程度、初期値: 48

yure_tp2
		
= 4;
				
//「ゆれぐあい」
		
0~適度。初期値: 4

lineWidth_tp2
	
= 2;
				
//「黒線の太さ」
		
0~。初期値: 2

huToumeiDoAtGazo= 0.5;
				
//「グラスの不透明度」
	
0~1。初期値: 0.5


//※ステンドグラスのサイズは、画像のサイズです


//---■■■ 通常使う分にはいじらなくても良い設定。(影の方向など、こだわり設定) ■■■

var kougenVisible
	
=
	
false;
		
//光源の参考表示 trueまたはfalse、初期値false
var kougenV
		
=
	
0;
			
//光源の水平位置 -1.5~1.5程度、初期値0(画面の縦幅を-1~1としたときの値で、0が画面中央)
//(※kougenVにはバグがあります。0以外に設定しないでください m(_ _)m)
var kougenH
		
=
	
0;
			
//光源の垂直位置 -1.5~1.5程度、初期値0(画面の横幅を-1~1としたときの値で、0が画面中央)
//(上の設定kougenVisible=trueにすると値を決めやすいです)
var shadowCanvasSizeRate =
	
4;
		
//「影CANVASの横サイズ(倍率)」 1~5程度、初期値4
//(※試しに1にした後、2にしてみれば意味が分かると思います)
//画像描写について
var sankouHyouji
	
=
	
true;
		
//画像描写のときに、元画像の参考表示 trueまたはfalse、初期値true
var isouX
			
=
	
0;
			
//つぶれ具合が気に入らない場合に元画像を読むピクセルを指定ピクセルずらす 0~16程度、初期値0
var isouY
			
=
	
0;
			
//つぶれ具合が気に入らない場合に元画像を読むピクセルを指定ピクセルずらす 0~16程度、初期値0

//備考: 光源は疑似光源です。(正しい計算をした光源ではありません:-) )


//--- ユーザーによる調整 ここまで




function onresizex( e ) {
with( kougenEL.style ) {
display
		
=
	
kougenVisible ? "block" : "none";

position
	
=
	
"absolute";

width
		
=
	
"16px";

height
		
=
	
"16px";

borderRadius =
	
"8px";

backgroundColor
	
=
	
"yellow";

border
		
=
	
"solid 1px orange";

left
		
=
	
window.innerWidth / 2 + kougenH * window.innerWidth / 2;

top
			
=
	
window.innerHeight / 2 + kougenV * window.innerHeight / 2;

}

draw( canvas2, true );
}


function onloadx() {

canvasEL = $( "canvasELID" );
canvasEL2 = $( "canvasELID2" );
canvas = canvasEL.getContext( '2d' );
canvas2 = canvasEL2.getContext( '2d' );
canvasEL.style.borderWidth = borderWidth;
canvasEL.style.borderRadius = borderRadius;

kougenEL = document.createElement( "div" );
document.body.appendChild( kougenEL );


switch( stendType ) {
case 1:
	
type1();
	
break;

case 2:
	
type2();
	
break;

}
}

function type1() {

canvasEL.width = stendWidth;
canvasEL.height = stendHeight;
masuW = canvasEL.width / xmasu;
masuH = canvasEL.height / ymasu;

//make tens
	

for( var y = 0; y < ymasu + 1; y++ ) {
var yureY = ( y == 0 || y == ymasu ) ? 0 : yure;
for( var x = 0; x < xmasu + 1; x++ ) {
var yureX = ( x == 0 || x == xmasu ) ? 0 : yure;
var ten = new Array();
ten[ 0 ] = canvasEL.width / xmasu * x + Math.floor( Math.random() * yureX ) - yureX / 2;
ten[ 1 ] = canvasEL.height / ymasu * y + Math.floor( Math.random() * yureY ) - yureY / 2;
ten[ 2 ] = colors[ Math.floor( Math.random() * colors.length ) ];
tens[ tens.length ] = ten;
}
}

afterTypes();
}

function type2() {
image = new Image();
image.src = imageSRC;
image.onload = type2_2;
}
function type2_2() {

xmasu = xmasu_tp2;
ymasu = ymasu_tp2;
yure = yure_tp2;
lineWidth = lineWidth_tp2;

if( sankouHyouji ) {
document.body.appendChild( image );
with( image.style ) {
position
	
=
	
"absolute";

right
		
=
	
0;

top
			
=
	
0;

}
}

canvasEL.width = image.width;
canvasEL.height = image.height;
canvasEL2.width = image.width * shadowCanvasSizeRate;
canvasEL2.height = image.height;

masuW = canvasEL.width / xmasu;
masuH = canvasEL.height / ymasu;
canvas.drawImage( image, 0, 0 );
srcORG = canvas.getImageData( 0, 0, image.width, image.height );

//make tens
	

for( var y = 0; y < ymasu + 1; y++ ) {
var yureY = ( y == 0 || y == ymasu ) ? 0 : yure;
for( var x = 0; x < xmasu + 1; x++ ) {
var yureX = ( x == 0 || x == xmasu ) ? 0 : yure;
var ten = new Array();
ten[ 0 ] = canvasEL.width / xmasu * x + Math.floor( Math.random() * yureX ) - yureX / 2;
ten[ 1 ] = canvasEL.height / ymasu * y + Math.floor( Math.random() * yureY ) - yureY / 2;

var gx = Math.floor( x * masuW + masuW / 2 ) + isouX;
var gy = Math.floor( y * masuH + masuH / 2 ) + isouY;
var idx = ( gx + gy * image.width ) * 4;
var r, g, b, a;
//check.
if( idx >= srcORG.data.length ) {
r = 255;
g = 255;
b = 255;
a = 0;
} else {
r = srcORG.data[ idx ]; //赤
g = srcORG.data[ idx + 1 ]; //緑
b = srcORG.data[ idx + 2 ]; //青
a = srcORG.data[ idx + 3 ]; //透明度
}
ten[ 2 ] = [ r, g, b, a == 0 ? 0 : huToumeiDoAtGazo ];

tens[ tens.length ] = ten;
}
}

afterTypes();
}

function afterTypes() {

stendEL = $( "stend" );

shadowShift = ( canvasEL2.width - canvasEL.width ) / 2;
canvasEL2.style.left = - shadowShift + borderWidth;
canvasEL2.style.top = canvasEL.height + borderWidth * 2 + hanare;

//initX, initYをプログラムを駆使して決めるならここに書く↓


stendEL.style.left = initX;
stendEL.style.top = initY;
stendEL.style.display = "block";

draw( canvas, false );
onresizex();


dragging = false;
onmousedown = function( e ) {
if( e.target.id != "canvasELID" ) return true;
dragging = true;
dnX = e.clientX - canvasEL.getBoundingClientRect().left;
dnY = e.clientY - canvasEL.getBoundingClientRect().top;
e.preventDefault();
e.stopPropagation();
return false;
};
onmousemove = function( e ) {
if( ! dragging ) return true;
		

stendEL.style.left = document.body.scrollLeft + e.clientX - dnX;
stendEL.style.top = document.body.scrollTop + e.clientY - dnY;
draw( canvas2, true );
};
onmouseup = function( e ) {
if( ! dragging ) return true;
dragging = false;
};
onresize = onresizex;
document.body.onscroll = onresizex;

}

function draw( canvas, isShadow ) {

if( isShadow ) {
canvas.clearRect( 0, 0, canvasEL2.width, canvasEL2.height );
//影は上下反転する
canvas.save();
canvas.translate( shadowShift, canvasEL2.height );
canvas.scale( 1, -1 );
} else {
canvas.clearRect( 0, 0, canvasEL.width, canvasEL.height );
}

//画面に対するステンドグラスの位置
var horizonRate = ( stend.getBoundingClientRect().left - window.innerWidth / 2 + canvasEL.width / 2 ) / (window.innerWidth / 2);
var verticalRate = 1- ( stend.getBoundingClientRect().top + document.body.scrollTop - document.body.scrollTop ) / ( window.innerHeight - canvasEL.height );

var endKageY;
x = 0;
y = ymasu - 1;
bottomPos = tens[ x + ( y + 1 ) * ( xmasu + 1 ) ][ 1 ];
d = tens[ x + y * ( xmasu + 1 ) ][ 1 ];
endKageY = d * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;
endKageY = endKageY + ( canvasEL2.height - bottomPos );

var startKageY;
x = 0;
y = 0;
e = tens[ x + y * ( xmasu + 1 ) ][ 1 ];
startKageY = e * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;
startKageY = startKageY + ( canvasEL2.height - bottomPos );

var kageHeight = endKageY - startKageY;

//fill areas
for( var y = 0; y < ymasu; y++ ) {
for( var x = 0; x < xmasu; x++ ) {
//1枚の面を構成する4頂点を得る
var p = new Array();
p[ 0 ] = new Array();
p[ 0 ][ 0 ] = tens[ x + y * ( xmasu + 1 ) ][ 0 ];
p[ 0 ][ 1 ] = tens[ x + y * ( xmasu + 1 ) ][ 1 ];
p[ 0 ][ 2 ] = tens[ x + y * ( xmasu + 1 ) ][ 2 ];
p[ 1 ] = new Array();
p[ 1 ][ 0 ] = tens[ ( x + 1 ) + y * ( xmasu + 1 ) ][ 0 ];
p[ 1 ][ 1 ] = tens[ ( x + 1 ) + y * ( xmasu + 1 ) ][ 1 ];
p[ 1 ][ 2 ] = tens[ ( x + 1 ) + y * ( xmasu + 1 ) ][ 2 ];
p[ 2 ] = new Array();
p[ 2 ][ 0 ] = tens[ ( x + 1 ) + ( y + 1 ) * ( xmasu + 1 ) ][ 0 ];
p[ 2 ][ 1 ] = tens[ ( x + 1 ) + ( y + 1 ) * ( xmasu + 1 ) ][ 1 ];
p[ 2 ][ 2 ] = tens[ ( x + 1 ) + ( y + 1 ) * ( xmasu + 1 ) ][ 2 ];
p[ 3 ] = new Array();
p[ 3 ][ 0 ] = tens[ x + ( y + 1 ) * ( xmasu + 1 ) ][ 0 ];
p[ 3 ][ 1 ] = tens[ x + ( y + 1 ) * ( xmasu + 1 ) ][ 1 ];
p[ 3 ][ 2 ] = tens[ x + ( y + 1 ) * ( xmasu + 1 ) ][ 2 ];

if( isShadow ) {
//エッジ分つめる
p[ 0 ][ 0 ] += lineWidth / 2;
p[ 0 ][ 1 ] += lineWidth / 2;
p[ 1 ][ 0 ] -= lineWidth / 2;
p[ 1 ][ 1 ] += lineWidth / 2;
p[ 2 ][ 0 ] -= lineWidth / 2;
p[ 2 ][ 1 ] -= lineWidth / 2;
p[ 3 ][ 0 ] += lineWidth / 2;
p[ 3 ][ 1 ] -= lineWidth / 2;

//影は遠いほど広がる
p[ 0 ][ 0 ] = ( p[ 0 ][ 0 ] - ( canvasEL.width / 2 ) ) * ( 1.4 - 0.4 * ( p[ 0 ][ 1 ] - hanare ) / canvasEL.height );
p[ 1 ][ 0 ] = ( p[ 1 ][ 0 ] - ( canvasEL.width / 2 ) ) * ( 1.4 - 0.4 * ( p[ 1 ][ 1 ] - hanare ) / canvasEL.height );
p[ 2 ][ 0 ] = ( p[ 2 ][ 0 ] - ( canvasEL.width / 2 ) ) * ( 1.4 - 0.4 * ( p[ 2 ][ 1 ] - hanare ) / canvasEL.height );
p[ 3 ][ 0 ] = ( p[ 3 ][ 0 ] - ( canvasEL.width / 2 ) ) * ( 1.4 - 0.4 * ( p[ 3 ][ 1 ] - hanare ) / canvasEL.height );

//影はステンドグラスの左右位置で左右に傾く
p[ 0 ][ 0 ] += canvasEL.width / 2 + 200 * ( horizonRate - kougenH ) * ( 1 - ( p[ 0 ][ 1 ] - hanare ) / canvasEL.height );
p[ 1 ][ 0 ] += canvasEL.width / 2 + 200 * ( horizonRate - kougenH ) * ( 1 - ( p[ 1 ][ 1 ] - hanare ) / canvasEL.height );
p[ 2 ][ 0 ] += canvasEL.width / 2 + 200 * ( horizonRate - kougenH ) * ( 1 - ( p[ 2 ][ 1 ] - hanare ) / canvasEL.height );
p[ 3 ][ 0 ] += canvasEL.width / 2 + 200 * ( horizonRate - kougenH ) * ( 1 - ( p[ 3 ][ 1 ] - hanare ) / canvasEL.height );

//影はステンドグラスの上下位置で伸縮する
p[ 0 ][ 1 ] = p[ 0 ][ 1 ] * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;
p[ 1 ][ 1 ] = p[ 1 ][ 1 ] * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;
p[ 2 ][ 1 ] = p[ 2 ][ 1 ] * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;
p[ 3 ][ 1 ] = p[ 3 ][ 1 ] * verticalRate + ( 1 - ( verticalRate + kougenV ) ) * canvasEL.height;

//影位置調整
p[ 0 ][ 1 ] += canvasEL2.height - bottomPos;
p[ 1 ][ 1 ] += canvasEL2.height - bottomPos;
p[ 2 ][ 1 ] += canvasEL2.height - bottomPos;
p[ 3 ][ 1 ] += canvasEL2.height - bottomPos;


}

//面を描く
canvas.beginPath();
for( var i = 0; i < p.length; i++ ) {
if( i == 0 )
canvas.moveTo( p[ i ][ 0 ], p[ i ][ 1 ] );
else
canvas.lineTo( p[ i ][ 0 ], p[ i ][ 1 ] );
}
canvas.closePath();

//面を塗る
var rgb = p[ 0 ][ 2 ][ 0 ] + "," + p[ 0 ][ 2 ][ 1 ] + "," + p[ 0 ][ 2 ][ 2 ];
var a = p[ 0 ][ 2 ][ 3 ];
if( isShadow ) {
var y2 = p[ 0 ][ 1 ] - startKageY;
var a2 = ( a - 0.2 ) * ( y2 * y2 / ( kageHeight * kageHeight ) ) + 0;
canvas.fillStyle = "rgba(" + rgb + "," + a2 + ")";
} else
canvas.fillStyle = "rgba(" + rgb + "," + a + ")";
canvas.fill();
}
}

//draw edges
if( isShadow == false && lineWidth != 0 ) {
canvas.lineWidth = lineWidth;
canvas.strokeStyle = "black";
for( var y = 0; y < ymasu + 1; y++ ) {
for( var x = 0; x < xmasu + 1; x++ ) {
var pc = tens[ x + y * ( xmasu + 1 ) ];

if( y != ymasu ) {
var pb = tens[ x + ( y + 1 ) * ( xmasu + 1 ) ];
canvas.beginPath();
canvas.moveTo( pc[ 0 ], pc[ 1 ] );
canvas.lineTo( pb[ 0 ], pb[ 1 ] );
canvas.stroke();
}

if( x != xmasu ) {
var pr = tens[ ( x + 1 ) + y * ( xmasu + 1 ) ];
canvas.beginPath();
canvas.moveTo( pc[ 0 ], pc[ 1 ] );
canvas.lineTo( pr[ 0 ], pr[ 1 ] );
canvas.stroke();
}
}
}
}

if( isShadow ) {
canvas.restore();
}
}


</script>
</head>
<body onload="onloadx();" style="
background-color
	
:
	
lightgray;

">
<h1>ステンドグラス</h1>
ステンドグラスは、ドラッグできます。<BR>
右端の画像は、ステンドグラスの元になった画像です。
<div id="stend" style="
position
			
:
	
absolute;

z-index
				
:
	
3;

display
				
:
	
none;

">
<canvas id="canvasELID" width="280" height="320" style="
background-color
	
:
	
transparent;

cursor
				
:
	
pointer;

border
				
:
	
solid 4px black;

border-radius
		
:
	
.5em;

display
				
:
	
block;

">[no canvas]</canvas><BR>
<canvas id="canvasELID2" width="640" height="320" style="
background-color
	
:
	
transparent;

position
			
:
	
absolute;

display
				
:
	
block;


">[no canvas]</canvas>
</div>
<div style="height:80%;">
</div>
ソースコード:
<code>
%%com.include:default.html%%
</code>
</body>
</html>

ページの上端へ (もくじ開く)