viewbox drag now visible on playback annotator.
This commit is contained in:
parent
949a16b01f
commit
6d7a223a69
4 changed files with 82 additions and 14 deletions
|
@ -171,6 +171,7 @@ class AnimationSlice:
|
|||
dwg = svgwrite.Drawing(fn, size=(box.width, box.height))
|
||||
dwg.viewbox(box.x, box.y, box.width, box.height)
|
||||
self.add_to_dwg(dwg)
|
||||
dwg.defs.add(dwg.style("path{stroke-width:1mm;stroke-linecap: round;}"))
|
||||
return dwg
|
||||
|
||||
def get_as_svg(self) -> str:
|
||||
|
@ -325,6 +326,7 @@ class AnnotationIndex:
|
|||
self.shelve = {}
|
||||
|
||||
def refresh(self):
|
||||
logger.info("refreshing")
|
||||
# reset the index
|
||||
for key in list(self.shelve.keys()):
|
||||
print(key)
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</ul> -->
|
||||
|
||||
<hr>
|
||||
<a href="?refresh">Reload index</a>
|
||||
<a href="?refresh=1">Reload index</a>
|
||||
</body>
|
||||
<script>
|
||||
let images = document.querySelectorAll('[data-audio]');
|
||||
|
|
|
@ -279,7 +279,7 @@ class AnimationHandler(tornado.web.RequestHandler):
|
|||
self.config.storage, os.path.basename(
|
||||
filename) + ".json_appendable"
|
||||
)
|
||||
drawing = {"file": filename, "shape": []}
|
||||
drawing = {"file": filename, "shape": [], "viewboxes": []}
|
||||
with open(path, "r") as fp:
|
||||
events = json.loads("[" + fp.read() + "]")
|
||||
for i, event in enumerate(events):
|
||||
|
@ -292,7 +292,7 @@ class AnimationHandler(tornado.web.RequestHandler):
|
|||
# ignore double metadatas, which appear when continuaing an existing drawing
|
||||
continue
|
||||
if event["event"] == "viewbox":
|
||||
pass
|
||||
drawing["viewboxes"].extend(event['viewboxes'])
|
||||
if event["event"] == "stroke":
|
||||
# points = []
|
||||
# for i in range(int(len(stroke) / 4)):
|
||||
|
@ -496,9 +496,9 @@ class IndexHandler(tornado.web.RequestHandler):
|
|||
def get(self):
|
||||
do_refresh = bool(self.get_query_argument('refresh', False))
|
||||
if do_refresh:
|
||||
self.logger.info("Reloading Annotation Index")
|
||||
logger.info("Reloading Annotation Index")
|
||||
self.index.refresh()
|
||||
self.logger.info("\treloaded annotation index")
|
||||
logger.info("\treloaded annotation index")
|
||||
|
||||
self.render("templates/index.html", index=self.index)
|
||||
|
||||
|
|
|
@ -476,23 +476,26 @@ class Annotator extends EventTarget {
|
|||
}
|
||||
this.filename = drawing.file;
|
||||
this.strokes = drawing.shape.map(s => new Stroke(s['color'], s['points']));
|
||||
this.viewboxes = drawing.viewboxes;
|
||||
this.currentPathI = null;
|
||||
this.currentPointI = null;
|
||||
this.currentViewboxI = null;
|
||||
this.dimensions = drawing.dimensions;
|
||||
this.svgEl.setAttribute('viewBox', `0 0 ${this.dimensions[0]} ${this.dimensions[1]}`)
|
||||
|
||||
let bgEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
bgEl.setAttribute("x", 0);
|
||||
bgEl.setAttribute("y", 0);
|
||||
bgEl.setAttribute("width", this.dimensions[0]);
|
||||
bgEl.setAttribute("height", this.dimensions[1]);
|
||||
bgEl.classList.add('background');
|
||||
this.svgEl.prepend(bgEl);
|
||||
// let bgEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
// bgEl.setAttribute("x", 0);
|
||||
// bgEl.setAttribute("y", 0);
|
||||
// bgEl.setAttribute("width", this.dimensions[0]);
|
||||
// bgEl.setAttribute("height", this.dimensions[1]);
|
||||
// bgEl.classList.add('background');
|
||||
// this.svgEl.prepend(bgEl);
|
||||
|
||||
this.firstFrameTime = this.strokes[0].points[0][3];
|
||||
this.lastFrameTime = this.getFinalFrameTime();
|
||||
this.playheadEl.max = this.lastFrameTime;
|
||||
this.nextFrameTimeout = null;
|
||||
this.nextViewboxTimeout = null;
|
||||
this._setPausedFlag(true);
|
||||
|
||||
this.formatter = wNumb({
|
||||
|
@ -740,6 +743,15 @@ class Annotator extends EventTarget {
|
|||
// this.currentTime = path.points[point_i][3];
|
||||
}
|
||||
|
||||
setViewboxPosition(box_i) {
|
||||
if(this.currentViewboxI == box_i){
|
||||
return;
|
||||
}
|
||||
this.currentViewboxI = box_i
|
||||
const b = this.viewboxes[box_i];
|
||||
this.svgEl.setAttribute('viewBox', `${b.x} ${b.y} ${this.dimensions[0]} ${this.dimensions[1]}`)
|
||||
}
|
||||
|
||||
getNextPosition(path_i, point_i) {
|
||||
const path = this.strokes[path_i];
|
||||
let next_path, next_point;
|
||||
|
@ -790,6 +802,32 @@ class Annotator extends EventTarget {
|
|||
this.nextFrameTimeout = setTimeout(() => this.playStrokePosition(next_path, next_point, true), dt);
|
||||
}
|
||||
|
||||
|
||||
playViewboxPosition(box_i, allow_interrupt) {
|
||||
if (allow_interrupt) {
|
||||
if (!this.videoIsPlaying) {
|
||||
console.log('not playing because of interrupt');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// this.videoIsPlaying = true;
|
||||
// }
|
||||
this.setViewboxPosition(box_i);
|
||||
|
||||
const next_box_i = box_i+1;
|
||||
if(this.viewboxes.length <= next_box_i){
|
||||
console.debug('done playing viewbox');
|
||||
return;
|
||||
}
|
||||
|
||||
const t = this.viewboxes[next_box_i].t;
|
||||
|
||||
// calculate interval based on playback start to avoid drifting of time
|
||||
const dt = t - (window.performance.now() - this.startTimeMs);
|
||||
this.nextViewboxTimeout = setTimeout(() => this.playViewboxPosition(next_box_i, true), dt);
|
||||
}
|
||||
|
||||
scrubTo(ms) {
|
||||
// const [path_i, point_i] = this.findPositionForTime(ms);
|
||||
// console.log(path_i, point_i);
|
||||
|
@ -816,6 +854,7 @@ class Annotator extends EventTarget {
|
|||
|
||||
_interruptPlayback() {
|
||||
clearTimeout(this.nextFrameTimeout);
|
||||
clearTimeout(this.nextViewboxTimeout);
|
||||
clearTimeout(this.audioEndTimeout);
|
||||
clearTimeout(this.audioStartTimeout);
|
||||
clearTimeout(this.startVideoTimeout);
|
||||
|
@ -835,12 +874,18 @@ class Annotator extends EventTarget {
|
|||
this._seekByTimeMs(this._currentTimeMs); // prevent playback issue for initial load
|
||||
|
||||
this.startTimeMs = window.performance.now() - this._currentTimeMs;
|
||||
|
||||
// strokes
|
||||
if (this._currentTimeMs < 0) {
|
||||
this.startVideoTimeout = setTimeout((e) => this.playStrokePosition(this.currentPathI, this.currentPointI), this._currentTimeMs * -1);
|
||||
} else {
|
||||
this.playStrokePosition(this.currentPathI, this.currentPointI);
|
||||
} this.playAudioSegment(this._currentTimeMs, this.outPointTimeMs);
|
||||
}
|
||||
// viewboxes
|
||||
// const nextViewboxI = Math.max(this.currentViewboxI++, this.viewboxes.length-1);
|
||||
this.playViewboxPosition(this.currentViewboxI);
|
||||
|
||||
// audio
|
||||
this.playAudioSegment(this._currentTimeMs, this.outPointTimeMs);
|
||||
// this.playStrokePosition(this.currentPathI, this.currentPointI);
|
||||
this._setPausedFlag(false);
|
||||
|
||||
|
@ -927,6 +972,7 @@ class Annotator extends EventTarget {
|
|||
this.dispatchEvent(new CustomEvent('seeking', { detail: time }));
|
||||
this._currentTimeMs = Number.parseFloat(time) * 1000;
|
||||
[this.currentPathI, this.currentPointI] = this.findPositionForTime(this._currentTimeMs);
|
||||
|
||||
this.playheadEl.value = this._currentTimeMs;
|
||||
this._updateFrame();
|
||||
this.dispatchEvent(new CustomEvent('seeked', { detail: this.currentTime }));
|
||||
|
@ -934,6 +980,7 @@ class Annotator extends EventTarget {
|
|||
|
||||
_updateFrame() {
|
||||
this.drawStrokePosition(this.inPointPosition, [this.currentPathI, this.currentPointI]);
|
||||
this.setViewboxPosition(this.findViewboxForTime(this._currentTimeMs));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -996,5 +1043,24 @@ class Annotator extends EventTarget {
|
|||
return [path_i, point_i];
|
||||
}
|
||||
|
||||
findViewboxForTime(ms){
|
||||
ms = Math.min(Math.max(ms, 0), this.lastFrameTime);
|
||||
// console.log('scrub to', ms)
|
||||
let box_i = 0;
|
||||
this.viewboxes.every((viewbox, index) => {
|
||||
const startAt = viewbox.t;
|
||||
|
||||
if (startAt > ms) {
|
||||
return false; // too far
|
||||
} else {
|
||||
// in case nothings comes after, we store the last best option thus far
|
||||
box_i = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
return box_i;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue