Skip to content

Instantly share code, notes, and snippets.

@firejune
Created September 12, 2011 23:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save firejune/1212753 to your computer and use it in GitHub Desktop.
Save firejune/1212753 to your computer and use it in GitHub Desktop.
The minimap provides you with a new way to visualize your site.
/** pQuery! LOL **/
Object.extend($, Prototype);
Object.extend($, Object);
/**
* Returns window dimensions and scroll positions
* @author Firejune<to@firejune.com>
* @license MIT
*/
Object.extend(Position, {
getPageSize: function() {
var doc = window.scrollMaxX ?
{w: window.innerWidth + window.scrollMaxX, h: window.innerHeight + window.scrollMaxY} :
{w: document.body.scrollWidth, h: document.body.scrollHeight};
var win = self.innerHeight ?
{w: self.innerWidth, h: self.innerHeight} : // IE
document.documentElement && document.documentElement.clientHeight ?
{w: document.documentElement.clientWidth, h: document.documentElement.clientHeight} : {w: 0, h: 0};
// for small pages with total size less then size of the viewport
doc = {w: Math.max(win.w, doc.w), h: Math.max(win.h, doc.h)};
return { page: { width: doc.w, height: doc.h }, window: { width: win.w, height: win.h } };
},
scrollX: function() {
return (window.scrollX || window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
},
scrollY: function() {
return (window.scrollY || window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
}
});
/**
* Position based scrollTo effect
* @author Firejune<to@firejune.com>
* @license MIT
*/
Effect.Scroll = Class.create(Effect.Base, {
initialize: function(top, left) {
var args = $A(arguments)
, options = typeof args.last() == "object" ? args.last() : {};
this.scroll = {
left: left || 0
, top: top
};
this.start(options);
},
setup: function() {
this.scrollX = Position.scrollX();
this.scrollY = Position.scrollY();
var getSize = Position.getPageSize();
var max = getSize.page.height - getSize.window.height;
if (this.options.offset) this.scrollY += this.options.offset;
if (this.options.offset) this.scrollY += this.options.offset;
this.delta = {
x: (this.scroll.left > max ? max : this.scroll.left) - this.scrollX
, y: (this.scroll.top > max ? max : this.scroll.top) - this.scrollY
}
},
update: function(position) {
scrollTo(
(this.scrollX + position * this.delta.x).round()
, (this.scrollY + position * this.delta.y).round());
}
});
/*!
* minimap 0.1 (http://firejune.com/1704)
* based on fracs (http://larsjung.de/fracs)
*
* provided under the terms of the MIT License
*
* requires:
* - prototype.js prototype 1.6.0+ (http://www.prototypejs.org)
* - scriptaculous 1.8.0+ (http://script.aculo.us)
* - extensions.js (http://firejune.com)
*/
var Minimap = (function(doc, win) {
/*
* Options.
*/
var $options = {
crop: false
, invert: false
, width: 100
, height: 400
, duration: 0.2
, focusWidth: 0.5
, focusHeight: 0.5
, transition: Effect.Transitions.flicker
, styles: [
{selector: 'header,footer,section,article', fillStyle: 'rgb(230,230,230)'}
, {selector: 'h1', fillStyle: 'rgb(240,140,060)'}
, {selector: 'h2', fillStyle: 'rgb(200,100,100)'}
, {selector: 'h3', fillStyle: 'rgb(100,200,100)'}
, {selector: 'h4', fillStyle: 'rgb(100,100,200)'}
]
, viewport: {fillStyle: 'rgba(228,77,38,0.3)'}
, drag: {fillStyle: 'rgba(228,77,38,0.6)'}
}
, $canvas
, $context
, $drag;
/*
* Class.
*/
return function(options) {
$options = $.extend($options, options);
$canvas = new Element('canvas', {
id: 'outline'
, width: $options.width
, height: $options.height
});
if (!($canvas instanceof HTMLElement)
|| $canvas.nodeName.toLowerCase() !== 'canvas')
return console.error('Your browser does not support Canvas');
doc.body.insert($canvas);
this.draw = draw;
this.scroll = scroll;
$context = $canvas.getContext('2d');
$canvas.setStyle('cursor: pointer').observe('mousedown', function(event) {
event.stop();
$drag = true;
$canvas.setStyle('cursor: crosshair');
doc.body.setStyle('cursor: crosshair');
Event.observe(win, 'mousemove', scroll);
});
Event.observe(win, 'mouseup', function(event) {
event.stop();
$canvas.setStyle('cursor: pointer');
doc.body.setStyle('cursor: auto');
Event.stopObserving(win, 'mousemove', scroll);
$drag = false;
draw();
event.element() == $canvas && scroll(event);
});
$canvas.onselectstart = function() {
return false;
};
Event.observe(win, 'load', draw);
Event.observe(win, 'resize', draw);
Event.observe(win, 'scroll', draw);
draw();
console.info('Initialized minimap.');
};
/*
* Core.
*/
function scroll(event) {
var r = rect($canvas)
, x = event.clientX - r.left
, y = event.clientY - r.top;
scrollTo(
x / draw.scale - draw.win.width * $options.focusWidth
, y / draw.scale - draw.win.height * $options.focusHeight
, event.type == 'mousemove' ? 0 : $options.duration);
}
function scrollTo(left, top, duration) {
scroll.ani && scroll.ani.state == 'running' && scroll.ani.cancel();
scroll.ani = new Effect.Scroll(top, left, {
fps: 100
, duration: duration
, transition: $options.transition
});
}
function draw() {
var vp = viewport()
, scaleX = $options.width / vp.doc.width
, scaleY = $options.height / vp.doc.height;
if ($options.crop) {
$canvas.width = vp.doc.width * draw.scale;
$canvas.height = vp.doc.height * draw.scale;
}
$context.clearRect(0, 0, $canvas.width, $canvas.height);
$context.scale(draw.scale, draw.scale);
$.extend(draw, {
scale: scaleX < scaleY ? scaleX : scaleY
, doc: vp.doc
, win: vp.win
});
applyStyles($context);
drawViewport();
$context.scale(1 / draw.scale, 1 / draw.scale);
}
function applyStyles(context) {
for (idx in $options.styles) {
var style = $.extend({
strokeWidth: 'auto'
, strokeStyle: 'auto'
, fillStyle: 'auto'
}, $options.styles[idx]);
$$(style.selector).each(function(el) {
el.visible() && drawElement(
context
, el
, style.strokeWidth
, style.strokeStyle
, style.fillStyle);
});
}
}
function drawElement(context, element, strokeWidth, strokeStyle, fillStyle) {
var position = rect(element);
strokeWidth = strokeWidth == 'auto' ?
parseInt(element.getStyle('border-width')) || '' : strokeWidth;
strokeStyle = strokeStyle == 'auto' ?
element.getStyle('border-color') : strokeStyle;
fillStyle = fillStyle == 'auto' ?
element.getStyle('background-color') : fillStyle;
drawRect(context, position, strokeWidth, strokeStyle, fillStyle);
}
function drawViewport() {
if ($drag === true && $options.drag !== undefined) {
var storkeWidth = $options.drag.storkeWidth;
var strokeStyle = $options.drag.strokeStyle;
var fillStyle = $options.drag.fillStyle;
} else {
var storkeWidth = $options.viewport.storkeWidth;
var strokeStyle = $options.viewport.strokeStyle;
var fillStyle = $options.viewport.fillStyle;
}
drawRect(
$context
, draw.win
, storkeWidth
, strokeStyle
, fillStyle
, $options.invert);
}
function drawRect(context, rect, lineWidth, strokeStyle, fillStyle, invert) {
invert = invert || false;
lineWidth = lineWidth > 0.2 / draw.scale ? lineWidth : 0.2 / draw.scale;
if (invert === false) {
context.beginPath();
context.rect(rect.left, rect.top, rect.width, rect.height);
context.fillStyle = fillStyle;
context.fill();
context.lineWidth = lineWidth;
context.strokeStyle = strokeStyle;
context.stroke();
} else {
context.beginPath();
context.rect(0, 0, draw.doc.width, rect.top);
context.rect(0, rect.top, rect.left, rect.height);
context.rect(rect.right, rect.top, draw.doc.right - rect.right, rect.height);
context.rect(0, rect.bottom, draw.doc.width, draw.doc.bottom - rect.bottom);
context.fillStyle = fillStyle;
context.fill();
context.beginPath();
context.rect(rect.left, rect.top, rect.width, rect.height);
context.lineWidth = lineWidth;
context.strokeStyle = strokeStyle;
context.stroke();
}
}
function viewport() {
var size = Position.getPageSize()
, left = Position.scrollX()
, top = Position.scrollY();
return {
win: new Rect(left, top, size.window.width, size.window.height)
, doc: new Rect(0, 0, size.page.width, size.page.height)
};
}
function rect(element) {
var dimensions = element.getDimensions()
, offset = element.cumulativeOffset();
return new Rect(offset.left, offset.top, dimensions.width, dimensions.height);
}
function Rect(left, top, width, height) {
this.left = left.round();
this.top = top.round();
this.width = width.round();
this.height = height.round();
this.right = this.left + this.width;
this.bottom = this.top + this.height;
}
})(document, window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment