読者です 読者をやめる 読者になる 読者になる

jQuery カラーピッカー plugin で補色計算してみた。

jQuery

jQuery カラーピッカー plugin を使う機会があったので、サンプルを作って「補色」を求めたら興味深いことが出てきました。
ColorPicker - jQuery plugin
を使います。昔からあるピッカーで有名なもの。
f:id:posturan:20160924163005j:plain
HTML をサンプルとして以下のように準備しました。CSSなどは割愛します。プラグインで配布された colorpicker.css
colorpicker.js を読み込むようにしておきます。

<div class="box">
   <div>Target</div>
   <div id="target"><div>#00ff00</div></div>
</div>
<div class="box">
   <div>反転色</div>
   <div id="reverse"><div>#ff00ff</div></div>
</div>
<div class="box">
   <div>補色</div>
   <div id="comple"><div>#ff00ff</div></div>
</div>
<div class="box">
   <div>背景:Target  文字:補色</div>
   <div id="stringcomple"><div>#ff00ff</div></div>
</div>
<div class="clear">
   <div id="customWidget">
      <div id="colorSelector"><div style="background-color: #00ff00"></div></div>
      <div id="colorpickerHolder"></div>
   </div>
</div>

反転色、補色の区別があるのかどうかよくわかってないのですが、今回は、、

反転色:RGB 値を各々、255 から差し引いたコードを指定
補色: Adobe Illustrator CS4方式、RGB コードの最小値+最大値を各RGB コードで差し引いた結果で RGB コードに指定

で計算してみました。

// picker の初期色表示
$('#colorSelector div').css("background-color", $('#target div').html());
// picjer 設定
$('#colorpickerHolder').ColorPicker({
   flat: true,
   color: $('#target div').html(),
   onSubmit: function(hsb, hex, rgb, el){
      $('#colorSelector div').css('backgroundColor', '#' + hex);
      // 反転色
      var r = ((255-rgb['r']) < 16 ? "0" : "") + (255-rgb['r']).toString(16);
      var g = ((255-rgb['g']) < 16 ? "0" : "") + (255-rgb['g']).toString(16);
      var b = ((255-rgb['b']) < 16 ? "0" : "") + (255-rgb['b']).toString(16);
      var reverse = "#" + r + g + b;
      // 補色計算
      var sum = Math.max(rgb['r'], Math.max(rgb['g'], rgb['b'])) +  Math.min(rgb['r'], Math.min(rgb['g'], rgb['b']));
      console.log(sum);
      var cr = sum - rgb['r'];
      var cg = sum - rgb['g'];
      var cb = sum - rgb['b'];
      var complementary = "#" + (cr < 16 ? "0" : "") + cr.toString(16)
                        + (cg < 16 ? "0" : "") + cg.toString(16)
                        + (cb < 16 ? "0" : "") + cb.toString(16)
      console.log(complementary);

      // 結果を別のエリアにセット
      /*---------- target -------------------------------------*/
      $('#target div').html("#" + hex);
      $('#target').css("background-color", "#" + hex);
      // 文字は反転色で。
      $('#target').css("color", reverse);
      /*---------- reverse -------------------------------------*/
      $('#reverse div').html(reverse);
      $('#reverse').css("background-color", reverse);
      // 文字は反転色で。
      $('#reverse').css("color", "#" + hex);
      /*---------- complementary -------------------------------*/
      $('#comple div').html(complementary);
      $('#comple').css("background-color", complementary);
      // 文字は反転色で。
      var complevarse = "#" + ((255-cr) < 16 ? "0" : "") + (255-cr).toString(16)
                       + ((255-cg) < 16 ? "0" : "") + (255-cg).toString(16)
                       + ((255-cb) < 16 ? "0" : "") + (255-cb).toString(16);
      $('#comple').css("color", complevarse);
      /*---------- stringcomple 背景:Target  文字:補色 ------*/
      $('#stringcomple div').html(complementary);
      $('#stringcomple').css("background-color", "#" + hex);
      $('#stringcomple').css("color", complementary);

      // 閉じる
      $('#colorpickerHolder').stop().animate({ height: 0 }, 500 );
   }
});
$('#colorpickerHolder>div').css('position', 'absolute');
var widt = false;
$('#colorSelector').bind('click', function(){
   $('#colorpickerHolder').stop().animate({ height: widt ? 0 : 173 }, 500 );
   widt = !widt;
});

色が濃い場合は、反転色も補色も見た目の差があまりないのですが、薄い色で実行すると、、

f:id:posturan:20160924163858j:plain

この補色を文字色にするとなんとも醜い。

補色=色相環図の反対側にある色と言われているけど、色が薄い場合、Adobe Illustrator CS4方式の計算は正しく見えるけど
2つの色を並べた時に鮮明な違いを起こすのが、補色というなら、反転色の計算の方が仕様にあってる思えます。
では、色相環のサークル(円)は、いったい何なの?意味あるの?

補色の目的によると思うが、自動計算で求めるのにもっと良い計算式があるのだろうか?
それとも、配色の感じ方が人それぞれ違うはずだから、計算で普遍的に色コードを求めた結果を押し付けるのはナンセンス?

ListView に配置した Formフィールド 受信

Wicket

WicketListView でリストの中に TextField やフォーム送信するものを配置することが時々ある。

送信後、どうやって受け取るか以前はよく悩んでたもので、getRequestCycle().getRequest().getPostParameters() から取得する力技をよく
書いてたものだ。
そんなことすると、パラメータ名を辿りながらの取得でコーディングしていて悲しくなる。

Java8 から以下の方法で取得すればリストに書いた流れで取得できるではないですか。

例えば、ListView があって

final ListView<Foo> listview = new ListView<Foo>("listview", new PropertyModel<List<Foo>>(this, "list")){
   @Override
   protected void populateItem(ListItem<Foo> item){
       // フォームフィールドの配置
       item.add(new TextField<String>("name", new Model<>()));
       item.add(new HiddenField<String>("id", new Model<>()));
       // ・・・等々。。。
   }
};
queue(listview);

これを、AjaxButton の submit や、Form の onSubmit() で受信する時に、
ListView から取得するコンポーネントイテレータ処理をラムダで書けば、、
つまり、、ListView → iterator()java.util.IteratorforEachRemaining(Consumer)

順序どおりの受信を簡単に取得できるではないですか。

listview.iterator().forEachRemaining(c->{
    String name = ((TextField<String>)c.get("name")).getModelObject();
    String id = ((HiddenField<String>)c.get("id")).getModelObject();
    // 後は好きなように処理をする。。。
});

table で複数行の sortable

jQuery

テーブルの Sortable 去年書いたけど、書き直す。ドラッグした後に、きちんとハンドルを外す。

sortable の stop オプションで、ui.item.data('multidrag') で取得した移動対象を移動後に
ui-selected を外してやります。

HTML

<table id="sortable">
  <thead>
     <tr><th>header-1</th><th>header-2</th></tr>
  </thead>
  <tbody>
     <tr><td>Item-1</td><td>aaa</td></tr>
     <tr><td>Item-2</td><td>bbb</td></tr>
     <tr><td>Item-3</td><td>ccc</td></tr>
     <tr><td>Item-4</td><td></td></tr>
     <tr><td>Item-5</td><td>eee</td></tr>
     <tr><td>Item-6</td><td></td></tr>
     <tr><td>Item-7</td><td>fff</td></tr>
     <tr><td>Item-8</td><td></td></tr>
     <tr><td>Item-9</td><td></td></tr>
     <tr><td>Item-10</td><td></td></tr>
    </tbody>
</table>

CSS

@CHARSET "UTF-8";

table{
   margin: 40px 50px;
   box-sizing: border-box;
   border-collapse; collapse;
   border-spacing: 0;
   white-space: nowrap;
}
th{ background-color: #c0c0c0; }
th,td{ border: 1px solid #444444; height: 40px; width: 100px; padding: 0 8px; }
.ui-selecting{ background-color: #feca40; }
.ui-selected{ background-color: #f39814; color: #ffffff; }
.ui-state-highlight{ background-color: #ffe45c; }

f:id:posturan:20160914003320j:plain


jQueury

$('#sortable tbody').selectable({
   cancel: '.sort-handle, .ui-selected',
}).sortable({
   placeholder: "ui-state-highlight",
   axis: 'y',
   opacity: 0.9,
   items: "> tr",
   handle: 'td, .sort-handle, .ui-selected',
   helper: function(e, item){
      if (!item.hasClass('ui-selected')){
         item.parent().children('.ui-selected').removeClass('ui-selected');
         item.addClass('ui-selected');
      }
      var selected = item.parent().children('.ui-selected').clone();
      ph = item.outerHeight() * selected.length;
      item.data('multidrag', selected).siblings('.ui-selected').remove();
      return $('<tr/>').append(selected);
   },
   cursor: "move",
   start: function(e, ui){
      ui.placeholder.css({'height':ph});
   },
   stop: function(e, ui){
      var selected = ui.item.data('multidrag');
      ui.item.after(selected);
      ui.item.remove();
      selected.removeClass('ui-selected');
      $(selected).children("td").removeClass('ui-selected');
   },
   update: function(e, ui){
      console.log("updated !!");
   }
});

Wicket scheduleRequestHandlerAfterCurrent 使用の書き方を見直す

Wicket

Wicket の WebPage でファイルダウンロードさせる1つの方法である RequestCycle scheduleRequestHandlerAfterCurrent を使用する時、

そのまま以下のようなことを書いていた。。

getRequestCycle().scheduleRequestHandlerAfterCurrent(new ResourceStreamRequestHandler(new AbstractResourceStreamWriter(){
   @Override
   public void write(OutputStream out){

      // OutputStream に出力

      out.flush();
      out.close();
   }
   @Override
   public String getContentType(){
      // 返す ContentType を指定
      return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
}, "filename"));

でも、これは AbstractResourceStreamWriter の close() が約束されてないことになり Eclipse では
潜在的なリソース・リーク: '' may not be closed
になる

AbstractResourceStreamWriter のインターフェースである IResourceStreamWriter は、IResourceStream の継承で
Closeable であるので、try(){ ~ } 構文にすべきだ。

try(IResourceStreamWriter writer = new AbstractResourceStreamWriter(){
   @Override
   public void write(OutputStream out) throws IOException{

      // OutputStream に出力する。

      out.flush();
      out.close();
   }
   @Override
   public String getContentType() {
      // 返す ContentType を指定
      return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
}){
   getRequestCycle().scheduleRequestHandlerAfterCurrent(
      new ResourceStreamRequestHandler(writer, URLEncoder.encode("サンプル.xlsx", "UTF-8")
   ));
}catch(Exception e){
   // エラー捕捉
}}

Java で横長カレンダーを書くための処理

Java

先日、jQuery で横長の帯状のカレンダーを書くための処理を書いて
横長カレンダーを書く - Oboe吹きプログラマの黙示録
、では Java だったらどう書く?
と思ったので書いてみました。

例えば、

LocalDate start = LocalDate.parse("2016-12-22");
LocalDate end   = LocalDate.parse("2017-01-05");

という開始日付と終了日付が与えられてる場合です。

まず、日(day) を並べてみます。無限 Stream 生成して開始と終了の差で制限を書けて Stream を完結させます。

Stream.iterate(start, d->d.plusDays(1)).limit(Period.between(start, end).getDays() + 1).forEach(e->{
  System.out.print(e.getDayOfMonth() + " ");
});

以下のようになります。

22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 

問題は月で、この場合の月として割り当てた時の日数の長さを求めたいのです。
無理やり、Stream の 実行を繋げて処理を書くと。。。

Stream.iterate(start, d->d.plusDays(1)).limit(Period.between(start, end).getDays() + 1)
.map(e->LocalDate.of(e.getYear(), e.getMonthValue(), 1)).collect(Collectors.groupingBy(e->e, Collectors.counting()))
.entrySet().stream().sorted((e1, e2)->e1.getKey().compareTo(e2.getKey())).forEach(e->{
   System.out.println( e.getKey().getMonthValue() + "月 : " + e.getValue() + " days");
});

長いけど、Cool に見えてきます。

12月 : 10 days
1月 : 5 days

となり、12月→1月の順番を保てます。

横長カレンダーを書く

CSS jQuery

横長カレンダーを書く jQuery ソース・・・メモ。
moment.js http://momentjs.com/ を使うことにした。jQuery は、2.x系で書いた。

moment-with-locales.js を使う。

/* datetext  = yyyy/MM/dd 日付
 * predays   = 前方日数(datetextの日付を含まないカウント)
 * afterdays = 後方日数(datetextの日付を含まないカウント)
 */
var writeCalandarHead = function(datetext, predays, afterdays){
   var basedate = moment(datetext, "YYYY/MM/DD");
   var dt = moment(datetext, "YYYY/MM/DD");
   dt.add(predays * -1 - 1, "days");
   var mary = {};
   $('thead tr:nth-child(1) th:nth-child(n+2)').remove();
   $('thead tr:nth-child(2) th').remove();
   var len = predays + afterdays + 1;
   for(var i=0; i < len; i++){
      dt = dt.add(1, "days");
      var key = (dt.month()+1) + "月";
      mary[key] = mary[key]==undefined ? 1 : mary[key] = mary[key] + 1;
      var plusclass = "";
      if (basedate.diff(dt, "days")==0){
         plusclass = ' class="today"';
      }else{
         var week = dt.format("dddd");
         if (week=="Saturday") plusclass = ' class="Saturday"';
         if(week=="Sunday") plusclass = ' class="Sunday"';
      }
      $('thead tr:nth-child(2)').append("<th" + plusclass + ">" + dt.date() + "</th>" );
   }
   Object.keys(mary).forEach(function(key){
      $('thead tr:nth-child(1)').append('<th colspan="' + mary[key] + '">' + key + '</th>');
   });
};
$(function(){
   $("input[class='datepicker default']").datepicker({
      prevText:"前月", nextText:"翌月",
      changeMonth: true,
      changeYear: true, yearRange: '-3:+4',
      onSelect: function(dateText){
         writeCalandarHead(dateText, 5, 20);
      },
   }).datepicker("setDate", moment().format('YYYY/MM/DD'));
   writeCalandarHead(moment().format('YYYY/MM/DD'), 5, 20);
});

HTMLは、、、jquery-2.x と jquery-ui を指定して、、

<input type="text" class="datepicker default" style="width: 70px">
<div>
   <table>
      <thead>
         <tr><th rowspan="2" style="width: 100px">header</th></tr>
         <tr></tr>
      </thead>
   </table>
</div>

CSS は、、

.ui-datepicker{ font-size: 80%; }
.ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year{ width: auto; }
table{
   box-sizing: border-box;
   border-spacing: 0;
   border-collapse: collapse;
   white-space: nowrap;
   font-size: 14px;
}
th{ border: 1px solid #000000; }
tr:nth-child(2) th{ width: 30px; }

th.today    { background-color: #ffff60; }
th.Saturday { background-color: #b3f2ff; }
th.Sunday   { background-color: #ffb9b3; }

表示すると。

f:id:posturan:20160906110837j:plain

カスタムデータ属性で CSSを効かせる

CSS

HTML5 のカスタムデータ属性で CSSを効かせる方法、

例) td タグに、data-date="2016/09/04" にある場合、

td[data-date="2016/09/04"]{
   background-color: #ffc0cb !important;
}

のように書く。

data 属性の値はプログラムで動的に付与していくケースがほとんどと思われるので、

^= の記述で、

      td[data-date^="2016/09/"]

と書けば、2016年 9月の td タグにスタイルを充てることができる。

あるいは、jQuery を走らせて CSS をセットすれば、動的な表示に対するスタイルも充てられるだろう。