2色間グラデーション、3色間グラデーションは見かけるものの任意色間のグラデーションはないように思われる。
かなり前から僕自身も考えていたのですが、需要がないとか、ダルそうだったので作成しなかったのですが、取り組んでみたところ、それ程難しいことでもなかったので紹介しておきます。
さて、2色間グラデーションは簡単でプログラムは以下になります。
// 引数pを2桁の16進数文字列に変換
function toHex( p ){
p = Math.round( p );
if( p <= 0x00 ){ // 0以下
return '00' ;
}else if( p < 0x10 ){ // 16より小さい場合
return "0"+p.toString(16);
}else if( p <= 0xff ){ // 255以下
return p.toString(16);
}else{ // 255より大きい
return 'ff' ;
}
}
// 16進数6桁のカラー文字列を引数(c)にとり、
// r, g, b をプロパティに持つオブジェクトを返す。
// 0 <= r, g, b <= 255
function toRGB( c ){
// 文字列を数に変換("#"は除く)
var p = parseInt( '0x'+( c.charAt(0) == "#" ? c.substring(1) : c ) );
var r = ( p & 0xff0000 ) >>> 0x10 ; // 論理積からビットシフトし、R要素を取り出す
var g = ( p & 0x00ff00 ) >>> 0x08 ; // 論理積からビットシフトし、G要素を取り出す
var b = ( p & 0x0000ff ) >>> 0x00 ; // 論理積からビットシフトし、B要素を取り出す
return { r:r, g:g, b:b };
}
// 16進数6桁のカラー文字列を引数(p0,p1)にとり、
// 長さnの2色間グラデーション配列を生成する。
function gradation2( p0, p1, n ){
var c0 = toRGB( p0 ); // オブジェクトを生成
var c1 = toRGB( p1 ); // オブジェクトを生成
var re = new Array( n ); // 返り値
var dr = ( c1.r-c0.r )/( n-1 ); // R方向オフセット量
var dg = ( c1.g-c0.g )/( n-1 ); // G方向オフセット量
var db = ( c1.b-c0.b )/( n-1 ); // B方向オフセット量
for(var i=0;i<n;i++){
// 16進数6桁の文字列にして格納
re[i] = toHex( c0.r )+''+toHex( c0.g )+''+toHex( c0.b );
// オフセット
c0.r += dr ;
c0.g += dg ;
c0.b += db ;
}
return re ;
}
流れはプログラムを参考に。次に3色ですが、これは上記関数(gradation2
)を再利用します。
// 16進数6桁のカラー文字列を引数(p0,p1,p2)にとり、 // 長さnの3色間グラデーション配列を生成する。 function gradation3( p0, p1, p2, n ){ // 3色間の場合 [ p0-p1 ], [ p1-p2 ] のグラデーション配列を // それぞれ求め、それを連結する var m = Math.floor( n/2 ); // [ p0-p1 ], [ p1-p2 ] の配列の長さ var re0 = gradation2( p0, p1, m+1 ); // [ p0-p1 ]間グラデーション配列 var re1 = gradation2( p1, p2, m+(n&1) ); // [ p1-p2 ]間グラデーション配列 return re0.concat( re1.slice(1) ); // 重複分を除き、連結して返す }
問題の任意色ですが、先ずはプログラムを見て下さい。同様に gradation2
を使います。
// 引数の例 // gradationX( "ff0000", "00ff00", "0000ff", "ffff00", "ff00ff", "00ffff", 128 ) // この場合、赤 → 緑 → 青 → 黄 → 桃 → 水 で長さ128の配列を返す function gradationX(){ var a = arguments ; // Argumentオブジェクト var s = a.length-1 ; // 色数 var n = a[ a.length-1 ]; // 返す配列の長さ if( s == 2 ){ return gradation2( a[0], a[1], n ); }else{ var rex = new Array( s-1 ); // 各色間のグラデーション配列(二次元配列) var m = Math.floor( n/rex.length ); // 各rex要素の長さ var dn = n-m*rex.length ; // 上記 m の式は丸められているため、その誤差を求めておく if( dn-- == 0 ){ // 誤差がないとき for(var i=0;i<rex.length;i++) rex[i] = gradation2( a[i], a[i+1], m+Number(i!=rex.length-1) ); }else{ for(var i=0;i<rex.length;i++) rex[i] = gradation2( a[i], a[i+1], m+1+Number(i<dn) ); } var re = rex[0]; for(var i=0;i<rex.length-1;i++) re = re.concat( rex[i+1].slice(1) ); return re ; } }
この関数の使用例はこちら。
難しいのは指定した長さ(n)の配列を返すことであって、基本的なアルゴリズムは3色の場合と変わらないです。
さて、これらの関数をいちいち定義しておくのは面倒なので、一つの関数にまとめました。
これ一つで何色でもOKです。
// string[] gradation( c0, c1, [ c2, [ c3, ... ], n )
function gradation(){
var a = arguments ;
var s = a.length-1 ;
var n = a[ a.length-1 ];
switch( s ){
case 2 : {
var c0 = toRGB( a[0] );
var c1 = toRGB( a[1] );
var re = new Array( n );
var dc = new Array( 3 );
for(var i=0;i<3;i++) dc[i] = ( c1[i]-c0[i] )/( n-1 );
for(var i=0;i<n;i++){
re[i] = toHex( c0[0] )+''+toHex( c0[1] )+''+toHex( c0[2] );
for(var j=0;j<3;j++) c0[j] += dc[j];
}
return re ;
}
case 3 : {
var m = Math.floor( n/2 );
var re0 = a.callee( a[0], a[1], m+1 );
var re1 = a.callee( a[1], a[2], m+(n&1) );
return re0.concat( re1.slice(1) );
}
default : {
var t = s-1 ;
var rex = new Array( t );
var m = Math.floor( n/t );
var dn = n-m*t ;
if( dn-- == 0 ){
for(var i=0;i<t;i++) rex[i] = a.callee( a[i], a[i+1], m+Number(i!=t-1) );
}else{
for(var i=0;i<t;i++) rex[i] = a.callee( a[i], a[i+1], m+1+Number(i<dn) );
}
var re = rex[0];
for(var i=0;i<t-1;i++) re = re.concat( rex[i+1].slice(1) );
return re ;
}
}
function toHex( p ){
p = Math.round( p );
return p <= 0x00 ? '00' : ( p < 0x10 ? "0"+p.toString(16) : ( p <= 0xff ? p.toString(16) : 'ff' ) );
}
function toRGB( c ){
var p = parseInt( '0x'+( c.charAt(0) == "#" ? c.substring(1) : c ) );
var r = ( p & 0xff0000 ) >>> 0x10 ;
var g = ( p & 0x00ff00 ) >>> 0x08 ;
var b = ( p & 0x0000ff ) >>> 0x00 ;
return new Array( r, g, b );
}
}
これを使ったカラーテーブル。
もう随分昔になりますが、これと関係して Mathematics > Gradation のページを作成していたんですが、時間がなくてほにゃらら。