recording and setKeyframes entanglement
This commit is contained in:
parent
7f6ab572c7
commit
de5550287c
6 changed files with 738 additions and 552 deletions
File diff suppressed because it is too large
Load diff
|
@ -91,6 +91,7 @@ const config = {
|
|||
},
|
||||
record: {
|
||||
ignoreProps: ['fontVariationAxes','letterDelays','color'],
|
||||
recordMapped: true,
|
||||
},
|
||||
midi: {
|
||||
touchTimeThreshold_s: 0.5,
|
||||
|
|
|
@ -188,7 +188,6 @@ window.onload = () => {
|
|||
alert('Sorry, Variable Time is a tool currently designed to be used on desktop!');
|
||||
}
|
||||
window.addEventListener('panelEvent', (e) => {
|
||||
console.log('debug panelEvent received', e);
|
||||
clearTimeout(window.panelFinderTimeout);
|
||||
let target = false;
|
||||
if (e.detail.panelID === 'artboard') {
|
||||
|
@ -204,7 +203,6 @@ window.onload = () => {
|
|||
}
|
||||
});
|
||||
window.addEventListener('sequenceEvent', (e) => {
|
||||
console.log('debug sequenceEvent received', e);
|
||||
let target = false;
|
||||
if (e.detail.panelID === 'artboard') {
|
||||
target = artboard;
|
||||
|
|
|
@ -137,7 +137,7 @@ const Record = function(tp) {
|
|||
return hot.hasOwnProperty(layerID)
|
||||
&& hot[layerID].hasOwnProperty(propTitle);
|
||||
};
|
||||
const makeHot = (layerID, propTitle) => {
|
||||
const addHot = (layerID, propTitle) => {
|
||||
if (!isHot(layerID, propTitle)) {
|
||||
if (!hot.hasOwnProperty(layerID)) {
|
||||
hot[layerID] = {};
|
||||
|
@ -146,11 +146,36 @@ const Record = function(tp) {
|
|||
recording: [],
|
||||
};
|
||||
}
|
||||
const button = tp
|
||||
.getPanelPropContainer(propTitle)
|
||||
.querySelector('.recordButton');
|
||||
if (button !== null) {
|
||||
button.classList.add('active');
|
||||
buffy.register(layerID);
|
||||
// handle UI only if layer is selected
|
||||
if (getLayer(layerID).isSelected()) {
|
||||
const button = tp
|
||||
.getPanelPropContainer(propTitle)
|
||||
.querySelector('.recordButton');
|
||||
if (button !== null) {
|
||||
button.classList.add('active');
|
||||
}
|
||||
}
|
||||
};
|
||||
const removeHot = (layerID, propTitle) => {
|
||||
if (isHot(layerID, propTitle)) {
|
||||
delete hot[layerID][propTitle];
|
||||
}
|
||||
// what if it is the last prop in the layer
|
||||
if (hot.hasOwnProperty(layerID)) {
|
||||
if (Object.keys(hot[layerID]).length === 0) {
|
||||
delete hot[layerID];
|
||||
buffy.deregister(layerID);
|
||||
}
|
||||
}
|
||||
// handle UI only if layer is selected
|
||||
if (getLayer(layerID).isSelected()) {
|
||||
const button = tp
|
||||
.getPanelPropContainer(propTitle)
|
||||
.querySelector('.recordButton');
|
||||
if (button !== null) {
|
||||
button.classList.remove('active');
|
||||
}
|
||||
}
|
||||
};
|
||||
//const makeNotHot = (layerID, propTitle) => {
|
||||
|
@ -181,21 +206,27 @@ const Record = function(tp) {
|
|||
if(isRecording) {
|
||||
stopRecording();
|
||||
} else {
|
||||
Object.keys(audio.mapping)
|
||||
.forEach((layerID) => {
|
||||
if (getLayer(layerID).isSelected()) {
|
||||
Object.keys(audio.mapping[layerID])
|
||||
.forEach((propTitle) => {
|
||||
makeHot(layerID, propTitle);
|
||||
if (config.record.recordMapped) {
|
||||
// make all mapped props hot and
|
||||
Object.keys(audio.mapping)
|
||||
.forEach((layerID) => {
|
||||
if (getLayer(layerID).isSelected()) {
|
||||
Object.keys(audio.mapping[layerID])
|
||||
.forEach((propTitle) => {
|
||||
addHot(layerID, propTitle);
|
||||
});
|
||||
}
|
||||
});
|
||||
buffy.register(layerID);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// only make this propTitle hot and
|
||||
// register its layer for recording
|
||||
addHot(layer.id(), propTitle);
|
||||
}
|
||||
startRecording();
|
||||
}
|
||||
});
|
||||
console.log("Record::addRecordButton",
|
||||
`added a record button for ${propTitle}`);
|
||||
//console.log("Record::addRecordButton",
|
||||
//`added a record button for ${propTitle}`);
|
||||
}
|
||||
} else {
|
||||
console.log("Record::addRecordButton",
|
||||
|
@ -300,7 +331,7 @@ const Record = function(tp) {
|
|||
lastPosition = position;
|
||||
});
|
||||
} else {
|
||||
console.log('whoops input_clone is null');
|
||||
console.log('Record::startRecording', `whoops input_clone for ${propTitle} is null`);
|
||||
}
|
||||
});
|
||||
tp.sheet.sequence.position = 0;
|
||||
|
@ -310,11 +341,7 @@ const Record = function(tp) {
|
|||
};
|
||||
const stopRecording = () => {
|
||||
return new Promise((resolve) => {
|
||||
console.log('stoprecording');
|
||||
const layerKeys = Object.keys(hot);
|
||||
console.log('stopRecording', 'layerKeys', {
|
||||
layerKeys
|
||||
}, 'hot', JSON.stringify(hot));
|
||||
const promises = [];
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
|
@ -329,23 +356,19 @@ const Record = function(tp) {
|
|||
});
|
||||
});
|
||||
layerKeys.forEach((layerID) => {
|
||||
console.log('stopRecording', layerID);
|
||||
const layer = getLayer(layerID);
|
||||
const propTitles = Object.keys(hot[layerID]);
|
||||
const keyframes = [];
|
||||
propTitles.forEach((propTitle) => {
|
||||
console.log('stopRecording', propTitle);
|
||||
// NOTE: layerID is not actually used atm
|
||||
// and should be the layer anyways
|
||||
uncloneInput(layerID, propTitle);
|
||||
console.log('stopRecording', 'should have uncloned input for ' + propTitle);
|
||||
keyframes.push({
|
||||
path: [propTitle],
|
||||
keyframes: hot[layerID][propTitle].recording,
|
||||
});
|
||||
});
|
||||
//setTimeout(() => {
|
||||
console.log('stopRecording', 'adding the keyframes now because we wnat it to happen right now please', keyframes);
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
tp.setKeyframes(layer, keyframes).then(() => {
|
||||
|
@ -358,17 +381,12 @@ const Record = function(tp) {
|
|||
});
|
||||
sequencialPromises(promises, () => {
|
||||
Object.keys(hot).forEach((layerID) => {
|
||||
buffy.deregister(layerID);
|
||||
Object.keys(hot[layerID]).forEach((propTitle) => {
|
||||
delete hot[layerID][propTitle];
|
||||
if (Object.keys(hot[layerID]).length === 0) {
|
||||
delete hot[layerID];
|
||||
}
|
||||
const button = tp.getPanel().querySelector(`.recordButton${propTitle}`);
|
||||
button.classList.remove('active');
|
||||
removeHot(layerID, propTitle);
|
||||
});
|
||||
buffy.deregister(layerID);
|
||||
});
|
||||
console.log('stopRecording', 'absolutely stopped recording');
|
||||
console.log('Record::stopRecording', 'stopped recording');
|
||||
isRecording = false;
|
||||
resolve();
|
||||
});
|
||||
|
@ -377,6 +395,8 @@ const Record = function(tp) {
|
|||
|
||||
// public
|
||||
this.addRecordButton = addRecordButton;
|
||||
this.addHot = addHot;
|
||||
this.removeHot = removeHot;
|
||||
this.getHot = () => {
|
||||
return hot;
|
||||
};
|
||||
|
|
|
@ -135,48 +135,84 @@ const TheatrePlay = function(autoInit = false) {
|
|||
return keyframes;
|
||||
};
|
||||
const getSequenceButton = (path) => {
|
||||
let t = getPanelPropTitle(path.join('.'));
|
||||
let t = getPanelPropTitle(Array.isArray(path) ? path.join('.') : path);
|
||||
if (t === null) {
|
||||
return null;
|
||||
}
|
||||
return t.parentElement.querySelector('[title="Sequence this prop"]');
|
||||
};
|
||||
const isSequenced = (path) => {
|
||||
return getSequenceButton(path) === null;
|
||||
};
|
||||
|
||||
const setSequenced = (propTitle, sequenced) => {
|
||||
return new Promise((resolve) => {
|
||||
const contextItem = sequenced ? 'sequence' : 'make static';
|
||||
const antiContextItem = sequenced ? 'make static' : 'sequence';
|
||||
const setSequenced = (propTitle, sequenced, metaResolve = false) => {
|
||||
const f = (resolve) => {
|
||||
const propIsSequenced = isSequenced(propTitle);
|
||||
const somethingToDo = sequenced !== propIsSequenced;
|
||||
|
||||
const finishedSequencedEvent = (e) => {
|
||||
tp.getPanel().removeEventListener('injected', finishedSequencedEvent);
|
||||
console.log('debug FINISHED SEQUENCED EVENT', e, propTitle);
|
||||
resolve(true);
|
||||
};
|
||||
if (somethingToDo) {
|
||||
const contextItem = sequenced ? 'sequence' : 'make static';
|
||||
const antiContextItem = sequenced ? 'make static' : 'sequence';
|
||||
|
||||
const clickContextMenu = () => {
|
||||
let done = false;
|
||||
tp.getPanelPropTitle(propTitle).removeEventListener('contextmenu', clickContextMenu);
|
||||
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => {
|
||||
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) {
|
||||
tp.getPanel().addEventListener('injected', finishedSequencedEvent);
|
||||
s.click();
|
||||
console.log('debug click');
|
||||
done = true;
|
||||
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) {
|
||||
done = true;
|
||||
resolve(false);
|
||||
const finishedSequencedEvent = (e) => {
|
||||
// only care about events from our prop
|
||||
if (propTitle === e.detail.prop.join('.')) {
|
||||
// if we un-sequence, we listen to stateEditors' event
|
||||
if (!sequenced && e.detail.origin === 'stateEditors.ts' && e.detail.sequenced === sequenced) {
|
||||
window.removeEventListener('sequenceEvent', finishedSequencedEvent);
|
||||
resolve(true);
|
||||
|
||||
// if we sequence, then we wait until the track is there
|
||||
} else if (sequenced && e.detail.origin === 'BasicKeyframedTrack.tsx' && e.detail.sequenced === sequenced) {
|
||||
window.removeEventListener('sequenceEvent', finishedSequencedEvent);
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log('TheatrePlayu::setSequenced', 'ignored event', e, e.detail);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!done) {
|
||||
setTimeout(() => {
|
||||
clickContextMenu();
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu);
|
||||
getPanelPropTitle(propTitle).dispatchEvent(new Event('contextmenu'));
|
||||
});
|
||||
let counter = 0;
|
||||
const clickContextMenu = (e) => {
|
||||
let done = false;
|
||||
if (e.target !== null) {
|
||||
e.target.removeEventListener('contextmenu', clickContextMenu);
|
||||
}
|
||||
tp.shadowRoot.querySelectorAll('ul li span').forEach((s) => {
|
||||
if (s.innerHTML.toLowerCase() === contextItem.toLowerCase()) {
|
||||
window.addEventListener('sequenceEvent', finishedSequencedEvent);
|
||||
s.click();
|
||||
done = true;
|
||||
} else if (s.innerHTML.toLowerCase() === antiContextItem.toLowerCase()) {
|
||||
done = true;
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
if (!done) {
|
||||
setTimeout(() => {
|
||||
if (counter < 4) {
|
||||
clickContextMenu(e);
|
||||
counter++;
|
||||
} else {
|
||||
setSequenced(propTitle, sequenced, resolve);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
getPanelPropTitle(propTitle).addEventListener('contextmenu', clickContextMenu);
|
||||
getPanelPropTitle(propTitle).dispatchEvent(new Event('contextmenu'));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
if (!metaResolve) {
|
||||
return new Promise((resolve) => {
|
||||
f(resolve);
|
||||
});
|
||||
} else {
|
||||
f(metaResolve);
|
||||
}
|
||||
};
|
||||
|
||||
const addKeyframes = (layer, keyframes) => {
|
||||
|
@ -187,48 +223,53 @@ const TheatrePlay = function(autoInit = false) {
|
|||
}
|
||||
const existingKeyframes = getKeyframes(layer);
|
||||
const promises = [];
|
||||
const ms = 0;//config.tp.addKeyframesTimeout_s * 1000;
|
||||
const ms = 0; //config.tp.addKeyframesTimeout_s * 1000;
|
||||
keyframes.forEach((k) => {
|
||||
let prop = layer.theatreObject.props;
|
||||
for (let i = 0; i < k.path.length; i++) {
|
||||
prop = prop[k.path[i]];
|
||||
}
|
||||
const position = tp.sheet.sequence.position;
|
||||
// NOTE: can we sequence values without pretend clicking?
|
||||
const sequenceButton = getSequenceButton(k.path);
|
||||
if (sequenceButton !== null) {
|
||||
promises.push(() => { return new Promise((subResolve) => {
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
setTimeout(() => {
|
||||
sequenceButton.click();
|
||||
const detectSE = (e) => {
|
||||
if (e.detail.panelID === layer.id()) {
|
||||
window.removeEventListener('sequenceEvent',detectSE);
|
||||
console.log('received sequenceEvent',e);
|
||||
const f = (e) => {
|
||||
tp.getPanel().removeEventListener('injected', f);
|
||||
if (layer.isSelected()) {
|
||||
setSequenced(k.path.join('.'), true)
|
||||
.then(() => {
|
||||
subResolve();
|
||||
};
|
||||
tp.getPanel().addEventListener('injected', f);
|
||||
}
|
||||
};
|
||||
window.addEventListener('sequenceEvent', detectSE);
|
||||
}, ms);// * promises.length);
|
||||
})});
|
||||
} else {
|
||||
//console.error(k.path, 'did not find sequence button');
|
||||
// is (probably) already sequenced
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// we cannot select layers without pseudoclicking
|
||||
// so let's wait for a happy 'injected' event that
|
||||
// closes off the selection
|
||||
//
|
||||
// first, the listener callback
|
||||
const f = () => {
|
||||
tp.getPanel().removeEventListener('injected', f);
|
||||
setSequenced(k.path.join('.'), true)
|
||||
.then(() => {
|
||||
subResolve();
|
||||
});
|
||||
};
|
||||
// then add it
|
||||
tp.getPanel().addEventListener('injected', f);
|
||||
// and fire the click
|
||||
layer.select();
|
||||
}
|
||||
}, ms); // * promises.length);
|
||||
})
|
||||
});
|
||||
let propHasKeyframesAt = -1;
|
||||
if (existingKeyframes !== null &&
|
||||
existingKeyframes !== false &&
|
||||
typeof existingKeyframes !== 'undefined' &&
|
||||
Array.isArray(existingKeyframes)) {
|
||||
existingKeyframes.forEach((existingK, existingKI) => {
|
||||
if (arraysEqual(k.path, existingK.path)) {
|
||||
propHasKeyframesAt = existingKI;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (existingKeyframes !== null &&
|
||||
existingKeyframes !== false &&
|
||||
typeof existingKeyframes !== 'undefined' &&
|
||||
Array.isArray(existingKeyframes)) {
|
||||
existingKeyframes.forEach((existingK, existingKI) => {
|
||||
if (arraysEqual(k.path, existingK.path)) {
|
||||
propHasKeyframesAt = existingKI;
|
||||
}
|
||||
});
|
||||
}
|
||||
k.keyframes.forEach((keyframe) => {
|
||||
let alreadyThere = false;
|
||||
if (propHasKeyframesAt >= 0) {
|
||||
|
@ -240,29 +281,33 @@ const TheatrePlay = function(autoInit = false) {
|
|||
});
|
||||
}
|
||||
if (!alreadyThere) {
|
||||
promises.push(() => { return new Promise((subResolve) => {
|
||||
setTimeout(() => {
|
||||
tp.sheet.sequence.position = keyframe.position;
|
||||
this.studio.transaction(({
|
||||
set
|
||||
}) => {
|
||||
set(prop, keyframe.value);
|
||||
subResolve();
|
||||
});
|
||||
}, ms);// * promises.length);
|
||||
})});
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
setTimeout(() => {
|
||||
tp.sheet.sequence.position = keyframe.position;
|
||||
this.studio.transaction(({
|
||||
set
|
||||
}) => {
|
||||
set(prop, keyframe.value);
|
||||
subResolve();
|
||||
});
|
||||
}, ms); // * promises.length);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
promises.push(() => { return new Promise((subResolve) => {
|
||||
setTimeout(() => {
|
||||
tp.sheet.sequence.position = position;
|
||||
subResolve();
|
||||
}, ms);// * promises.length);
|
||||
})});
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
setTimeout(() => {
|
||||
tp.sheet.sequence.position = position;
|
||||
subResolve();
|
||||
}, ms); // * promises.length);
|
||||
})
|
||||
});
|
||||
});
|
||||
sequencialPromises(promises, resolve);
|
||||
//Promise.all(promises).then(() => {
|
||||
//resolve();
|
||||
//resolve();
|
||||
//});
|
||||
});
|
||||
};
|
||||
|
@ -273,19 +318,28 @@ const TheatrePlay = function(autoInit = false) {
|
|||
return false;
|
||||
}
|
||||
const promises = [];
|
||||
let waitify = false;
|
||||
keyframes.forEach((k) => {
|
||||
promises.push(new Promise((subResolve) => {
|
||||
const propTitle = k.path.join('.');
|
||||
setSequenced(propTitle, false)
|
||||
.then(subResolve);
|
||||
}));
|
||||
const propTitle = k.path.join('.');
|
||||
if (isSequenced(propTitle)) {
|
||||
waitify = true;
|
||||
promises.push(() => {
|
||||
return new Promise((subResolve) => {
|
||||
setSequenced(propTitle, false)
|
||||
.then(() => {
|
||||
subResolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
Promise
|
||||
.all(promises)
|
||||
.then(() => {
|
||||
sequencialPromises(promises, () => {
|
||||
const timeout_ms = waitify ? 1000 : 0;
|
||||
setTimeout(() => {
|
||||
addKeyframes(layer, keyframes)
|
||||
.then(resolve);
|
||||
});
|
||||
}, timeout_ms);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -358,6 +412,8 @@ const TheatrePlay = function(autoInit = false) {
|
|||
// remove object from objects list
|
||||
delete theatreObjects[name];
|
||||
};
|
||||
this.isSequenced = isSequenced;
|
||||
this.getSequenceButton = getSequenceButton;
|
||||
this.getSequencePanelLeft = getSequencePanelLeft;
|
||||
this.getPanel = getPanel;
|
||||
this.getPanelPropTitle = getPanelPropTitle;
|
||||
|
|
|
@ -395,11 +395,21 @@ const isMobile = () => {
|
|||
return false;
|
||||
};
|
||||
|
||||
// NOTE:
|
||||
// promises must be delivered inside a function like:
|
||||
//
|
||||
// const promises = [];
|
||||
//
|
||||
// promises.push(() => { return new Promise((resolve) => { console.log('lalala ONE'); resolve() }); });
|
||||
// promises.push(() => { return new Promise((resolve) => { console.log('lalala TWO'); resolve() }); });
|
||||
// promises.push(() => { return new Promise((resolve) => { console.log('lalala THREE'); resolve() }); });
|
||||
//
|
||||
// sequencialPromises(promises, () => { console.log('i am done'); });
|
||||
const sequencialPromises = async (iterable, callback = false) => {
|
||||
for (const x of iterable) {
|
||||
await x();
|
||||
}
|
||||
if (callback !== false) {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue