142 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var Class = require('../../core/class');
 | |
| var Container = require('../../dom/container');
 | |
| var Element = require('../../dom/native');
 | |
| 
 | |
| var fps = 1000 / 60, invalids = [], renderTimer, renderInvalids = function(){
 | |
| 	clearTimeout(renderTimer);
 | |
| 	renderTimer = null;
 | |
| 	var canvases = invalids;
 | |
| 	invalids = [];
 | |
| 	for (var i = 0, l = canvases.length; i < l; i++){
 | |
| 		var c = canvases[i];
 | |
| 		c._valid = true;
 | |
| 		c.render();
 | |
| 	}
 | |
| };
 | |
| 
 | |
| var resolution = typeof window !== 'undefined' && window.devicePixelRatio || 1;
 | |
| 
 | |
| var previousHit = null, previousHitSurface = null;
 | |
| 
 | |
| var CanvasSurface = Class(Element, Container, {
 | |
| 
 | |
| 	initialize: function(width, height, existingElement){
 | |
| 		var element = this.element = existingElement || document.createElement('canvas');
 | |
| 		var context = this.context = element.getContext('2d');
 | |
| 		this._valid = true;
 | |
| 		if (width != null && height != null) this.resize(width, height);
 | |
| 
 | |
| 		element.addEventListener('mousemove', this, false);
 | |
| 		element.addEventListener('mouseout', this, false);
 | |
| 		element.addEventListener('mouseover', this, false);
 | |
| 		element.addEventListener('mouseup', this, false);
 | |
| 		element.addEventListener('mousedown', this, false);
 | |
| 		element.addEventListener('click', this, false);
 | |
| 	},
 | |
| 
 | |
| 	handleEvent: function(event){
 | |
| 		if (event.clientX == null) return;
 | |
| 		var element = this.element,
 | |
| 			rect = element.getBoundingClientRect(),
 | |
| 			x = event.clientX - rect.left - element.clientLeft,
 | |
| 			y = event.clientY - rect.top - element.clientTop,
 | |
| 			hit = this.hitTest(x, y);
 | |
| 
 | |
| 		if (hit !== previousHit){
 | |
| 			if (previousHit){
 | |
| 				previousHit.dispatch({
 | |
| 					type: 'mouseout',
 | |
| 					target: previousHit,
 | |
| 					relatedTarget: hit,
 | |
| 					sourceEvent: event
 | |
| 				});
 | |
| 			}
 | |
| 			if (hit){
 | |
| 				hit.dispatch({
 | |
| 					type: 'mouseover',
 | |
| 					target: hit,
 | |
| 					relatedTarget: previousHit,
 | |
| 					sourceEvent: event
 | |
| 				});
 | |
| 			}
 | |
| 			previousHit = hit;
 | |
| 			previousHitSurface = this;
 | |
| 			this.refreshCursor();
 | |
| 		}
 | |
| 
 | |
| 		if (hit) hit.dispatch(event);
 | |
| 	},
 | |
| 
 | |
| 	refreshCursor: function(){
 | |
| 		if (previousHitSurface !== this) return;
 | |
| 		var hit = previousHit, hitCursor = '', hitTooltip = '';
 | |
| 		while (hit){
 | |
| 			if (!hitCursor && hit._cursor){
 | |
| 				hitCursor = hit._cursor;
 | |
| 				if (hitTooltip) break;
 | |
| 			}
 | |
| 			if (!hitTooltip && hit._tooltip){
 | |
| 				hitTooltip = hit._tooltip;
 | |
| 				if (hitCursor) break;
 | |
| 			}
 | |
| 			hit = hit.parentNode;
 | |
| 		}
 | |
| 		// TODO: No way to set cursor/title on the surface
 | |
| 		this.element.style.cursor = hitCursor;
 | |
| 		this.element.title = hitTooltip;
 | |
| 	},
 | |
| 
 | |
| 	resize: function(width, height){
 | |
| 		var element = this.element;
 | |
| 		element.setAttribute('width', width * resolution);
 | |
| 		element.setAttribute('height', height * resolution);
 | |
| 		element.style.width = width + 'px';
 | |
| 		element.style.height = height + 'px';
 | |
| 		this.width = width;
 | |
| 		this.height = height;
 | |
| 		return this;
 | |
| 	},
 | |
| 
 | |
| 	invalidate: function(left, top, width, height){
 | |
| 		if (this._valid){
 | |
| 			this._valid = false;
 | |
| 			invalids.push(this);
 | |
| 			if (!renderTimer){
 | |
| 				if (window.mozRequestAnimationFrame){
 | |
| 					renderTimer = true;
 | |
| 					window.mozRequestAnimationFrame(renderInvalids);
 | |
| 				} else {
 | |
| 					renderTimer = setTimeout(renderInvalids, fps);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return this;
 | |
| 	},
 | |
| 
 | |
| 	hitTest: function(x, y){
 | |
| 		if (x < 0 || y < 0 || x > this.width || y > this.height) return null;
 | |
| 		var node = this.lastChild;
 | |
| 		while (node){
 | |
| 			var hit = node.hitTest(x, y);
 | |
| 			if (hit) return hit;
 | |
| 			node = node.previousSibling;
 | |
| 		}
 | |
| 		return null;
 | |
| 	},
 | |
| 
 | |
| 	render: function(){
 | |
| 		var node = this.firstChild, context = this.context;
 | |
| 		context.setTransform(resolution, 0, 0, resolution, 0, 0);
 | |
| 		context.clearRect(0, 0, this.width, this.height);
 | |
| 		while (node){
 | |
| 			node.renderTo(context, resolution, 0, 0, resolution, 0, 0);
 | |
| 			node = node.nextSibling;
 | |
| 		}
 | |
| 		this.refreshCursor();
 | |
| 	}
 | |
| 
 | |
| });
 | |
| 
 | |
| CanvasSurface.tagName = 'canvas';
 | |
| 
 | |
| module.exports = CanvasSurface; | 
