9:16 recording: positionable crop strip + on-screen guide frame
New controls in the Registra tab: a 'Posizione taglio' slider choosing WHICH strip of the screen survives the cover-crop when the format differs from the display (ffmpeg crop x/y driven by cropX 0..1), and a guide checkbox. The guide is a dashed frame + dimmed surroundings drawn on the output window (outside #stage, last in DOM, overlay only — composeFrame never captures it), live-updated from slider/format and hidden when no crop applies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVMQ2JhsJ77pGKqcvvFHqc
This commit is contained in:
1 parent
51c6bca2c8
commit
c2709ff158
6 files changed
+86
-5
No files matched your search
+34
-2
@@ -1866,14 +1866,45 @@ function fmtTime(s) {
|
||||
function recTick() { $('#rec-time').textContent = fmtTime(Math.floor((Date.now() - recStartedAt) / 1000)); }
|
||||
function stopRecTimer() { if (recTimer) { clearInterval(recTimer); recTimer = null; } }
|
||||
|
||||
// Crop position + on-screen guide for formats that differ from the screen
|
||||
// (e.g. 9:16 reels): the slider picks WHICH strip of the screen is recorded,
|
||||
// the guide draws that area on the output (overlay only — never recorded).
|
||||
let recCropCfg = { pos: 50, guide: true };
|
||||
try { recCropCfg = Object.assign(recCropCfg, JSON.parse(localStorage.getItem('reccrop') || '{}')); } catch (e) {}
|
||||
function recGuideSend() {
|
||||
const [w, h] = $('#rec-aspect').value.split('x').map(Number);
|
||||
send({ type: 'recGuide', on: recCropCfg.guide, w, h, pos: recCropCfg.pos / 100 });
|
||||
}
|
||||
function recCropLabel() {
|
||||
const p = recCropCfg.pos;
|
||||
$('#rec-crop-val').textContent = p === 50 ? 'centro' : (p < 50 ? '◀ ' + (50 - p) * 2 + '%' : (p - 50) * 2 + '% ▶');
|
||||
}
|
||||
(function recCropInit() {
|
||||
$('#rec-crop').value = recCropCfg.pos;
|
||||
$('#rec-guide-chk').checked = recCropCfg.guide;
|
||||
recCropLabel();
|
||||
$('#rec-crop').addEventListener('input', (e) => {
|
||||
recCropCfg.pos = parseInt(e.target.value, 10);
|
||||
localStorage.setItem('reccrop', JSON.stringify(recCropCfg));
|
||||
recCropLabel(); recGuideSend();
|
||||
});
|
||||
$('#rec-guide-chk').addEventListener('change', (e) => {
|
||||
recCropCfg.guide = e.target.checked;
|
||||
localStorage.setItem('reccrop', JSON.stringify(recCropCfg));
|
||||
recGuideSend();
|
||||
});
|
||||
$('#rec-aspect').addEventListener('change', recGuideSend);
|
||||
})();
|
||||
|
||||
$('#btn-rec').addEventListener('click', () => {
|
||||
if (!recOn) {
|
||||
// Pass format + optional max duration so the output can auto-stop (Reel mode).
|
||||
const [w, h] = $('#rec-aspect').value.split('x').map(Number);
|
||||
send({ type: 'recStart', w, h, maxMs: parseInt($('#rec-dur').value, 10) || 0 });
|
||||
send({ type: 'recStart', w, h, maxMs: parseInt($('#rec-dur').value, 10) || 0, cropX: recCropCfg.pos / 100 });
|
||||
recGuideSend();
|
||||
} else {
|
||||
const [w, h] = $('#rec-aspect').value.split('x').map(Number);
|
||||
send({ type: 'recStop', w, h });
|
||||
send({ type: 'recStop', w, h, cropX: recCropCfg.pos / 100 });
|
||||
stopRecTimer();
|
||||
$('#rec-label').textContent = 'Conversione MP4…';
|
||||
$('#btn-rec').disabled = true;
|
||||
@@ -2171,6 +2202,7 @@ djv.onReport((m) => {
|
||||
if (ledCfg.on) ledSend();
|
||||
glbSend();
|
||||
mapSendAll();
|
||||
recGuideSend();
|
||||
const sel = $('#device-select');
|
||||
const cur = sel.value;
|
||||
sel.innerHTML = '<option value="">— Input live (mic/line/BlackHole) —</option>';
|
||||
|
||||
Reference in new issue
Block a user