新規投稿
フォローする

Drag and Drop APIを使ってアイコンレイアウト

Drag and Drop APIはHTML5から実装されたAPIです。 clickイベントなどを細かく書かずとも、ドラッグアンドドロップを簡単に実装できます。 今回はアイコンをレイアウトするアプリを作りました。

サンプル

1)アイコンアプリにアイコン画像ファイルを登録しておく。
2)レイアウトアプリのレコード追加画面で、背景画像を登録する。
3)レイアウトアプリのレコード詳細画面で、背景画像上にアイコンを配置する。

※配置したアイコンを削除する場合は、背景画像の範囲外にドロップします。
※レイアウトアプリ内でアイコンの色やサイズを変更する機能は実装していません。

フォーム設定

アイコンアプリ

レイアウトアプリ

コード

レイアウトアプリに下記sample.jsとsample.cssを読み込ませます。

JavaScript

sample.js

(function() {
  "use strict";
  kintone.events.on(['app.record.create.show', 'app.record.edit.show'], function(event){
    kintone.app.record.setFieldShown('Table', false);
  });
  kintone.events.on('app.record.detail.show', function(event){
    var
      icons,
      listBox = document.createElement('div'),
      layoutBox = document.createElement('div'),
      saveButton = document.createElement('a'),
      createImageUrl = function(fileKey){
        return new kintone.Promise(function(resolve){
          var xhr = new XMLHttpRequest();
          xhr.open('GET', '/k/v1/file.json?fileKey=' + fileKey);
          xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
          xhr.responseType = 'blob';
          xhr.addEventListener('load', function(){
            resolve((window.URL || window.webkitURL).createObjectURL(xhr.response));
          });
          xhr.send();
        });
      },
      placeIcon = function(fileKey, x, y){
        var img = document.createElement('img');
        img.setAttribute('src', icons.find(function(icon){return icon.fileKey===fileKey}).url);
        img.dataset.fileKey = fileKey;
        img.dataset.x = x;
        img.dataset.y = y;
        img.style.left = x + 'px';
        img.style.top = y + 'px';
        img.classList.add('icon');
        layoutBox.appendChild(img);
        img.addEventListener('dragstart', function(e){
          img.classList.add('dragging');
          e.dataTransfer.setData('dragFile', fileKey);
          e.dataTransfer.setData('dragX', e.offsetX);
          e.dataTransfer.setData('dragY', e.offsetY);
        });
      },
      removeIcon = function(){
        if(document.getElementsByClassName('dragging').length)
          layoutBox.removeChild(document.getElementsByClassName('dragging')[0]);
      };
    kintone.app.record.setFieldShown('Table', false);
    kintone.app.record.setFieldShown('背景', false);
    layoutBox.id = 'layoutBox';
    saveButton.innerHTML = '保存';
    kintone.app.record.getSpaceElement('space').appendChild(listBox);
    kintone.app.record.getSpaceElement('space').appendChild(layoutBox);
    kintone.app.record.getSpaceElement('space').appendChild(saveButton);
    createImageUrl(event.record.背景.value[0].fileKey).then(function(url){
      var img = document.createElement('img');
      img.addEventListener('load', function(){
        layoutBox.style.width = img.naturalWidth  + 'px';
        layoutBox.style.height = img.naturalHeight  + 'px';
      });
      img.setAttribute('src', url);
      img.setAttribute('draggable', 'false');
      layoutBox.appendChild(img);
    });
    kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
      app: 10914 //アイコンアプリのID
    }).then(function(response){
      Promise.all(response.records.map(function(record){
        return createImageUrl(record.icon.value[0].fileKey).then(function(url){
          return {
            url: url,
            fileKey: record.icon.value[0].fileKey
          };
        });
      })).then(function(results){
        icons = results;
        icons.forEach(function(icon){
          var img = document.createElement('img');
          img.setAttribute('src', icon.url);
          img.addEventListener('dragstart', function(e){
            e.dataTransfer.setData('dragFile', icon.fileKey);
            e.dataTransfer.setData('dragX', e.offsetX);
            e.dataTransfer.setData('dragY', e.offsetY);
          });
          listBox.appendChild(img);
        });
        event.record.Table.value.forEach(function(row){
          if(!row.value.icon.value) return;
          placeIcon(row.value.icon.value, row.value.x.value, row.value.y.value);
        });
      });
    });
    layoutBox.addEventListener('dragover', function(e){
      e.preventDefault();
    });
    layoutBox.addEventListener('dragleave', function(e){
      removeIcon();
    });
    layoutBox.addEventListener('drop', function(e){
      e.preventDefault();
      removeIcon();
      placeIcon(e.dataTransfer.getData('dragFile'), e.offsetX-e.dataTransfer.getData('dragX'), e.offsetY-e.dataTransfer.getData('dragY'));
    });
    saveButton.addEventListener('click', function(){
      kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', {
        app: kintone.app.getId(),
        id: kintone.app.record.getId(),
        record: {
          Table: {value: [].map.call(document.getElementsByClassName('icon'), function(icon){
            return {value: {
              icon: {type: 'SINGLE_LINE_TEXT', value: icon.dataset.fileKey},
              x: {type: 'NUMBER', value: icon.dataset.x},
              y: {type: 'NUMBER', value: icon.dataset.y},
            }};
          })}
        }
      }).then(function(response){
        alert('保存しました。');
        location.reload();
      });
    });
  });
})();

CSS

sample.css

#layoutBox{
  position: relative;
}
#layoutBox *{
  position: absolute;
}
2

0件のコメント

サインインしてコメントを残してください。