mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-28 09:50:09 +00:00
450 lines
9.3 KiB
HTML
Executable File
450 lines
9.3 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html>
|
|
<!--
|
|
//---------------------------------------------------------------------
|
|
//
|
|
|
|
-->
|
|
|
|
<style>
|
|
.mark-center:after {
|
|
position: absolute;
|
|
display: block;
|
|
content: "";
|
|
width: 5px;
|
|
height: 5px;
|
|
left: 50%;
|
|
top: 50%;
|
|
border-left: solid 2px red;
|
|
border-top: solid 2px red;
|
|
margin-left: -1px;
|
|
margin-top: -1px;
|
|
opacity: 0.8;
|
|
z-index: 1;
|
|
}
|
|
.mark-center:before {
|
|
position: absolute;
|
|
display: block;
|
|
content: "";
|
|
width: 5px;
|
|
height: 5px;
|
|
right: 50%;
|
|
bottom: 50%;
|
|
border-bottom: solid 2px red;
|
|
border-right: solid 2px red;
|
|
margin-bottom: -1px;
|
|
margin-right: -1px;
|
|
opacity: 0.8;
|
|
z-index: 1;
|
|
}
|
|
|
|
|
|
/* XXX appears that there is no way to hide the scrollbar on FF...
|
|
* ...one way around this is to use something like iScroll/Scrolly
|
|
* on FF or where more control is needed...
|
|
*/
|
|
.viewer {
|
|
position: relative;
|
|
display: inline-block;
|
|
border: solid 1px gray;
|
|
|
|
width: 600px;
|
|
height: 500px;
|
|
|
|
overflow: hidden;
|
|
}
|
|
|
|
|
|
|
|
.scaler {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
top: 50%;
|
|
left: 50%;
|
|
margin-top: -50%;
|
|
margin-left: -50%;
|
|
|
|
transform-origin: 50% 50%;
|
|
|
|
overflow-x: hidden;
|
|
overflow-y: scroll;
|
|
|
|
-ms-overflow-style: none;
|
|
}
|
|
.scaler::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
|
|
|
|
/* This is to be used for:
|
|
* - vrtical positioning
|
|
* - scaling
|
|
* (update width to fit viewer)
|
|
*/
|
|
.ribbon-set {
|
|
position: relative;
|
|
display: inline-block;
|
|
|
|
/* This allways needs to be of viewer width, this mostly applies
|
|
* to scaling...
|
|
*/
|
|
width: 100%;
|
|
|
|
padding-top: 50%;
|
|
padding-bottom: 50%;
|
|
}
|
|
|
|
|
|
|
|
.ribbon-container {
|
|
position: relative;
|
|
display: block;
|
|
|
|
height: 120px;
|
|
width: 100%;
|
|
|
|
overflow-x: scroll;
|
|
overflow-y: hidden;
|
|
|
|
-ms-overflow-style: none;
|
|
}
|
|
.ribbon-container::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
.ribbon-container:before {
|
|
position: absolute;
|
|
content: attr(index);
|
|
}
|
|
|
|
|
|
.ribbon {
|
|
position: relative;
|
|
display: inline-block;
|
|
|
|
height: 100px;
|
|
width: auto;
|
|
|
|
white-space: nowrap;
|
|
overflow: visible;
|
|
|
|
background: silver;
|
|
/*box-shadow: 0px 0px 25px -10px rgba(0,0,0,0.75);*/
|
|
box-shadow: 0px 0px 25px -10px rgba(0,0,0,1);
|
|
|
|
/* start/end markers... */
|
|
/*border-left: 100px solid gray;
|
|
border-right: 100px solid gray;*/
|
|
|
|
margin: 10px;
|
|
|
|
margin-left: 50%;
|
|
/* XXX for some reason this does not work as expected */
|
|
margin-right: 50%;
|
|
}
|
|
|
|
|
|
.image {
|
|
position: relative;
|
|
display: inline-block;
|
|
|
|
width: 100px;
|
|
height: 100px;
|
|
|
|
outline: solid blue 1px;
|
|
|
|
background: silver;
|
|
}
|
|
.image:after {
|
|
content: attr(index);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
|
|
|
|
</style>
|
|
|
|
<script src="../ext-lib/jquery.js"></script>
|
|
<script src="../ext-lib/jquery-ui.js"></script>
|
|
|
|
<script src="../ext-lib/velocity.min.js"></script>
|
|
|
|
<script src="../lib/jli.js"></script>
|
|
|
|
<script>
|
|
|
|
var scale = function(){
|
|
var s = /scale\(([^\)]+)\)/.exec($('.scaler')[0].style.transform)
|
|
return s ? parseFloat(s.pop()) : 1
|
|
}
|
|
|
|
|
|
// XXX when setting origin at scales different from 1, we'll need to
|
|
// adjust offset to compensate for the shift...
|
|
// XXX one other simplification might be adding a new element specifically
|
|
// dedicated to scaling...
|
|
var centerOrigin = function(){
|
|
var H = $('.viewer').height()
|
|
var s = $('.viewer')[0].scrollTop
|
|
|
|
$('.ribbon-set').css({
|
|
'transform-origin': '50% '+ (s + H/2) +'px'
|
|
})
|
|
}
|
|
|
|
|
|
// XXX these accumolate errors...
|
|
var zoomIn = function(c){
|
|
c = c || 1.2
|
|
|
|
centerOrigin()
|
|
$('.scaler')
|
|
.velocity('stop')
|
|
.velocity({
|
|
scale: '*='+c,
|
|
|
|
width: '/='+c,
|
|
height: '/='+c,
|
|
'margin-left': '/='+c,
|
|
'margin-top': '/='+c,
|
|
}, {
|
|
duration: 300,
|
|
easing: 'linear',
|
|
})
|
|
}
|
|
var zoomOut = function(c){
|
|
c = c || 1.2
|
|
|
|
centerOrigin()
|
|
$('.scaler')
|
|
.velocity('stop')
|
|
.velocity({
|
|
scale: '/='+c,
|
|
|
|
width: '*='+c,
|
|
height: '*='+c,
|
|
'margin-left': '*='+c,
|
|
'margin-top': '*='+c,
|
|
}, {
|
|
duration: 300,
|
|
easing: 'linear',
|
|
})
|
|
}
|
|
|
|
|
|
// items - list of items, each item must be make(..) compatible
|
|
// ...this can also be a function and return multiple
|
|
// items (XXX)
|
|
// make - item DOM constructor
|
|
//
|
|
// Options:
|
|
// container - element that actually contains the items (default: 'this')
|
|
// direction - scroll direction (default: 'vertical')
|
|
// threshold -
|
|
//
|
|
// XXX horizontal scroll is still buggy -- mostly in thresholds...
|
|
var makeScrollHandler = function(items, make, options){
|
|
options = options || {}
|
|
|
|
var direction = options.direction || 'vertical'
|
|
//var threshold = options.threshold ||
|
|
var _container = options.container || 'this'
|
|
|
|
// XXX should we do an initial load here???
|
|
|
|
return function(evt){
|
|
var container = _container == 'this' ?
|
|
this
|
|
: typeof(_container) == typeof('str') ?
|
|
this.querySelector(_container)
|
|
: _container
|
|
|
|
if(direction == 'vertical'){
|
|
var size = this.scrollHeight
|
|
var offset = this.scrollTop
|
|
var visible_size = this.offsetHeight
|
|
|
|
var elem_scroll_attr = 'scrollTop'
|
|
var elem_offset_attr = 'offsetTop'
|
|
var elem_size_attr = 'offsetHeight'
|
|
|
|
} else {
|
|
var size = this.scrollWidth
|
|
var offset = this.scrollLeft
|
|
var visible_size = this.offsetWidth
|
|
|
|
var elem_scroll_attr = 'scrollLeft'
|
|
var elem_offset_attr = 'offsetLeft'
|
|
var elem_size_attr = 'offsetWidth'
|
|
}
|
|
|
|
// XXX
|
|
var threshold = visible_size
|
|
|
|
var dom_items = container.children
|
|
|
|
// head limit -- add items to the head...
|
|
if(offset < threshold){
|
|
var i = parseInt(dom_items[0].getAttribute('index')) - 1
|
|
var e = items instanceof Function ?
|
|
items(i)
|
|
// XXX make this support multiple items...
|
|
: items[i]
|
|
|
|
// make the item(s)...
|
|
if(e){
|
|
// XXX need to account for situations where the whole thing is replaced...
|
|
var c = dom_items[0]
|
|
var pre = c[elem_offset_attr]
|
|
|
|
container.prepend(make(e))
|
|
|
|
// compensate offset for added items...
|
|
var d = c[elem_offset_attr] - pre
|
|
// XXX need to do this only if the browser is not compensating...
|
|
if(direction == 'horizontal'){
|
|
this[elem_scroll_attr] += d
|
|
}
|
|
|
|
// remove hidden items from tail...
|
|
var t = offset + visible_size + threshold
|
|
;[].slice.call(dom_items)
|
|
// XXX add threshold / items-to-keep-offscreen limit ...
|
|
// XXX this is wrong for horizontal scroll...
|
|
.filter(function(e){ return e[elem_offset_attr] > t })
|
|
// XXX can we remove these in one go???
|
|
.forEach(function(e){ e.remove() })
|
|
}
|
|
}
|
|
|
|
// tail limit -- add items to the tail...
|
|
if( size - (offset + visible_size) < threshold ){
|
|
var i = parseInt(dom_items[dom_items.length-1].getAttribute('index')) + 1
|
|
var e = items instanceof Function ?
|
|
items(i)
|
|
// XXX make this support multiple items...
|
|
: items[i]
|
|
|
|
if(e){
|
|
container.append(make(e))
|
|
|
|
//var clone = container.cloneNode(true)
|
|
//container.replaceWith(clone)
|
|
|
|
// XXX need to account for situations where the whole thing is replaced...
|
|
var c = dom_items[dom_items.length-1]
|
|
var pre = c[elem_offset_attr]
|
|
|
|
// remove hidden items for head...
|
|
;[].slice.call(dom_items)
|
|
// XXX add threshold / items-to-keep-offscreen limit ...
|
|
.filter(function(e){ return e[elem_offset_attr] + e[elem_size_attr] < offset })
|
|
// XXX can we remove these in one go???
|
|
.forEach(function(e){ e.remove() })
|
|
|
|
// compensate offset for removed items...
|
|
var d = c[elem_offset_attr] - pre
|
|
// XXX need to do this only if the browser is not compensating...
|
|
if(direction == 'horizontal'){
|
|
this[elem_scroll_attr] += d
|
|
}
|
|
|
|
//container.replaceWith(container)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var setup = function(){
|
|
var H = $('.viewer').height()
|
|
var W = $('.viewer').width()
|
|
|
|
var ribbon_set = $('.ribbon-set')[0]
|
|
|
|
|
|
// XXX need to calculate this considering scale...
|
|
var threshold = 300
|
|
var ribbon_count = 10
|
|
var image_count = 10
|
|
|
|
|
|
var ribbon_container = document.createElement('div')
|
|
ribbon_container.classList.add('ribbon-container')
|
|
var ribbon = document.createElement('div')
|
|
ribbon.classList.add('ribbon')
|
|
var image = document.createElement('div')
|
|
image.classList.add('image')
|
|
|
|
var makeImage = function(n){
|
|
var i = image.cloneNode()
|
|
i.setAttribute('index', n)
|
|
return i
|
|
}
|
|
var makeRibbon = function(n){
|
|
var r = ribbon.cloneNode()
|
|
for(var i=0; i < image_count; i++){
|
|
r.appendChild(makeImage(i))
|
|
}
|
|
|
|
var rc = ribbon_container.cloneNode()
|
|
rc.appendChild(r)
|
|
rc.setAttribute('index', n)
|
|
|
|
$(rc).scroll(makeScrollHandler(
|
|
function(n){ return n >= 0 ? n : undefined },
|
|
makeImage,
|
|
{
|
|
container: r,
|
|
direction: 'horizontal',
|
|
threshold: 300,
|
|
}))
|
|
|
|
return rc
|
|
}
|
|
|
|
|
|
var fragment = document.createDocumentFragment()
|
|
for(var i=0; i < ribbon_count; i++){
|
|
fragment.appendChild(makeRibbon(i))
|
|
}
|
|
ribbon_set.appendChild(fragment)
|
|
|
|
|
|
// set margins to be parant and not content dependant...
|
|
$('.scaler')
|
|
.velocity({
|
|
'margin-left': -W/2,
|
|
'margin-top': -H/2,
|
|
}, 0)
|
|
.scroll(makeScrollHandler(
|
|
function(n){ return n >= 0 ? n : undefined },
|
|
makeRibbon,
|
|
{
|
|
container: ribbon_set,
|
|
threshold: 300,
|
|
}))
|
|
}
|
|
|
|
|
|
$(function(){
|
|
setup()
|
|
})
|
|
|
|
</script>
|
|
|
|
<body>
|
|
|
|
<div class="viewer mark-center">
|
|
<div class="scaler">
|
|
<div class="ribbon-set">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|
|
<!-- vim:set sw=4 ts=4 : -->
|