UNPKG

zdog

Version:

Round, flat, designer-friendly pseudo-3D engine

249 lines (216 loc) 6.91 kB
/** * Illustration */ ( function( root, factory ) { // module definition if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('./boilerplate'), require('./anchor'), require('./dragger') ); } else { // browser global var Zdog = root.Zdog; Zdog.Illustration = factory( Zdog, Zdog.Anchor, Zdog.Dragger ); } }( this, function factory( utils, Anchor, Dragger ) { function noop() {} var TAU = utils.TAU; var Illustration = Anchor.subclass({ element: undefined, centered: true, zoom: 1, dragRotate: false, resize: false, onPrerender: noop, onDragStart: noop, onDragMove: noop, onDragEnd: noop, onResize: noop, }); utils.extend( Illustration.prototype, Dragger.prototype ); Illustration.prototype.create = function( options ) { Anchor.prototype.create.call( this, options ); Dragger.prototype.create.call( this, options ); this.setElement( this.element ); this.setDragRotate( this.dragRotate ); this.setResize( this.resize ); }; Illustration.prototype.setElement = function( element ) { element = this.getQueryElement( element ); if ( !element ) { throw new Error( 'Zdog.Illustration element required. Set to ' + element ); } var nodeName = element.nodeName.toLowerCase(); if ( nodeName == 'canvas' ) { this.setCanvas( element ); } else if ( nodeName == 'svg' ) { this.setSvg( element ); } }; Illustration.prototype.setSize = function( width, height ) { width = Math.round( width ); height = Math.round( height ); if ( this.isCanvas ) { this.setSizeCanvas( width, height ); } else if ( this.isSvg ) { this.setSizeSvg( width, height ); } }; Illustration.prototype.setResize = function( resize ) { this.resize = resize; // create resize event listener if ( !this.resizeListener ) { this.resizeListener = this.onWindowResize.bind( this ); } // add/remove event listener if ( resize ) { window.addEventListener( 'resize', this.resizeListener ); this.onWindowResize(); } else { window.removeEventListener( 'resize', this.resizeListener ); } }; // TODO debounce this? Illustration.prototype.onWindowResize = function() { this.setMeasuredSize(); this.onResize( this.width, this.height ); }; Illustration.prototype.setMeasuredSize = function() { var width, height; var isFullscreen = this.resize == 'fullscreen'; if ( isFullscreen ) { width = window.innerWidth; height = window.innerHeight; } else { var rect = this.element.getBoundingClientRect(); width = rect.width; height = rect.height; } this.setSize( width, height ); }; // ----- render ----- // Illustration.prototype.renderGraph = function( item ) { if ( this.isCanvas ) { this.renderGraphCanvas( item ); } else if ( this.isSvg ) { this.renderGraphSvg( item ); } }; // combo method Illustration.prototype.updateRenderGraph = function( item ) { this.updateGraph(); this.renderGraph( item ); }; // ----- canvas ----- // Illustration.prototype.setCanvas = function( element ) { this.element = element; this.isCanvas = true; // update related properties this.ctx = this.element.getContext('2d'); // set initial size this.setSizeCanvas( element.width, element.height ); }; Illustration.prototype.setSizeCanvas = function( width, height ) { this.width = width; this.height = height; // up-rez for hi-DPI devices var pixelRatio = this.pixelRatio = window.devicePixelRatio || 1; this.element.width = this.canvasWidth = width * pixelRatio; this.element.height = this.canvasHeight = height * pixelRatio; var needsHighPixelRatioSizing = pixelRatio > 1 && !this.resize; if ( needsHighPixelRatioSizing ) { this.element.style.width = width + 'px'; this.element.style.height = height + 'px'; } }; Illustration.prototype.renderGraphCanvas = function( item ) { item = item || this; this.prerenderCanvas(); Anchor.prototype.renderGraphCanvas.call( item, this.ctx ); this.postrenderCanvas(); }; Illustration.prototype.prerenderCanvas = function() { var ctx = this.ctx; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.clearRect( 0, 0, this.canvasWidth, this.canvasHeight ); ctx.save(); if ( this.centered ) { var centerX = this.width / 2 * this.pixelRatio; var centerY = this.height / 2 * this.pixelRatio; ctx.translate( centerX, centerY ); } var scale = this.pixelRatio * this.zoom; ctx.scale( scale, scale ); this.onPrerender( ctx ); }; Illustration.prototype.postrenderCanvas = function() { this.ctx.restore(); }; // ----- svg ----- // Illustration.prototype.setSvg = function( element ) { this.element = element; this.isSvg = true; this.pixelRatio = 1; // set initial size from width & height attributes var width = element.getAttribute('width'); var height = element.getAttribute('height'); this.setSizeSvg( width, height ); }; Illustration.prototype.setSizeSvg = function( width, height ) { this.width = width; this.height = height; var viewWidth = width / this.zoom; var viewHeight = height / this.zoom; var viewX = this.centered ? -viewWidth/2 : 0; var viewY = this.centered ? -viewHeight/2 : 0; this.element.setAttribute( 'viewBox', viewX + ' ' + viewY + ' ' + viewWidth + ' ' + viewHeight ); if ( this.resize ) { // remove size attributes, let size be determined by viewbox this.element.removeAttribute('width'); this.element.removeAttribute('height'); } else { this.element.setAttribute( 'width', width ); this.element.setAttribute( 'height', height ); } }; Illustration.prototype.renderGraphSvg = function( item ) { item = item || this; empty( this.element ); this.onPrerender( this.element ); Anchor.prototype.renderGraphSvg.call( item, this.element ); }; function empty( element ) { while ( element.firstChild ) { element.removeChild( element.firstChild ); } } // ----- drag ----- // Illustration.prototype.setDragRotate = function( item ) { if ( !item ) { return; } else if ( item === true ) { /* eslint consistent-this: "off" */ item = this; } this.dragRotate = item; this.bindDrag( this.element ); }; Illustration.prototype.dragStart = function( /* event, pointer */) { this.dragStartRX = this.dragRotate.rotate.x; this.dragStartRY = this.dragRotate.rotate.y; Dragger.prototype.dragStart.apply( this, arguments ); }; Illustration.prototype.dragMove = function( event, pointer ) { var moveX = pointer.pageX - this.dragStartX; var moveY = pointer.pageY - this.dragStartY; var displaySize = Math.min( this.width, this.height ); var moveRY = moveX/displaySize * TAU; var moveRX = moveY/displaySize * TAU; this.dragRotate.rotate.x = this.dragStartRX - moveRX; this.dragRotate.rotate.y = this.dragStartRY - moveRY; Dragger.prototype.dragMove.apply( this, arguments ); }; return Illustration; } ) );