mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 11:20: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 : -->
 |