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; }
0件のコメント