187 lines
5.6 KiB
JavaScript
187 lines
5.6 KiB
JavaScript
import { $ } from './dom.js';
|
|
import { resetState } from './state.js';
|
|
import { computeHomography, toMatrix3dCSS } from './homography.js';
|
|
import { buildCornerInputs, syncCornerInputs, readCornerInputs, syncFieldInputs, syncWidgetInputs } from './inputs.js';
|
|
import { updateLivePreview } from './preview.js';
|
|
import { setFieldEditing, setMode, toggleKeepAspect, isFieldEditing, getFieldEditW, getFieldEditH, setFieldEditW, setFieldEditH, isKeepAspect } from './mode.js';
|
|
import { setUpdateAllRef, loadBgFile, clearBg, loadWidgetFile, setWidgetImageLock } from './file-loader.js';
|
|
import { initDropZones } from './drop-zone.js';
|
|
|
|
export function updateCSSOutput(state) {
|
|
const H = computeHomography(state);
|
|
$('cssOutput').value = toMatrix3dCSS(H);
|
|
}
|
|
|
|
export function initUI(state, updateAll) {
|
|
setUpdateAllRef(updateAll);
|
|
|
|
$('chainLink').addEventListener('click', () => {
|
|
toggleKeepAspect();
|
|
});
|
|
|
|
$('btnEditField').addEventListener('click', () => {
|
|
setFieldEditing(true, state);
|
|
$('inpFieldW').focus();
|
|
});
|
|
|
|
$('btnCancelField').addEventListener('click', () => {
|
|
setFieldEditing(false, state);
|
|
});
|
|
|
|
$('inpFieldW').addEventListener('input', () => {
|
|
if (!isFieldEditing()) return;
|
|
const newW = parseInt($('inpFieldW').value);
|
|
if (newW <= 0) return;
|
|
if (isKeepAspect()) {
|
|
const newH = Math.round(newW * state.fieldH / state.fieldW);
|
|
$('inpFieldH').value = newH;
|
|
setFieldEditH(newH);
|
|
}
|
|
setFieldEditW(newW);
|
|
});
|
|
|
|
$('inpFieldH').addEventListener('input', () => {
|
|
if (!isFieldEditing()) return;
|
|
const newH = parseInt($('inpFieldH').value);
|
|
if (newH <= 0) return;
|
|
if (isKeepAspect()) {
|
|
const newW = Math.round(newH * state.fieldW / state.fieldH);
|
|
$('inpFieldW').value = newW;
|
|
setFieldEditW(newW);
|
|
}
|
|
setFieldEditH(newH);
|
|
});
|
|
|
|
$('btnSaveField').addEventListener('click', () => {
|
|
const newW = parseInt($('inpFieldW').value);
|
|
const newH = parseInt($('inpFieldH').value);
|
|
if (newW <= 0 || newH <= 0) return;
|
|
|
|
if ($('chkRescaleCoords').checked && (newW !== state.fieldW || newH !== state.fieldH)) {
|
|
const sx = newW / state.fieldW;
|
|
const sy = newH / state.fieldH;
|
|
state.corners.forEach(c => {
|
|
c.x = Math.round(c.x * sx);
|
|
c.y = Math.round(c.y * sy);
|
|
});
|
|
}
|
|
|
|
state.fieldW = newW;
|
|
state.fieldH = newH;
|
|
setFieldEditing(false, state);
|
|
buildCornerInputs(state);
|
|
updateAll();
|
|
});
|
|
|
|
$('inpWidgetW').addEventListener('input', () => {
|
|
if (state.widgetImage) return;
|
|
const v = parseInt($('inpWidgetW').value);
|
|
if (v > 0) { state.widgetW = v; updateAll(); }
|
|
});
|
|
|
|
$('inpWidgetH').addEventListener('input', () => {
|
|
if (state.widgetImage) return;
|
|
const v = parseInt($('inpWidgetH').value);
|
|
if (v > 0) { state.widgetH = v; updateAll(); }
|
|
});
|
|
|
|
document.addEventListener('input', (e) => {
|
|
if (e.target.id && /^c\d[xy]$/.test(e.target.id)) {
|
|
readCornerInputs(state);
|
|
updateAll();
|
|
}
|
|
});
|
|
|
|
$('copyBtn').addEventListener('click', () => {
|
|
const text = $('cssOutput').value;
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
const btn = $('copyBtn');
|
|
const orig = btn.textContent;
|
|
btn.textContent = 'Copied!';
|
|
setTimeout(() => btn.textContent = orig, 1500);
|
|
});
|
|
});
|
|
|
|
$('resetBtn').addEventListener('click', () => {
|
|
setFieldEditing(false, state);
|
|
resetState();
|
|
syncFieldInputs(state, state.fieldW, state.fieldH);
|
|
syncWidgetInputs(state);
|
|
buildCornerInputs(state);
|
|
$('btnBgRemove').style.display = 'none';
|
|
$('bgObjectFit').value = state.objectFit;
|
|
$('inpWidgetW').disabled = false;
|
|
$('inpWidgetH').disabled = false;
|
|
$('btnWidgetRemove').style.display = 'none';
|
|
updateAll();
|
|
});
|
|
|
|
$('btnEdit').addEventListener('click', () => {
|
|
setMode('edit');
|
|
requestAnimationFrame(() => updateLivePreview(state));
|
|
});
|
|
|
|
$('btnPreview').addEventListener('click', () => {
|
|
setMode('preview');
|
|
requestAnimationFrame(() => updateLivePreview(state));
|
|
});
|
|
|
|
window.addEventListener('resize', () => updateLivePreview(state));
|
|
|
|
$('btnBgLoad').addEventListener('click', () => $('bgFileInput').click());
|
|
|
|
$('bgFileInput').addEventListener('change', (e) => {
|
|
if (e.target.files[0]) loadBgFile(e.target.files[0], state);
|
|
e.target.value = '';
|
|
});
|
|
|
|
$('btnBgRemove').addEventListener('click', () => clearBg(state));
|
|
|
|
$('bgObjectFit').addEventListener('change', (e) => {
|
|
state.objectFit = e.target.value;
|
|
updateAll();
|
|
});
|
|
|
|
$('btnWidgetLoad').addEventListener('click', () => $('widgetFileInput').click());
|
|
|
|
$('widgetFileInput').addEventListener('change', (e) => {
|
|
if (e.target.files[0]) loadWidgetFile(e.target.files[0], state);
|
|
e.target.value = '';
|
|
});
|
|
|
|
$('btnWidgetRemove').addEventListener('click', () => {
|
|
state.widgetImage = null;
|
|
setWidgetImageLock(false);
|
|
updateAll();
|
|
});
|
|
|
|
$('widgetPasteInput').addEventListener('paste', (e) => {
|
|
const items = e.clipboardData?.items;
|
|
if (!items) return;
|
|
for (const item of items) {
|
|
if (item.type.startsWith('image/')) {
|
|
const file = item.getAsFile();
|
|
if (file) loadWidgetFile(file, state);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
document.addEventListener('paste', (e) => {
|
|
if (document.activeElement?.id === 'widgetPasteInput') return;
|
|
const tag = document.activeElement?.tagName;
|
|
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
|
|
const items = e.clipboardData?.items;
|
|
if (!items) return;
|
|
for (const item of items) {
|
|
if (item.type.startsWith('image/')) {
|
|
const file = item.getAsFile();
|
|
if (file) loadBgFile(file, state);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
initDropZones(state);
|
|
}
|