diff --git a/ui/lib/jli.js b/ui/lib/jli.js index 3d0ba641..253e9c77 100755 --- a/ui/lib/jli.js +++ b/ui/lib/jli.js @@ -401,6 +401,184 @@ function setElementTransform(elem, offset, scale, duration){ } +// Run a function controllably in an animation frame +// +// NOTE: we do not need to make this run several callbacks as the +// browser already does this and will do the loop faster... +function animationFrameRunner(func){ + var next + var _nop = function(){ return this } + var frame + + if(this === window){ + self = new animationFrameRunner + } else { + self = this + } + + self.func = func + + var _tick = function(){ + func(Date.now()) + frame = getAnimationFrame(next) + } + + // main user interface... + var start = function(){ + next = _tick + this.start = _nop + this.stop = stop + + // start things up... + // NOTE: we are not calling _tick here directly to avoid stray, + // off-frame call to func... + frame = getAnimationFrame(next) + + return this + } + var stop = function(){ + if(frame != null){ + cancelAnimationFrame(frame) + frame = null + } + next = _nop + this.start = start + this.stop = _nop + return this + } + + // setup the ticker in stopped state... + stop.call(self) + + return self +} + + +/* +// NOTE: this is exclusive, e.g. all other animations set with this will +// be stopped on call... +// XXX for some reason this is slower that animateElementTo(..) on iPad... +function animateElementTo2(elem, to, duration, easing, speed, use_transitions){ + use_transitions = use_transitions != null ? + use_transitions + : USE_TRANSITIONS_FOR_ANIMATION + // use transition for animation... + if(use_transitions){ + setTransitionEasing(elem, easing) + duration == null && setTransitionDuration(elem, duration) + setElementTransform(elem, to) + return + } + + to = typeof(to) == typeof(1) ? { + left: to, + top: 0, + } : to + speed = typeof(speed) == typeof(2) ? { + x: speed, + y: 0, + } : speed + duration = duration == null ? getElementTransitionDuration(elem) : duration + + // stop other animations... + var runner = elem.data('animating') + if(runner != null){ + runner.stop() + } + + // setup context... + var start = Date.now() + var then = start + duration + var from = getElementShift(elem) + + // do var caching... + var to_top = to.top + var to_left = to.left + var from_top = from.top + var from_left = from.left + var cur_top = from_top + var cur_left = from_left + var dist_top = to_top - from_top + var dist_left = to_left - from_left + if(speed != null){ + var speed_x = speed.x + var speed_y = speed.y + } + + elem.animating = true + + var runner = animationFrameRunner(function(t){ + // end of the animation... + if(t >= then){ + setElementTransform(elem, to) + runner.stop() + return + } + // animation stopped... + if(!elem.animating){ + setElementTransform(elem, cur) + runner.stop() + return + } + + // calculate target position for current step... + if(speed != null){ + // NOTE: these are inlined here for speed... + if(Math.abs(dist_top) >= 1){ + dy = ((t - start) * speed_y) + if(Math.abs(dist_top) > Math.abs(dy)){ + dist_top -= dy + cur_top = Math.round(cur_top + dy) + // normalize... + cur_top = Math.abs(dist_top) <= 1 ? to_top : cur_top + // calc speed for next step... + speed_y = dist_top / (duration - (t - start)) + } else { + cur_top = to_top + } + } + if(Math.abs(dist_left) >= 1){ + dx = ((t - start) * speed_x) + if(Math.abs(dist_left) > Math.abs(dx)){ + dist_left -= dx + cur_left = Math.round(cur_left + dx) + // normalize... + cur_left = Math.abs(dist_left) <= 1 ? to_left : cur_left + // calc speed for next step... + speed_x = dist_left / (duration - (t - start)) + } else { + cur_left = to_left + } + } + + // liner speed... + } else { + var r = (t - start) / duration + cur_top = Math.round(from_top + (dist_top * r)) + cur_left = Math.round(from_left + (dist_left * r)) + } + + // do the actual move... + setElementTransform(elem, { + top: cur_top, + left: cur_left + }) + }) + + elem.data('animating', runner) + return runner.start() +} + + +function stopAnimation2(elem){ + var runner = elem.data('animating') + if(runner != null){ + runner.stop() + } +} +*/ + + // XXX make this a drop-in replacement for setElementTransform... // XXX cleanup, still flacky... function animateElementTo(elem, to, duration, easing, speed, use_transitions){ @@ -446,9 +624,16 @@ function animateElementTo(elem, to, duration, easing, speed, use_transitions){ top: to.top - from.top, left: to.left - from.left, } + + // XXX are we using this... elem.animating = true + elem.next_frame = null function animate(){ + // prevent running animations till next call of animateElementTo(..) + if(elem.next_frame === false){ + return + } var t = Date.now() // end of the animation... if(t >= then){ @@ -461,14 +646,10 @@ function animateElementTo(elem, to, duration, easing, speed, use_transitions){ return } - // do an intermediate step... - // XXX do proper easing... - // XXX sometimes results in jumping around... - // ...result of jumping over the to position... + // animate a step with speed... if(speed != null){ - - // XXX the following two blocks are the same... - // XXX looks a bit too complex, revise... + // NOTE: these are almost identical, they are inlined + // for speed... if(Math.abs(dist.top) >= 1){ dy = ((t - start) * speed.y) if(Math.abs(dist.top) > Math.abs(dy)){ @@ -482,8 +663,6 @@ function animateElementTo(elem, to, duration, easing, speed, use_transitions){ cur.top = to.top } } - - // XXX looks a bit too complex, revise... if(Math.abs(dist.left) >= 1){ dx = ((t - start) * speed.x) if(Math.abs(dist.left) > Math.abs(dx)){ @@ -498,7 +677,7 @@ function animateElementTo(elem, to, duration, easing, speed, use_transitions){ } } - // XXX this is a straight forward linear function... + // liner animate... } else { var r = (t - start) / duration cur.top = Math.round(from.top + (dist.top * r)) @@ -517,9 +696,15 @@ function animateElementTo(elem, to, duration, easing, speed, use_transitions){ function stopAnimation(elem){ if(elem.next_frame){ cancelAnimationFrame(elem.next_frame) + elem.next_frame = false + return } } +// XXX for some odd reason the 2'nd gen. animation is jittery... +//animateElementTo = animateElementTo2 +//stopAnimation = stopAnimation2 + // XXX account for other transitions... function setElementScale(elem, scale){ diff --git a/ui/marks.js b/ui/marks.js index 2159bffa..e2fc33fc 100755 --- a/ui/marks.js +++ b/ui/marks.js @@ -804,6 +804,7 @@ function setupMarks(viewer){ marksUpdated() } }) + // when the gid is swapped update the cache... .on('updatedImageGID', function(evt, was, is){ var i = MARKED.indexOf(was) if(i >= 0){ diff --git a/ui/modes.js b/ui/modes.js index 4451a481..53ff0952 100755 --- a/ui/modes.js +++ b/ui/modes.js @@ -371,10 +371,6 @@ var toggleImageProportions = createCSSClassToggler( }) } - // account for rotation... - correctImageProportionsForRotation(image) - centerView(null, 'css') - // square proportions... // NOTE: this will reset the size to default (defined in CSS) } else { @@ -382,12 +378,12 @@ var toggleImageProportions = createCSSClassToggler( width: '', height: '' }) - - // account for rotation... - correctImageProportionsForRotation(image) - centerView(null, 'css') } + // account for rotation... + correctImageProportionsForRotation(image) + centerView(null, 'css') + viewer.trigger('updatingImageProportions') }) @@ -440,5 +436,76 @@ var toggleImageInfoDrawer = makeDrawerToggler( +/********************************************************************** +* Experimental... +*/ + +function getImageProportions(gid){ + gid = gid == null ? getImageGID() : gid + var o = IMAGES[gid].orientation + o = o == null ? 0 : o + + var res = $.Deferred() + + var i = new Image() + i.onload = function(){ + if(o == 0 || o == 180){ + var w = i.width/i.height + } else { + var w = i.height/i.width + } + res.resolve(w) + } + i.src = getBestPreview(gid).url + + return res +} + + +function _fitImageToRibbonHeight(gid, image){ + setTimeout(function(){ + getImageProportions(gid).done(function(r){ + var h = image.height() + image.css({ + width: h * r, + }) + correctImageProportionsForRotation(image, image) + }) + }, 0) + return image +} + + +// XXX this does not work yet + I'm not sure if we need it... +var toggleRibbonImageProportions = createCSSClassToggler( + '.viewer', + [ + 'none', + 'ribbon-image-proportions' + ], + function(action){ + var image = $('.image') + + if(action == 'ribbon-image-proportions'){ + // register the updater... + IMAGE_UPDATERS.push(_fitImageToRibbonHeight) + updateImages() + + } else { + // unregister the updater... + IMAGE_UPDATERS.splice(IMAGE_UPDATERS.indexOf(_fitImageToRibbonHeight), 1) + image.css({ + width: '', + height: '' + }) + // account for rotation... + correctImageProportionsForRotation(image) + } + + centerView(null, 'css') + }) + + + /********************************************************************** * vim:set ts=4 sw=4 : */ diff --git a/ui/ribbons.js b/ui/ribbons.js index c72c78a6..61258179 100755 --- a/ui/ribbons.js +++ b/ui/ribbons.js @@ -915,10 +915,11 @@ function nextRibbon(){ // // NOTE: this is not needed for square image blocks. // NOTE: if an image block is square, this will remove the margins. -function correctImageProportionsForRotation(images){ - var viewer = $('.viewer') - var W = viewer.innerWidth() - var H = viewer.innerHeight() +function correctImageProportionsForRotation(images, container){ + container = container == null ? $('.viewer') : container + + var W = container.innerWidth() + var H = container.innerHeight() var viewer_p = W > H ? 'landscape' : 'portrait' diff --git a/ui/tags.js b/ui/tags.js index 235dfeed..b38835ca 100755 --- a/ui/tags.js +++ b/ui/tags.js @@ -558,6 +558,7 @@ function setupUnsortedTagHandler(viewer){ tagsUpdated() } }) + // when the gid is swapped update the cache... .on('updatedImageGID', function(evt, was, is){ var updated = false for(var tag in TAGS){