Flat, round, designer-friendly pseudo-3D engine for canvas & SVG
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 250 lines 7.0 kB view raw
1/** 2 * Illustration 3 */ 4 5( function( root, factory ) { 6 // module definition 7 if ( typeof module == 'object' && module.exports ) { 8 // CommonJS 9 module.exports = factory( require('./boilerplate'), require('./anchor'), 10 require('./dragger') ); 11 } else { 12 // browser global 13 var Zdog = root.Zdog; 14 Zdog.Illustration = factory( Zdog, Zdog.Anchor, Zdog.Dragger ); 15 } 16}( this, function factory( utils, Anchor, Dragger ) { 17 18function noop() {} 19var TAU = utils.TAU; 20 21var Illustration = Anchor.subclass({ 22 element: undefined, 23 centered: true, 24 zoom: 1, 25 dragRotate: false, 26 resize: false, 27 onPrerender: noop, 28 onDragStart: noop, 29 onDragMove: noop, 30 onDragEnd: noop, 31 onResize: noop, 32}); 33Illustration.ignoreKeysJSON = [ 'dragRotate', 'element', 'resize' ]; 34Illustration.type = 'Illustration'; 35 36utils.extend( Illustration.prototype, Dragger.prototype ); 37 38Illustration.prototype.create = function( options ) { 39 Anchor.prototype.create.call( this, options ); 40 Dragger.prototype.create.call( this, options ); 41 this.setElement( this.element ); 42 this.setDragRotate( this.dragRotate ); 43 this.setResize( this.resize ); 44}; 45 46Illustration.prototype.setElement = function( element ) { 47 element = this.getQueryElement( element ); 48 if ( !element ) { 49 throw new Error( 'Zdog.Illustration element required. Set to ' + element ); 50 } 51 52 var nodeName = element.nodeName.toLowerCase(); 53 if ( nodeName == 'canvas' ) { 54 this.setCanvas( element ); 55 } else if ( nodeName == 'svg' ) { 56 this.setSvg( element ); 57 } 58}; 59 60Illustration.prototype.setSize = function( width, height ) { 61 width = Math.round( width ); 62 height = Math.round( height ); 63 if ( this.isCanvas ) { 64 this.setSizeCanvas( width, height ); 65 } else if ( this.isSvg ) { 66 this.setSizeSvg( width, height ); 67 } 68}; 69 70Illustration.prototype.setResize = function( resize ) { 71 this.resize = resize; 72 // create resize event listener 73 if ( !this.resizeListener ) { 74 this.resizeListener = this.onWindowResize.bind( this ); 75 } 76 // add/remove event listener 77 if ( resize ) { 78 window.addEventListener( 'resize', this.resizeListener ); 79 this.onWindowResize(); 80 } else { 81 window.removeEventListener( 'resize', this.resizeListener ); 82 } 83}; 84 85// TODO debounce this? 86Illustration.prototype.onWindowResize = function() { 87 this.setMeasuredSize(); 88 this.onResize( this.width, this.height ); 89}; 90 91Illustration.prototype.setMeasuredSize = function() { 92 var width, height; 93 var isFullscreen = this.resize == 'fullscreen'; 94 if ( isFullscreen ) { 95 width = window.innerWidth; 96 height = window.innerHeight; 97 } else { 98 var rect = this.element.getBoundingClientRect(); 99 width = rect.width; 100 height = rect.height; 101 } 102 this.setSize( width, height ); 103}; 104 105// ----- render ----- // 106 107Illustration.prototype.renderGraph = function( item ) { 108 if ( this.isCanvas ) { 109 this.renderGraphCanvas( item ); 110 } else if ( this.isSvg ) { 111 this.renderGraphSvg( item ); 112 } 113}; 114 115// combo method 116Illustration.prototype.updateRenderGraph = function( item ) { 117 this.updateGraph(); 118 this.renderGraph( item ); 119}; 120 121// ----- canvas ----- // 122 123Illustration.prototype.setCanvas = function( element ) { 124 this.element = element; 125 this.isCanvas = true; 126 // update related properties 127 this.ctx = this.element.getContext('2d'); 128 // set initial size 129 this.setSizeCanvas( element.width, element.height ); 130}; 131 132Illustration.prototype.setSizeCanvas = function( width, height ) { 133 this.width = width; 134 this.height = height; 135 // up-rez for hi-DPI devices 136 var pixelRatio = this.pixelRatio = window.devicePixelRatio || 1; 137 this.element.width = this.canvasWidth = width * pixelRatio; 138 this.element.height = this.canvasHeight = height * pixelRatio; 139 var needsHighPixelRatioSizing = pixelRatio > 1 && !this.resize; 140 if ( needsHighPixelRatioSizing ) { 141 this.element.style.width = width + 'px'; 142 this.element.style.height = height + 'px'; 143 } 144}; 145 146Illustration.prototype.renderGraphCanvas = function( item ) { 147 item = item || this; 148 this.prerenderCanvas(); 149 Anchor.prototype.renderGraphCanvas.call( item, this.ctx ); 150 this.postrenderCanvas(); 151}; 152 153Illustration.prototype.prerenderCanvas = function() { 154 var ctx = this.ctx; 155 ctx.lineCap = 'round'; 156 ctx.lineJoin = 'round'; 157 ctx.clearRect( 0, 0, this.canvasWidth, this.canvasHeight ); 158 ctx.save(); 159 if ( this.centered ) { 160 var centerX = this.width / 2 * this.pixelRatio; 161 var centerY = this.height / 2 * this.pixelRatio; 162 ctx.translate( centerX, centerY ); 163 } 164 var scale = this.pixelRatio * this.zoom; 165 ctx.scale( scale, scale ); 166 this.onPrerender( ctx ); 167}; 168 169Illustration.prototype.postrenderCanvas = function() { 170 this.ctx.restore(); 171}; 172 173// ----- svg ----- // 174 175Illustration.prototype.setSvg = function( element ) { 176 this.element = element; 177 this.isSvg = true; 178 this.pixelRatio = 1; 179 // set initial size from width & height attributes 180 var width = element.getAttribute('width'); 181 var height = element.getAttribute('height'); 182 this.setSizeSvg( width, height ); 183}; 184 185Illustration.prototype.setSizeSvg = function( width, height ) { 186 this.width = width; 187 this.height = height; 188 var viewWidth = width / this.zoom; 189 var viewHeight = height / this.zoom; 190 var viewX = this.centered ? -viewWidth/2 : 0; 191 var viewY = this.centered ? -viewHeight/2 : 0; 192 this.element.setAttribute( 'viewBox', viewX + ' ' + viewY + ' ' + 193 viewWidth + ' ' + viewHeight ); 194 if ( this.resize ) { 195 // remove size attributes, let size be determined by viewbox 196 this.element.removeAttribute('width'); 197 this.element.removeAttribute('height'); 198 } else { 199 this.element.setAttribute( 'width', width ); 200 this.element.setAttribute( 'height', height ); 201 } 202}; 203 204Illustration.prototype.renderGraphSvg = function( item ) { 205 item = item || this; 206 empty( this.element ); 207 this.onPrerender( this.element ); 208 Anchor.prototype.renderGraphSvg.call( item, this.element ); 209}; 210 211function empty( element ) { 212 while ( element.firstChild ) { 213 element.removeChild( element.firstChild ); 214 } 215} 216 217// ----- drag ----- // 218 219Illustration.prototype.setDragRotate = function( item ) { 220 if ( !item ) { 221 return; 222 } else if ( item === true ) { 223 /* eslint consistent-this: "off" */ 224 item = this; 225 } 226 this.dragRotate = item; 227 228 this.bindDrag( this.element ); 229}; 230 231Illustration.prototype.dragStart = function( /* event, pointer */) { 232 this.dragStartRX = this.dragRotate.rotate.x; 233 this.dragStartRY = this.dragRotate.rotate.y; 234 Dragger.prototype.dragStart.apply( this, arguments ); 235}; 236 237Illustration.prototype.dragMove = function( event, pointer ) { 238 var moveX = pointer.pageX - this.dragStartX; 239 var moveY = pointer.pageY - this.dragStartY; 240 var displaySize = Math.min( this.width, this.height ); 241 var moveRY = moveX/displaySize * TAU; 242 var moveRX = moveY/displaySize * TAU; 243 this.dragRotate.rotate.x = this.dragStartRX - moveRX; 244 this.dragRotate.rotate.y = this.dragStartRY - moveRY; 245 Dragger.prototype.dragMove.apply( this, arguments ); 246}; 247 248return Illustration; 249 250} ) );