mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-28 18:00:09 +00:00
experementing with virtual-dom...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
0edc34f470
commit
0e7c3a32fd
@ -23,6 +23,7 @@ require('features/ui')
|
|||||||
require('features/ui-partial-ribbons-precache')
|
require('features/ui-partial-ribbons-precache')
|
||||||
require('features/ui-partial-ribbons')
|
require('features/ui-partial-ribbons')
|
||||||
require('features/ui-partial-ribbons-2')
|
require('features/ui-partial-ribbons-2')
|
||||||
|
require('features/ui-partial-ribbons-vdom')
|
||||||
require('features/ui-single-image')
|
require('features/ui-single-image')
|
||||||
require('features/ui-chrome')
|
require('features/ui-chrome')
|
||||||
require('features/ui-progress')
|
require('features/ui-progress')
|
||||||
|
|||||||
@ -65,7 +65,8 @@ core.ImageGridFeatures.Feature('viewer-testing', [
|
|||||||
'ui-single-image',
|
'ui-single-image',
|
||||||
//'ui-partial-ribbons',
|
//'ui-partial-ribbons',
|
||||||
// XXX this still has problems...
|
// XXX this still has problems...
|
||||||
'ui-partial-ribbons-2',
|
//'ui-partial-ribbons-2',
|
||||||
|
'ui-partial-ribbons-vdom',
|
||||||
|
|
||||||
'marks',
|
'marks',
|
||||||
'ui-range',
|
'ui-range',
|
||||||
|
|||||||
368
ui (gen4)/features/ui-partial-ribbons-vdom.js
Executable file
368
ui (gen4)/features/ui-partial-ribbons-vdom.js
Executable file
@ -0,0 +1,368 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
**********************************************************************/
|
||||||
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||||
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var vdom = require('ext-lib/virtual-dom')
|
||||||
|
|
||||||
|
var actions = require('lib/actions')
|
||||||
|
var features = require('lib/features')
|
||||||
|
|
||||||
|
var core = require('features/core')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
// XXX EXPERIMENT: use virtual-dom to do ribbon updates...
|
||||||
|
// - create and maintain a full ribbon view from .ribbon-set and down...
|
||||||
|
// - sync with dom only when needed...
|
||||||
|
// - on direct edits (use .update() / .reload() ???)
|
||||||
|
// - on .updateRibbon(..) -- lazily and when needed...
|
||||||
|
// - see if we can offload the vdom logic to a worker...
|
||||||
|
// XXX using virtual-dom...
|
||||||
|
// - make the below functions into methods...
|
||||||
|
// - add .sync() to sync-up the DOM with virtual dom...
|
||||||
|
// ...this would lead to .updateRibbon(..) to only need to
|
||||||
|
// figure out when to call .sync()
|
||||||
|
// XXX Q: should this be a special imagegrid/ribbons.js implementation
|
||||||
|
// or a different level API??
|
||||||
|
// ...maybe: imagegrid/ribbons-vdom.js as a completely standalone
|
||||||
|
// module that would be mixed with imagegrid/ribbons.js -- sounds
|
||||||
|
// a bit too complicated, overkill??
|
||||||
|
// XXX Q: how should we handle "sync" stuff???
|
||||||
|
// things like toggling marks or rotating an image...
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
window.vdom = vdom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// attribute hooks...
|
||||||
|
function GID(value){
|
||||||
|
this.value = JSON.stringify(value)
|
||||||
|
.replace(/^"(.*)"$/g, '$1') }
|
||||||
|
GID.prototype.hook = function(elem, prop){
|
||||||
|
elem.setAttribute(prop, this.value) }
|
||||||
|
|
||||||
|
function VALUE(value){
|
||||||
|
this.value = value || '' }
|
||||||
|
VALUE.prototype.hook = function(elem, prop){
|
||||||
|
this.value != ''
|
||||||
|
&& elem.setAttribute(prop, this.value) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// XXX get vertical offset and scale...
|
||||||
|
var makeView =
|
||||||
|
window.makeView =
|
||||||
|
function(data, images, count){
|
||||||
|
// XXX
|
||||||
|
var s = 1
|
||||||
|
var x = 0
|
||||||
|
|
||||||
|
var ribbons = data.ribbon_order
|
||||||
|
.map(function(gid){
|
||||||
|
return makeRibbon(gid, data, images, count) })
|
||||||
|
|
||||||
|
return vdom.h('div.ribbon-set', {
|
||||||
|
key: 'ribbon-set',
|
||||||
|
style: {
|
||||||
|
transform: 'scale('+ s +', '+ s +')',
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
vdom.h('div.ribbon-locator', {
|
||||||
|
key: 'ribbon-locator',
|
||||||
|
style: {
|
||||||
|
transform: 'translate3d(0px, '+ x +'px, 0px)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ribbons)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// XXX calc/get count...
|
||||||
|
var makeRibbon =
|
||||||
|
window.makeRibbon =
|
||||||
|
function(gid, data, images, count){
|
||||||
|
var imgs = []
|
||||||
|
|
||||||
|
data.getImages(gid, count, 'total')
|
||||||
|
.forEach(function(gid){
|
||||||
|
imgs.push(makeImage(gid, data, images))
|
||||||
|
makeImageMarks(gid, data, images)
|
||||||
|
.forEach(function(mark){
|
||||||
|
imgs.push(mark) })
|
||||||
|
})
|
||||||
|
return vdom.h('div.ribbon', {
|
||||||
|
key: 'ribbon-'+gid,
|
||||||
|
|
||||||
|
gid: new GID(gid),
|
||||||
|
},
|
||||||
|
imgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// XXX handle previews -- hook???
|
||||||
|
// NOTE: at this point this does not account for previews at all...
|
||||||
|
var makeImage =
|
||||||
|
window.makeImage =
|
||||||
|
function(gid, data, images){
|
||||||
|
var image = (images || {})[gid] || {}
|
||||||
|
var current = data.current == gid ? '.current' : ''
|
||||||
|
|
||||||
|
gid = JSON.stringify(gid).slice(1, -1)
|
||||||
|
|
||||||
|
return vdom.h('div.image'+current, {
|
||||||
|
key: 'image-'+gid,
|
||||||
|
|
||||||
|
gid: new GID(gid),
|
||||||
|
|
||||||
|
orientation: new VALUE(image.orientation),
|
||||||
|
flipped: new VALUE(image.flipped),
|
||||||
|
|
||||||
|
// XXX preview stuff???
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
var makeImageMarks =
|
||||||
|
window.makeImageMarks =
|
||||||
|
function(gid, data, images){
|
||||||
|
gid = JSON.stringify(gid).slice(1, -1)
|
||||||
|
var marks = []
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
|
||||||
|
return marks
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var PartialRibbonsActions = actions.Actions({
|
||||||
|
config: {
|
||||||
|
// Number of screen widths to load...
|
||||||
|
'ribbon-size-screens': 7,
|
||||||
|
|
||||||
|
// Amount of screen widths to keep around the current image...
|
||||||
|
'ribbon-update-threshold': 1.2,
|
||||||
|
|
||||||
|
// Oversize multiplier limit when we resize the ribbon down...
|
||||||
|
'ribbon-resize-threshold': 2,
|
||||||
|
|
||||||
|
// Sets size of ribbons in single image mode...
|
||||||
|
'ribbons-resize-single-image': 21,
|
||||||
|
|
||||||
|
// can be:
|
||||||
|
// 'hybrid'
|
||||||
|
// 'resize'
|
||||||
|
'ribbons-in-place-update-mode': 'resize',
|
||||||
|
|
||||||
|
'ribbons-in-place-update-timeout': 100,
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
'ribbon-update-timeout': 120,
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRibbon: ['- Interface/Update partial ribbon size',
|
||||||
|
function(target, w, size, threshold, preload){
|
||||||
|
target = target instanceof jQuery
|
||||||
|
? this.ribbons.getElemGID(target)
|
||||||
|
// NOTE: data.getImage(..) can return null at start or end
|
||||||
|
// of ribbon, thus we need to account for this...
|
||||||
|
: (this.data.getImage(target)
|
||||||
|
|| this.data.getImage(target, 'after'))
|
||||||
|
w = w || this.screenwidth
|
||||||
|
// get config data and normalize...
|
||||||
|
size = (size
|
||||||
|
|| this.config['ribbon-size-screens']
|
||||||
|
|| 9) * w
|
||||||
|
threshold = threshold == 0 ? threshold
|
||||||
|
: (threshold
|
||||||
|
|| this.config['ribbon-resize-threshold']
|
||||||
|
|| 2)
|
||||||
|
var update_threshold = (this.config['ribbon-update-threshold'] || 2) * w
|
||||||
|
preload = preload === undefined ? true : preload
|
||||||
|
var data = this.data
|
||||||
|
var ribbons = this.ribbons
|
||||||
|
|
||||||
|
var t = Date.now()
|
||||||
|
this.__last_ribbon_update = this.__last_ribbon_update || t
|
||||||
|
var timeout = this.config['ribbons-in-place-update-timeout']
|
||||||
|
var update_timeout = this.config['ribbon-update-timeout']
|
||||||
|
|
||||||
|
// localize transition prevention...
|
||||||
|
// NOTE: we can't get ribbon via target directly here as
|
||||||
|
// the target might not be loaded...
|
||||||
|
var r_gid = data.getRibbon(target)
|
||||||
|
if(r_gid == null){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// NOTE: for the initial load this may be empty...
|
||||||
|
var r = ribbons.getRibbon(r_gid)
|
||||||
|
|
||||||
|
// next/prev loaded...
|
||||||
|
var img = this.ribbons.getImage(target)
|
||||||
|
var nl = img.nextAll('.image:not(.clone)').length
|
||||||
|
var pl = img.prevAll('.image:not(.clone)').length
|
||||||
|
var loaded = nl + pl + 1
|
||||||
|
|
||||||
|
// next/prev available...
|
||||||
|
// NOTE: we do not include target in counts...
|
||||||
|
var gids = this.data.getImages(target, size, 'total')
|
||||||
|
var na = gids.slice(gids.indexOf(target)+1).length
|
||||||
|
var pa = gids.slice(0, gids.indexOf(target)).length
|
||||||
|
|
||||||
|
// full resize...
|
||||||
|
if(threshold == 0
|
||||||
|
// ribbon not loaded...
|
||||||
|
|| img.length == 0
|
||||||
|
// ribbon shorter than we expect...
|
||||||
|
|| (loaded < size && na + pa > loaded)
|
||||||
|
// ribbon too long...
|
||||||
|
|| loaded > size * threshold
|
||||||
|
// passed hard threshold -- too close to edge...
|
||||||
|
|| (nl < w && na > nl) || (pl < w && pa > pl)){
|
||||||
|
//console.log('RESIZE (sync)')
|
||||||
|
this.resizeRibbon(target, size)
|
||||||
|
|
||||||
|
// more complex cases...
|
||||||
|
// passed threshold on the right...
|
||||||
|
} else if((nl < update_threshold && na > nl)
|
||||||
|
// passed threshold on the left...
|
||||||
|
|| (pl < update_threshold && pa > pl)
|
||||||
|
// loaded more than we need by threshold...
|
||||||
|
|| nl + pl + 1 > size + update_threshold){
|
||||||
|
// resize...
|
||||||
|
if(this.config['ribbons-in-place-update-mode'] == 'resize'
|
||||||
|
// no ribbon loaded...
|
||||||
|
|| r.length == 0
|
||||||
|
// only if we are going slow...
|
||||||
|
|| (timeout != null
|
||||||
|
&& (t - this.__last_ribbon_update > timeout))
|
||||||
|
// full screen...
|
||||||
|
|| (this.toggleSingleImage
|
||||||
|
&& this.toggleSingleImage('?') == 'on')){
|
||||||
|
return function(){
|
||||||
|
var that = this
|
||||||
|
// sync update...
|
||||||
|
if(update_timeout == null){
|
||||||
|
//console.log('RESIZE (post)', t-this.__last_ribbon_update)
|
||||||
|
this.resizeRibbon(target, size)
|
||||||
|
|
||||||
|
// async update...
|
||||||
|
} else {
|
||||||
|
this.__update_timeout
|
||||||
|
&& clearTimeout(this.__update_timeout)
|
||||||
|
this.__update_timeout = setTimeout(function(){
|
||||||
|
//console.log('RESIZE (timeout)', t-this.__last_ribbon_update)
|
||||||
|
delete that.__update_timeout
|
||||||
|
that.resizeRibbon(target, size)
|
||||||
|
}, update_timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in-place update...
|
||||||
|
// XXX this is faster than .resizeRibbon(..) but it's not
|
||||||
|
// used unconditionally because I can't get rid of
|
||||||
|
// sync up images being replaced...
|
||||||
|
// ...note that .resizeRibbon(..) is substantially
|
||||||
|
// slower (updates DOM), i.e. introduces a lag, but
|
||||||
|
// the results look OK...
|
||||||
|
// XXX approaches to try:
|
||||||
|
// - wait for images to preload and only then update...
|
||||||
|
// - preload images in part of a ribbon and when ready update...
|
||||||
|
// ...this is like the first but we wait for less images...
|
||||||
|
} else {
|
||||||
|
//console.log('UPDATE', t - this.__last_ribbon_update)
|
||||||
|
var c = gids.indexOf(data.getImage('current', r_gid))
|
||||||
|
var t = gids.indexOf(target)
|
||||||
|
|
||||||
|
ribbons
|
||||||
|
.preventTransitions(r)
|
||||||
|
.updateRibbonInPlace(gids, r_gid, target)
|
||||||
|
.restoreTransitions(r, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__last_ribbon_update = t
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
|
||||||
|
var PartialRibbons =
|
||||||
|
module.PartialRibbons = core.ImageGridFeatures.Feature({
|
||||||
|
title: '',
|
||||||
|
doc: '',
|
||||||
|
|
||||||
|
priority: 'high',
|
||||||
|
|
||||||
|
tag: 'ui-partial-ribbons-vdom',
|
||||||
|
exclusive: ['ui-partial-ribbons'],
|
||||||
|
depends: [
|
||||||
|
'ui',
|
||||||
|
],
|
||||||
|
suggested: [
|
||||||
|
'ui-partial-ribbons-precache',
|
||||||
|
],
|
||||||
|
|
||||||
|
actions: PartialRibbonsActions,
|
||||||
|
|
||||||
|
handlers: [
|
||||||
|
['focusImage.pre centerImage.pre',
|
||||||
|
function(target, list){
|
||||||
|
// NOTE: we have to do this as we are called BEFORE the
|
||||||
|
// actual focus change happens...
|
||||||
|
// XXX is there a better way to do this???
|
||||||
|
target = list != null ? target = this.data.getImage(target, list) : target
|
||||||
|
|
||||||
|
this.updateRibbon(target)
|
||||||
|
}],
|
||||||
|
['resizing.post',
|
||||||
|
function(_, unit, size){
|
||||||
|
// keep constant size in single image...
|
||||||
|
if(this.toggleSingleImage && this.toggleSingleImage('?') == 'on'){
|
||||||
|
this.updateRibbon(
|
||||||
|
'current',
|
||||||
|
this.config['ribbons-resize-single-image'] || 13)
|
||||||
|
|
||||||
|
} else if(unit == 'scale'){
|
||||||
|
this.updateRibbon('current', this.screenwidth / size || 1)
|
||||||
|
|
||||||
|
} else if(unit == 'screenwidth'){
|
||||||
|
this.updateRibbon('current', size || 1)
|
||||||
|
|
||||||
|
} else if(unit == 'screenheight'){
|
||||||
|
size = size || 1
|
||||||
|
|
||||||
|
// convert target height in ribbons to width in images...
|
||||||
|
// NOTE: this does not account for compensation that
|
||||||
|
// .updateRibbon(..) makes for fitting whole image
|
||||||
|
// counts, this is a small enough error so as not
|
||||||
|
// to waste time on...
|
||||||
|
var s = this.ribbons.scale()
|
||||||
|
var h = this.ribbons.getScreenHeightRibbons()
|
||||||
|
var w = this.ribbons.getScreenWidthImages()
|
||||||
|
var nw = w / (h/size)
|
||||||
|
|
||||||
|
this.updateRibbon('current', nw)
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
@ -54,6 +54,8 @@ if(window.require && window.nw){
|
|||||||
<!-- velocity.js -->
|
<!-- velocity.js -->
|
||||||
<script src="ext-lib/velocity.min.js"></script>
|
<script src="ext-lib/velocity.min.js"></script>
|
||||||
|
|
||||||
|
<script src="ext-lib/virtual-dom.js"></script>
|
||||||
|
|
||||||
<!-- hammer.js -->
|
<!-- hammer.js -->
|
||||||
<script src="ext-lib/hammer.min.js"></script>
|
<script src="ext-lib/hammer.min.js"></script>
|
||||||
<script src="ext-lib/jquery.hammer.js"></script>
|
<script src="ext-lib/jquery.hammer.js"></script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user