44 | Select me with DevTools 45 |
46 | 47 |
48 |
49 | 7.8/10
50 |
57 | Change me in DevTools 58 |
59 | 60 |
61 |
62 | 8.0/10
63 |
70 | Breaking bad 71 |
72 | 73 |
74 |
75 | 9.6/10
76 |
83 | Quantum of solace 84 |
85 | 86 |
87 |
88 | 6.7/10
89 |
96 | Quantum of solace 97 |
98 | 99 |
100 |
101 | 7.4/10
102 |
109 | Quantum of solace 110 |
111 | 112 |
113 |
114 | 7.4/10
115 |
122 | Quantum of solace 123 |
124 | 125 |
126 |
127 | 7.4/10
128 |
135 | Quantum of solace 136 |
137 | 138 |
139 |
140 | 7.4/10
141 |
148 | Quantum of solace 149 |
150 | 151 |
152 |
153 | 7.4/10
154 |
161 | Quantum of solace 162 |
163 | 164 |
165 |
166 | 7.4/10
167 |
44 | Select me with DevTools 45 |
46 | 47 |
48 |
49 | 7.8/10
50 |
57 | Change me in DevTools 58 |
59 | 60 |
61 |
62 | 8.0/10
63 |
70 | Breaking bad 71 |
72 | 73 |
74 |
75 | 9.6/10
76 |
83 | Quantum of solace 84 |
85 | 86 |
87 |
88 | 6.7/10
89 |
96 | Quantum of solace 97 |
98 | 99 |
100 |
101 | 7.4/10
102 |
109 | Quantum of solace 110 |
111 | 112 |
113 |
114 | 7.4/10
115 |
122 | Quantum of solace 123 |
124 | 125 |
126 |
127 | 7.4/10
128 |
135 | Quantum of solace 136 |
137 | 138 |
139 |
140 | 7.4/10
141 |
148 | Quantum of solace 149 |
150 | 151 |
152 |
153 | 7.4/10
154 |
161 | Quantum of solace 162 |
163 | 164 |
165 |
166 | 7.4/10
167 |
41 | Select me with DevTools 42 |
43 | 44 |
45 |
46 | 7.8/10
47 |
54 | Change me in DevTools 55 |
56 | 57 |
58 |
59 | 8.0/10
60 |
67 | Breaking bad 68 |
69 | 70 |
71 |
72 | 9.6/10
73 |
80 | Quantum of solace 81 |
82 | 83 |
84 |
85 | 6.7/10
86 |
93 | Quantum of solace 94 |
95 | 96 |
97 |
98 | 7.4/10
99 |
106 | Quantum of solace 107 |
108 | 109 |
110 |
111 | 7.4/10
112 |
119 | Quantum of solace 120 |
121 | 122 |
123 |
124 | 7.4/10
125 |
132 | Quantum of solace 133 |
134 | 135 |
136 |
137 | 7.4/10
138 |
145 | Quantum of solace 146 |
147 | 148 |
149 |
150 | 7.4/10
151 |
158 | Quantum of solace 159 |
160 | 161 |
162 |
163 | 7.4/10
164 |
45 | Select me with DevTools 46 |
47 | 48 |
49 |
50 | 7.8/10
51 |
59 | Change me in DevTools 60 |
61 | 62 |
63 |
64 | 8.0/10
65 |
72 | Breaking bad 73 |
74 | 75 |
76 |
77 | 9.6/10
78 |
85 | Quantum of solace 86 |
87 | 88 |
89 |
90 | 6.7/10
91 |
98 | Quantum of solace 99 |
100 | 101 |
102 |
103 | 7.4/10
104 |
111 | Quantum of solace 112 |
113 | 114 |
115 |
116 | 7.4/10
117 |
124 | Quantum of solace 125 |
126 | 127 |
128 |
129 | 7.4/10
130 |
137 | Quantum of solace 138 |
139 | 140 |
141 |
142 | 7.4/10
143 |
150 | Quantum of solace 151 |
152 | 153 |
154 |
155 | 7.4/10
156 |
163 | Quantum of solace 164 |
165 | 166 |
167 |
168 | 7.4/10
169 |
Bitcoin
15 |2checkout
19 |Calendar
23 |Calculator
27 |Clock
31 |Discover
35 |Ebay
39 |Google wallet
43 |Maestro
47 |MasterCard
51 |PayPal
55 |Locker
59 |Visa
63 |Wire transfer
67 |Western Union
71 |innerHTML changed and element was rerendered automaticaly. Sorry, retina is not supported yet, so a bit blurry.
'; 3 | } 4 | 5 | window.changeNodeTransform = function () { 6 | var element = document.getElementsByTagName('html-gl')[0]; 7 | element.style.webkitTransform = 'translateX(400px) translateY(100px)'; 8 | element.style.transform = 'translateX(400px) translateY(100px)'; 9 | } 10 | 11 | window.animateBasic = function () { 12 | var element = document.getElementsByTagName('html-gl')[0]; 13 | 14 | var v = Velocity(element, { 15 | translateX: 100, 16 | translateY: 100, 17 | scaleX: 4, 18 | scaleY: 4, 19 | rotateZ: "360deg" 20 | }, {duration: 1000}).then(function () { 21 | Velocity(element, { 22 | translateX: 0, 23 | translateY: 0, 24 | scaleX: 1, 25 | scaleY: 1, 26 | rotateZ: "0" 27 | }, {duration: 1000}); 28 | }); 29 | } -------------------------------------------------------------------------------- /page/js/effects.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('DOMContentLoaded', function(){ 2 | 3 | var slider = document.getElementsByTagName('html-gl')[1]; 4 | 5 | var renderer = window.HTMLGL.renderer; 6 | 7 | var filtersSwitchs = [true, false, false, false, false, false, false, false, false, false, false]; 8 | 9 | var gui = new dat.GUI({}); 10 | 11 | var displacementSprite = new PIXI.TilingSprite.fromImage("assets/img/displacement_map.jpg"); 12 | window.HTMLGL.stage.addChild(displacementSprite); 13 | var displacementFilter = new PIXI.filters.DisplacementFilter(displacementSprite); 14 | 15 | var displacementFolder = gui.addFolder('Displacement'); 16 | displacementFolder.add(filtersSwitchs, '0').name("apply"); 17 | displacementFolder.add(displacementFilter.scale, 'x', 1, 200).name("scaleX"); 18 | displacementFolder.add(displacementFilter.scale, 'y', 1, 200).name("scaleY"); 19 | displacementFolder.open(); 20 | 21 | var blurFilter = new PIXI.filters.BlurFilter(); 22 | 23 | var blurFolder = gui.addFolder('Blur'); 24 | blurFolder.add(filtersSwitchs, '1').name("apply"); 25 | blurFolder.add(blurFilter, 'blurX', 0, 32).name("blurX"); 26 | blurFolder.add(blurFilter, 'blurY', 0, 32).name("blurY"); 27 | 28 | //// 29 | 30 | var invertFilter = new PIXI.filters.InvertFilter(); 31 | 32 | var invertFolder = gui.addFolder('Invert'); 33 | invertFolder.add(filtersSwitchs, '2').name("apply"); 34 | invertFolder.add(invertFilter, 'invert', 0, 1).name("Invert"); 35 | 36 | //// 37 | 38 | var pixelateFilter = new PIXI.filters.PixelateFilter(); 39 | 40 | var pixelateFolder = gui.addFolder('Pixelate'); 41 | pixelateFolder.add(filtersSwitchs, '3').name("apply"); 42 | pixelateFolder.add(pixelateFilter.size, 'x', 1, 32).name("PixelSizeX"); 43 | pixelateFolder.add(pixelateFilter.size, 'y', 1, 32).name("PixelSizeY"); 44 | 45 | //// 46 | 47 | var grayFilter = new PIXI.filters.GrayFilter(); 48 | 49 | var grayFolder = gui.addFolder('Gray'); 50 | grayFolder.add(filtersSwitchs, '4').name("apply"); 51 | grayFolder.add(grayFilter, 'gray', 0, 1).name("Gray"); 52 | 53 | //// 54 | 55 | var sepiaFilter = new PIXI.filters.SepiaFilter(); 56 | 57 | var sepiaFolder = gui.addFolder('Sepia'); 58 | sepiaFolder.add(filtersSwitchs, '5').name("apply"); 59 | sepiaFolder.add(sepiaFilter, 'sepia', 0, 1).name("Sepia"); 60 | 61 | //// 62 | 63 | var twistFilter = new PIXI.filters.TwistFilter(); 64 | 65 | var twistFolder = gui.addFolder('Twist'); 66 | twistFolder.add(filtersSwitchs, '6').name("apply"); 67 | twistFolder.add(twistFilter, 'angle', 0, 10).name("Angle"); 68 | twistFolder.add(twistFilter, 'radius', 0, 1).name("Radius"); 69 | 70 | twistFolder.add(twistFilter.offset, 'x', 0, 1).name("offset.x");; 71 | twistFolder.add(twistFilter.offset, 'y', 0, 1).name("offset.y");; 72 | 73 | //// 74 | 75 | var dotScreenFilter = new PIXI.filters.DotScreenFilter(); 76 | 77 | var dotScreenFolder = gui.addFolder('DotScreen'); 78 | dotScreenFolder.add(filtersSwitchs, '7').name("apply"); 79 | dotScreenFolder.add(dotScreenFilter, 'angle', 0, 10); 80 | dotScreenFolder.add(dotScreenFilter, 'scale', 0, 1); 81 | 82 | //// 83 | 84 | var colorStepFilter = new PIXI.filters.ColorStepFilter(); 85 | 86 | var colorStepFolder = gui.addFolder('ColorStep'); 87 | colorStepFolder.add(filtersSwitchs, '8').name("apply"); 88 | 89 | colorStepFolder.add(colorStepFilter, 'step', 1, 100); 90 | colorStepFolder.add(colorStepFilter, 'step', 1, 100); 91 | 92 | //// 93 | 94 | var crossHatchFilter = new PIXI.filters.CrossHatchFilter(); 95 | 96 | var crossHatchFolder = gui.addFolder('CrossHatch'); 97 | crossHatchFolder.add(filtersSwitchs, '9').name("apply"); 98 | 99 | var rgbSplitterFilter = new PIXI.filters.RGBSplitFilter(); 100 | 101 | rgbSplitterFilter.blue = new PIXI.Point(10, 10); 102 | rgbSplitterFilter.green = new PIXI.Point(-10, 10); 103 | rgbSplitterFilter.red = new PIXI.Point(10, -10); 104 | 105 | var rgbSplitFolder = gui.addFolder('RGB Splitter'); 106 | rgbSplitFolder.add(filtersSwitchs, '10').name("apply"); 107 | 108 | 109 | var filterCollection = [displacementFilter, blurFilter, invertFilter, pixelateFilter, grayFilter, sepiaFilter, twistFilter, dotScreenFilter, colorStepFilter, crossHatchFilter, rgbSplitterFilter]; 110 | 111 | // create an new instance of a pixi stage 112 | var stage = window.HTMLGL.stage; 113 | 114 | var pondContainer = slider.sprite; 115 | 116 | stage.interactive = true; 117 | 118 | var padding = 100; 119 | var bounds = new PIXI.Rectangle(-padding, -padding, window.HTMLGL.renderer.width + padding * 2, window.HTMLGL.renderer.height + padding * 2) 120 | 121 | //var overlay = new PIXI.TilingSprite(PIXI.Texture.fromImage("assets/img/zeldaWaves.png"), window.HTMLGL.renderer.width, window.HTMLGL.renderer.height); 122 | //overlay.alpha = 0.1//0.2 123 | //pondContainer.addChild(overlay); 124 | 125 | displacementFilter.scale.x = 25; 126 | displacementFilter.scale.y = 25; 127 | 128 | var count = 0; 129 | var switchy = false; 130 | 131 | function animate() { 132 | 133 | if (window.HTMLGL.scrollY > 1000) { 134 | count += 0.1; 135 | 136 | var blurAmount = Math.cos(count); 137 | var blurAmount2 = Math.sin(count * 0.8); 138 | 139 | var filtersToApply = []; 140 | 141 | for (var i = 0; i < filterCollection.length; i++) { 142 | if (filtersSwitchs[i])filtersToApply.push(filterCollection[i]); 143 | } 144 | 145 | var rgbFilterIndex = filtersToApply.indexOf(rgbSplitterFilter); 146 | 147 | if (rgbFilterIndex !== -1) { 148 | slider.sprite.filters = [rgbSplitterFilter]; 149 | filtersToApply.splice(rgbFilterIndex, 1); 150 | } else { 151 | if (slider.sprite.filters) { 152 | var rgbFilterIndexOnSlider = slider.sprite.filters.indexOf(rgbSplitterFilter); 153 | if (rgbFilterIndexOnSlider !== -1) { 154 | slider.sprite.filters = null; 155 | } 156 | } 157 | } 158 | 159 | pondContainer.filters = filtersToApply.length > 0 ? filtersToApply : null; 160 | 161 | displacementSprite.x = count * 10; 162 | displacementSprite.y = count * 10; 163 | 164 | renderer.render(stage); 165 | } 166 | 167 | requestAnimationFrame(animate); 168 | } 169 | 170 | renderer.render(stage); 171 | requestAnimationFrame( animate ); 172 | }); -------------------------------------------------------------------------------- /page/js/slider.js: -------------------------------------------------------------------------------- 1 | //Slider logic, written in HTML / JS. 2 | //The only difference between HTML GL and basic HTML is that you should use element.styleGL.transform instead of element.style.transform 3 | 4 | var images = document.getElementsByTagName('img'), 5 | dragStart = false, 6 | slider = document.getElementsByTagName('html-gl')[1], 7 | startX = 0, 8 | startLeft = 0, 9 | transformPropertyName = document.body.style.transform !== undefined ? 'transform' : 'WebkitTransform'; 10 | 11 | for (var i = 0; i < images.length; i++) { 12 | images[i].ondragstart = function () { 13 | return false; 14 | }; 15 | } 16 | 17 | function setSliderX(x) { 18 | (slider.styleGL || slider.style)[transformPropertyName] = 'translateZ(0) translateX(' + (startLeft - (startX - (x || 0))) + 'px)'; 19 | } 20 | 21 | function getSliderX() { 22 | return parseInt(parseCSSTransform((slider.styleGL || slider.style)[transformPropertyName]).translateX) || 0 23 | } 24 | 25 | function onDragStart(event) { 26 | dragStart = true; 27 | startX = (event.pageX || event.x) || event.touches[0].pageX; 28 | startLeft = getSliderX(); 29 | } 30 | 31 | function onDragEnd() { 32 | dragStart = false; 33 | } 34 | 35 | function onMove(event) { 36 | if (dragStart) { 37 | var pageX = (event.pageX || event.x) || event.touches[0].pageX, 38 | moveTime = new Date(); 39 | 40 | setSliderX(pageX); 41 | } 42 | } 43 | 44 | slider.addEventListener('mousedown', onDragStart); 45 | slider.addEventListener('mouseup', onDragEnd); 46 | slider.addEventListener('mousemove', onMove); 47 | slider.addEventListener('touchstart', onDragStart); 48 | slider.addEventListener('touchend', onDragEnd); 49 | slider.addEventListener('touchmove', onMove); 50 | 51 | parseCSSTransform = function (transformString) { 52 | return (transformString.match(/([\w]+)\(([^\)]+)\)/g) || []) 53 | //make pairs of prop and value 54 | .map(function (it) { 55 | return it.replace(/\)$/, "").split(/\(/) 56 | }) 57 | //convert to key-value map/object 58 | .reduce(function (m, it) { 59 | return m[it[0]] = it[1], m 60 | }, {}); 61 | } -------------------------------------------------------------------------------- /src/effects/ascii.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Ascii = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.AsciiFilter(); 6 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 7 | } 8 | 9 | var p = Ascii.prototype; 10 | 11 | p.destroy = function () { 12 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 13 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 14 | } 15 | 16 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 17 | w.HTMLGL.effects.ascii = Ascii; 18 | })(window); -------------------------------------------------------------------------------- /src/effects/bloom.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Bloom = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.BloomFilter(); 6 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 7 | } 8 | 9 | var p = Bloom.prototype; 10 | 11 | p.destroy = function () { 12 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 13 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 14 | } 15 | 16 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 17 | w.HTMLGL.effects.bloom = Bloom; 18 | })(window); -------------------------------------------------------------------------------- /src/effects/diagonal-blur.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var DiagonalBlur = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.BlurDirFilter(2, 2); 6 | this.filter.passes = 2; 7 | this.filter.blur = 3; 8 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 9 | } 10 | 11 | var p = DiagonalBlur.prototype; 12 | 13 | p.destroy = function () { 14 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 15 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 16 | } 17 | 18 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 19 | w.HTMLGL.effects.diagonalblur = DiagonalBlur; 20 | })(window); -------------------------------------------------------------------------------- /src/effects/dotscreen.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var DotScreen = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.DotScreenFilter(); 6 | this.filter.scale = parseInt(this.element.getAttribute('dotScreenScale')) || 1; 7 | this.filter.angle = parseInt(this.element.getAttribute('dotScreenAngle')) || 1; 8 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 9 | } 10 | 11 | var p = DotScreen.prototype; 12 | 13 | p.destroy = function () { 14 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 15 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 16 | } 17 | 18 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 19 | w.HTMLGL.effects.dotscreen = DotScreen; 20 | })(window); -------------------------------------------------------------------------------- /src/effects/invert.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Invert = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.InvertFilter(); 6 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 7 | } 8 | 9 | var p = Invert.prototype; 10 | 11 | p.destroy = function () { 12 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 13 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 14 | } 15 | 16 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 17 | w.HTMLGL.effects.invert = Invert; 18 | })(window); -------------------------------------------------------------------------------- /src/effects/noise.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Noise = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.NoiseFilter(); 6 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 7 | } 8 | 9 | var p = Noise.prototype; 10 | 11 | p.destroy = function () { 12 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 13 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 14 | } 15 | 16 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 17 | w.HTMLGL.effects.noise = Noise; 18 | })(window); -------------------------------------------------------------------------------- /src/effects/pixelate.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Pixelate = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.PixelateFilter(); 6 | this.filter.size.x = parseInt(this.element.getAttribute('pixelSizeX')) || 2; 7 | this.filter.size.y = parseInt(this.element.getAttribute('pixelSizeY')) || 2; 8 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 9 | } 10 | 11 | var p = Pixelate.prototype; 12 | 13 | p.destroy = function () { 14 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 15 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 16 | } 17 | 18 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 19 | w.HTMLGL.effects.pixelate = Pixelate; 20 | })(window); -------------------------------------------------------------------------------- /src/effects/rgbsplit.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var RGBSplit = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.RGBSplitFilter(); 6 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 7 | } 8 | 9 | var p = RGBSplit.prototype; 10 | 11 | p.destroy = function () { 12 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 13 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 14 | } 15 | 16 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 17 | w.HTMLGL.effects.rgbsplit = RGBSplit; 18 | })(window); -------------------------------------------------------------------------------- /src/effects/twist.js: -------------------------------------------------------------------------------- 1 | (function (w) { 2 | 3 | var Twist = function (element) { 4 | this.element = element; 5 | this.filter = new PIXI.filters.TwistFilter(); 6 | this.filter.radius = parseInt(this.element.getAttribute('twistRadius')) || 0.5; 7 | this.filter.angle = parseInt(this.element.getAttribute('twistAngle')) || 5; 8 | this.element.sprite.filters = (this.element.sprite.filters || []).concat(this.filter); 9 | } 10 | 11 | var p = Twist.prototype; 12 | 13 | p.destroy = function () { 14 | var filterIndex = this.element.sprite.filters.indexOf(this.filter); 15 | this.element.sprite.filters = this.element.sprite.filters.splice(filterIndex, 1); 16 | } 17 | 18 | w.HTMLGL.effects = w.HTMLGL.effects || {}; 19 | w.HTMLGL.effects.twist = Twist; 20 | })(window); -------------------------------------------------------------------------------- /src/gl-context.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GLContext is a part of HTML GL library describing rendering context 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * */ 7 | 8 | (function (w) { 9 | //Defining global namespace with respect if exists 10 | HTMLGL = w.HTMLGL = w.HTMLGL || {}; 11 | 12 | //Defining it`s properties 13 | HTMLGL.JQ_PLUGIN_NAME = 'htmlgl'; 14 | HTMLGL.CUSTOM_ELEMENT_TAG_NAME = 'html-gl'; 15 | HTMLGL.READY_EVENT = 'htmlglReady'; 16 | HTMLGL.context = undefined; 17 | HTMLGL.stage = undefined; 18 | HTMLGL.renderer = undefined; 19 | HTMLGL.elements = []; 20 | HTMLGL.pixelRatio = null; 21 | HTMLGL.oldPixelRatio = null; 22 | HTMLGL.enabled = true; 23 | 24 | //Cache for window`s scroll position, filled in by updateScrollPosition 25 | HTMLGL.scrollX = 0; 26 | HTMLGL.scrollY = 0; 27 | 28 | var GLContext = function () { 29 | w.HTMLGL.context = this; 30 | 31 | this.createStage(); //Creating stage before showing it 32 | this.updateScrollPosition(); //Initialize scroll position for first time 33 | this.initListeners(); 34 | this.elementResolver = new w.HTMLGL.GLElementResolver(this); 35 | 36 | //Wait for DOMContentLoaded and initialize viewer then 37 | if (!document.body) { 38 | document.addEventListener("DOMContentLoaded", this.initViewer.bind(this)); 39 | } else { 40 | this.initViewer(); 41 | } 42 | } 43 | 44 | var p = GLContext.prototype; 45 | 46 | p.initViewer = function () { 47 | this.createViewer(); 48 | this.resizeViewer(); 49 | this.appendViewer(); 50 | } 51 | 52 | p.createViewer = function () { 53 | w.HTMLGL.renderer = this.renderer = PIXI.autoDetectRenderer(0, 0, {transparent: true}); 54 | this.renderer.view.style.position = 'fixed'; 55 | this.renderer.view.style.top = '0px'; 56 | this.renderer.view.style.left = '0px'; 57 | this.renderer.view.style['pointer-events'] = 'none'; 58 | this.renderer.view.style['pointerEvents'] = 'none'; 59 | } 60 | 61 | p.appendViewer = function () { 62 | document.body.appendChild(this.renderer.view); 63 | requestAnimationFrame(this.redrawStage.bind(this)); 64 | } 65 | 66 | p.resizeViewer = function () { 67 | var self = this, 68 | width = w.innerWidth, 69 | height = w.innerHeight; 70 | 71 | //Update pixelRatio since could be resized on different screen with different ratio 72 | HTMLGL.pixelRatio = w.HTMLGL.getPixelRatio(); 73 | 74 | console.log(HTMLGL.pixelRatio); 75 | 76 | width = width * HTMLGL.pixelRatio; 77 | height = height * HTMLGL.pixelRatio; 78 | 79 | if (HTMLGL.pixelRatio !== HTMLGL.oldPixelRatio) { 80 | this.disable(); 81 | this.updateTextures().then(function(){ 82 | self.updateScrollPosition(); 83 | self.updateElementsPositions(); 84 | 85 | var rendererScale = 1 / HTMLGL.pixelRatio; 86 | self.renderer.view.style.transformOrigin = '0 0'; 87 | self.renderer.view.style.webkitTransformOrigin = '0 0'; 88 | self.renderer.view.style.transform = 'scaleX(' + rendererScale + ') scaleY(' + rendererScale + ')'; 89 | self.renderer.view.style.webkitTransform = 'scaleX(' + rendererScale + ') scaleY(' + rendererScale + ')'; 90 | self.renderer.resize(width, height); 91 | 92 | w.HTMLGL.renderer.render(w.HTMLGL.stage); 93 | }); 94 | } else { 95 | if (this.renderer.view.parentNode) { //No need to update textures when is not mounted yet 96 | this.updateTextures(); 97 | } 98 | this.updateElementsPositions(); 99 | this.markStageAsChanged(); 100 | } 101 | 102 | HTMLGL.oldPixelRatio = HTMLGL.pixelRatio; 103 | } 104 | 105 | p.initListeners = function () { 106 | //window listeners 107 | w.addEventListener('scroll', this.updateScrollPosition.bind(this)); 108 | w.addEventListener('resize', w.HTMLGL.util.debounce(this.resizeViewer, 500).bind(this)); 109 | w.addEventListener('resize', this.updateElementsPositions.bind(this)); 110 | 111 | //document listeners - mouse and touch events 112 | document.addEventListener('click', this.onMouseEvent.bind(this), true); 113 | document.addEventListener('mousemove', this.onMouseEvent.bind(this), true); 114 | document.addEventListener('mouseup', this.onMouseEvent.bind(this), true); 115 | document.addEventListener('mousedown', this.onMouseEvent.bind(this), true); 116 | document.addEventListener('touchstart', this.onMouseEvent.bind(this)); 117 | document.addEventListener('touchend', this.onMouseEvent.bind(this)); 118 | } 119 | 120 | p.updateScrollPosition = function () { 121 | var scrollOffset = {}; 122 | 123 | if (window.pageYOffset != undefined) { 124 | scrollOffset = { 125 | left: pageXOffset, 126 | top: pageYOffset 127 | }; 128 | } else { 129 | var sx, sy, d = document, r = d.documentElement, b = d.body; 130 | sx = r.scrollLeft || b.scrollLeft || 0; 131 | sy = r.scrollTop || b.scrollTop || 0; 132 | scrollOffset = { 133 | left: sx, 134 | top: sy 135 | }; 136 | } 137 | 138 | this.document.x = -scrollOffset.left * HTMLGL.pixelRatio; 139 | this.document.y = -scrollOffset.top * HTMLGL.pixelRatio; 140 | 141 | w.HTMLGL.scrollX = scrollOffset.left; 142 | w.HTMLGL.scrollY = scrollOffset.top; 143 | 144 | this.markStageAsChanged(); 145 | } 146 | 147 | p.createStage = function () { 148 | w.HTMLGL.stage = this.stage = new PIXI.Stage(0xFFFFFF); 149 | w.HTMLGL.document = this.document = new PIXI.DisplayObjectContainer(); 150 | this.stage.addChild(w.HTMLGL.document); 151 | } 152 | 153 | //Avoiding function.bind() for performance and memory consuming reasons 154 | p.redrawStage = function () { 155 | if (w.HTMLGL.stage.changed && w.HTMLGL.renderer && w.HTMLGL.enabled) { 156 | w.HTMLGL.renderer.render(w.HTMLGL.stage); 157 | w.HTMLGL.stage.changed = false; 158 | } 159 | } 160 | 161 | p.updateTextures = function () { 162 | var updatePromises = []; 163 | 164 | w.HTMLGL.elements.forEach(function (element) { 165 | updatePromises.push(element.updateTexture()); 166 | }); 167 | 168 | return Promise.all(updatePromises); 169 | } 170 | 171 | p.initElements = function () { 172 | w.HTMLGL.elements.forEach(function (element) { 173 | element.init(); 174 | }); 175 | } 176 | 177 | p.updateElementsPositions = function () { 178 | w.HTMLGL.elements.forEach(function (element) { 179 | element.updateBoundingRect(); 180 | element.updatePivot(); 181 | element.updateSpriteTransform(); 182 | }); 183 | } 184 | 185 | p.onMouseEvent = function (event) { 186 | var x = event.x || event.pageX, 187 | y = event.y || event.pageY, 188 | //Finding element under mouse position 189 | element = event.dispatcher !== 'html-gl' ? this.elementResolver.getElementByCoordinates(x, y) : null; 190 | 191 | //Emit event if there is an element under mouse position 192 | element ? w.HTMLGL.util.emitEvent(element, event) : null; 193 | } 194 | 195 | //We would like to rerender if something changed, otherwise stand by 196 | p.markStageAsChanged = function () { 197 | if (w.HTMLGL.stage && !w.HTMLGL.stage.changed) { 198 | requestAnimationFrame(this.redrawStage); 199 | w.HTMLGL.stage.changed = true; 200 | } 201 | } 202 | 203 | p.disable = function () { 204 | w.HTMLGL.enabled = true; 205 | } 206 | 207 | p.enable = function () { 208 | w.HTMLGL.enabled = false; 209 | } 210 | 211 | w.HTMLGL.getPixelRatio = function() { 212 | return window.devicePixelRatio || 1; 213 | } 214 | 215 | w.HTMLGL.pixelRatio = w.HTMLGL.getPixelRatio(); 216 | 217 | w.HTMLGL.GLContext = GLContext; 218 | new GLContext(); 219 | })(window); -------------------------------------------------------------------------------- /src/gl-effects-manager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GLEffectsManager is a part of HTML GL library for applying effects based on tag attributes 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * */ 7 | 8 | (function (w) { 9 | var GLEffectsManager = function (element) { 10 | this.element = element; 11 | this.filters = {}; 12 | this.initObserver(); 13 | this.updateFilters(); 14 | } 15 | 16 | var p = GLEffectsManager.prototype; 17 | 18 | p.initObserver = function () { 19 | var self = this, 20 | config = { 21 | attributes: true, 22 | attributeFilter: ['effects'] 23 | }; 24 | 25 | this.observer = this.observer || new MutationObserver(self.updateFilters.bind(this)); 26 | 27 | this.observer.observe(this.element, config); 28 | } 29 | 30 | p.updateFilters = function () { 31 | var attributeValue = this.element.getAttribute('effects') || '', 32 | effectsNamesArray = attributeValue.split(' '); 33 | 34 | this.addFiltersFromAttributes(effectsNamesArray); 35 | this.cleanFiltersFromAttributes(effectsNamesArray); 36 | } 37 | 38 | p.addFiltersFromAttributes = function (effectsNamesArray) { 39 | var self = this; 40 | effectsNamesArray.forEach(function (effectName) { 41 | if (HTMLGL.effects[effectName]) { 42 | self.filters[effectName] = self.filters[effectName] || new HTMLGL.effects[effectName](self.element); 43 | } 44 | }); 45 | } 46 | 47 | p.cleanFiltersFromAttributes = function (effectsNamesArray) { 48 | var self = this; 49 | Object.keys(this.filters).forEach(function (effectName) { 50 | if (effectsNamesArray.indexOf(effectName) === -1 && self.filters[effectName]) { 51 | self.filters[effectName].destroy(); 52 | self.filters[effectName] = null; 53 | } 54 | }); 55 | } 56 | 57 | w.HTMLGL.GLEffectsManager = GLEffectsManager; 58 | 59 | //Reinitialize effects on elements 60 | w.HTMLGL.elements.forEach(function (element) { 61 | element.initEffects(); 62 | }); 63 | })(window); -------------------------------------------------------------------------------- /src/gl-element-resolver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GLElementResolver is a part of HTML GL library for resolving elements by coordinates given 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * */ 7 | 8 | (function (w) { 9 | var GLElementResolver = function (context) { 10 | } 11 | 12 | var p = GLElementResolver.prototype; 13 | 14 | p.getElementByCoordinates = function (x, y) { 15 | var element, 16 | self = this, 17 | result; 18 | 19 | w.HTMLGL.elements.forEach(function (glelement) { 20 | element = document.elementFromPoint(x - parseInt(glelement.transformObject.translateX || 0), y - parseInt(glelement.transformObject.translateY || 0)) 21 | if (self.isChildOf(element, glelement)) { 22 | result = element; 23 | } 24 | }); 25 | 26 | return result; 27 | } 28 | 29 | p.isChildOf = function (child, parent) { 30 | var current = child; 31 | while (current) { 32 | if (current === parent) return true; 33 | current = current.parentNode; 34 | } 35 | return false; 36 | } 37 | 38 | w.HTMLGL.GLElementResolver = GLElementResolver; 39 | })(window); -------------------------------------------------------------------------------- /src/gl-element.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GLElement is a part of HTML GL library describing single HTML-GL element 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * 7 | * Please, take into account: 8 | * - updateTexture is expensive 9 | * - updateSpriteTransform is cheap 10 | * */ 11 | 12 | (function (w) { 13 | var p = Object.create(HTMLElement.prototype), 14 | style = document.createElement('style'); 15 | 16 | //Default styling for html-gl elements 17 | style.innerHTML = HTMLGL.CUSTOM_ELEMENT_TAG_NAME + ' { display: inline-block; transform: translateZ(0);}'; 18 | document.head.appendChild(style); 19 | 20 | p.createdCallback = function () { 21 | //Checking is node created inside of html2canvas virtual window or not. We do not need WebGL there 22 | var currentNode = this, 23 | isMounted = false; 24 | 25 | while (currentNode = currentNode.parentNode) { 26 | if (currentNode.tagName === 'BODY') { 27 | isMounted = true; 28 | } 29 | } 30 | 31 | var isInsideHtml2Canvas = !isMounted || (this.baseURI === undefined || this.baseURI === '' || this.baseURI === null); 32 | 33 | if (!isInsideHtml2Canvas) { 34 | HTMLGL.elements.push(this); 35 | //Needed to determine is element WebGL rendered or not relying on tag name 36 | this.setAttribute('renderer', 'webgl'); 37 | this.renderer = 'webgl'; 38 | 39 | this.transformObject = {}; 40 | this.boundingRect = {}; 41 | this.image = {}; 42 | this.sprite = new PIXI.Sprite(); 43 | this.texture = {}; 44 | 45 | this.halfWidth = 0; 46 | this.halfHeight = 0; 47 | 48 | this.observer = undefined; 49 | 50 | this.glChilds = []; 51 | this.glChildsReady = 0; 52 | this.glParent = this.getGlParent(); 53 | 54 | this.initEffects(); 55 | this.bindCallbacks(); 56 | this.transformProperty = this.style.transform !== undefined ? 'transform' : 'WebkitTransform'; 57 | this.init(); 58 | } 59 | } 60 | 61 | p.init = function () { 62 | this.notifyGlParent(); 63 | this.updateTexture(); 64 | this.initObservers(); 65 | this.patchStyleGLTransform(); 66 | } 67 | 68 | p.getGlParent = function () { 69 | var parent = this, 70 | tagName = HTMLGL.CUSTOM_ELEMENT_TAG_NAME.toUpperCase(); 71 | 72 | while (parent) { 73 | parent = parent.parentNode; 74 | 75 | if (parent && parent.tagName === tagName) { 76 | return parent; 77 | } else if (parent === w.document) { 78 | return null; 79 | } 80 | } 81 | } 82 | 83 | //Notify GL parent about one more HTML GL child in the tree 84 | p.notifyGlParent = function () { 85 | if (this.glParent) { 86 | this.glParent.addGlChild(this); 87 | } 88 | } 89 | 90 | p.addGlChild = function (child) { 91 | this.glChilds.push(child); 92 | } 93 | 94 | p.glChildReady = function () { 95 | this.glChildsReady++; 96 | this.dispatchReady(); 97 | } 98 | 99 | //If all my childs ready notify parent that I am 100 | p.dispatchReady = function () { 101 | if (this.isReady()) { 102 | var event = new Event(HTMLGL.READY_EVENT); 103 | this.dispatchEvent(event); 104 | 105 | if (this.glParent) { 106 | //Inform parent 107 | this.glParent.glChildReady(); 108 | } 109 | } 110 | } 111 | 112 | p.isReady = function () { 113 | return (this.glChilds.length - this.glChildsReady === 0) && this.haveSprite(); 114 | } 115 | 116 | //Updating bounds, waiting for all images to load and calling rasterization then 117 | p.updateTexture = function () { 118 | var self = this; 119 | self.updateBoundingRect(); 120 | 121 | return new Promise(function(resolve, reject){ 122 | self.image = html2canvas(self, { 123 | width: self.boundingRect.width * HTMLGL.pixelRatio, 124 | height: self.boundingRect.height * HTMLGL.pixelRatio 125 | }).then(function(textureCanvas){ 126 | self.applyNewTexture(textureCanvas); 127 | resolve(); 128 | }); 129 | }); 130 | } 131 | 132 | //Recreating texture from canvas given after calling updateTexture 133 | p.applyNewTexture = function (textureCanvas) { 134 | this.image = textureCanvas; 135 | this.texture = PIXI.Texture.fromCanvas(this.image); 136 | 137 | if (!this.haveSprite()) { 138 | this.initSprite(this.texture); 139 | } else { 140 | this.sprite.texture.destroy(); 141 | this.sprite.texture = this.texture; 142 | } 143 | 144 | this.updatePivot(); 145 | this.updateSpriteTransform(); 146 | 147 | HTMLGL.context.markStageAsChanged(); 148 | } 149 | 150 | //Just updates WebGL representation coordinates and transformation 151 | p.updateSpriteTransform = function () { 152 | 153 | //TODO add 3d rotation support 154 | var translateX = parseFloat(this.transformObject.translateX) || 0, 155 | translateY = parseFloat(this.transformObject.translateY) || 0, 156 | scaleX = parseFloat(this.transformObject.scaleX) || 1, 157 | scaleY = parseFloat(this.transformObject.scaleY) || 1, 158 | rotate = (parseFloat(this.transformObject.rotateZ) / 180) * Math.PI || 0; 159 | 160 | if (this.sprite && this.sprite.position) { 161 | this.sprite.position.x = (this.boundingRect.left + translateX) * HTMLGL.pixelRatio + this.halfWidth; 162 | this.sprite.position.y = (this.boundingRect.top + translateY) * HTMLGL.pixelRatio + this.halfHeight; 163 | this.sprite.scale.x = scaleX; 164 | this.sprite.scale.y = scaleY; 165 | this.sprite.rotation = rotate; 166 | } 167 | 168 | HTMLGL.context.markStageAsChanged(); 169 | } 170 | 171 | //Getting bounding rect with respect to current scroll position 172 | p.updateBoundingRect = function () { 173 | var boundingRect = this.getBoundingClientRect(); 174 | 175 | this.boundingRect = { 176 | left: boundingRect.left, 177 | right: boundingRect.right, 178 | top: boundingRect.top, 179 | bottom: boundingRect.bottom, 180 | width: boundingRect.width, 181 | height: boundingRect.height, 182 | }; 183 | 184 | if (this.glParent && this.glParent.boundingRect) { 185 | this.boundingRect.left -= this.glParent.boundingRect.left; 186 | this.boundingRect.top -= this.glParent.boundingRect.top; 187 | } 188 | 189 | this.boundingRect.left = HTMLGL.scrollX + parseFloat(this.boundingRect.left); 190 | this.boundingRect.top = HTMLGL.scrollY + parseFloat(this.boundingRect.top); 191 | } 192 | 193 | //Correct pivot needed to rotate element around it`s center 194 | p.updatePivot = function () { 195 | this.halfWidth = this.sprite.width / 2; 196 | this.halfHeight = this.sprite.height / 2; 197 | this.sprite.pivot.x = this.halfWidth; 198 | this.sprite.pivot.y = this.halfHeight; 199 | } 200 | 201 | p.initSprite = function (texture) { 202 | var self = this, 203 | parentSprite = this.glParent && this.glParent.sprite || w.HTMLGL.document; 204 | 205 | this.sprite.texture = texture; 206 | parentSprite.addChild(this.sprite); 207 | 208 | setTimeout(function () { 209 | self.hideDOM(); 210 | //Notify parents that I am initialized 211 | self.dispatchReady(); 212 | }, 0); 213 | } 214 | 215 | p.initObservers = function () { 216 | //TODO Better heuristics for rerendering condition #2 217 | var self = this, 218 | config = { 219 | childList: true, 220 | characterData: true, 221 | subtree: true, 222 | attributes: true, 223 | attributeFilter: ['style'] 224 | }; 225 | 226 | this.observer = this.observer || new MutationObserver(function (mutations) { 227 | if (mutations[0].attributeName === 'style') { 228 | self.transformObject = self.getTransformObjectFromString(self.style[self.transformProperty]); 229 | self.updateSpriteTransform(); 230 | } else { 231 | self.updateTexture(); 232 | } 233 | }); 234 | 235 | this.observer.observe(this, config); 236 | } 237 | 238 | p.patchStyleGLTransform = function () { 239 | var self = this; 240 | self.styleGL = {}; 241 | 242 | HTMLGL.util.getterSetter(this.styleGL, this.transformProperty, function () { 243 | var result = ''; 244 | 245 | for (var transformPropertyName in self.transformObject) { 246 | var transformPropertyValue = '(' + self.transformObject[transformPropertyName] + ') '; 247 | result += transformPropertyName + transformPropertyValue; 248 | } 249 | 250 | return result; 251 | }, 252 | function (value) { 253 | self.transformObject = self.getTransformObjectFromString(value); 254 | self.updateSpriteTransform(); 255 | } 256 | ) 257 | } 258 | 259 | p.getTransformObjectFromString = function (transformString) { 260 | return (transformString.match(/([\w]+)\(([^\)]+)\)/g) || []) 261 | .map(function (it) { 262 | return it.replace(/\)$/, "").split(/\(/) 263 | }) 264 | .reduce(function (m, it) { 265 | return m[it[0]] = it[1], m 266 | }, {}); 267 | } 268 | 269 | p.hideDOM = function () { 270 | this.style.opacity = 0; 271 | } 272 | 273 | p.bindCallbacks = function () { 274 | this.applyNewTexture = this.applyNewTexture.bind(this); 275 | } 276 | 277 | p.haveSprite = function () { 278 | return this.sprite.stage; 279 | } 280 | 281 | p.initEffects = function () { 282 | if (HTMLGL.GLEffectsManager) { 283 | this.effectsManager = new HTMLGL.GLEffectsManager(this); 284 | } 285 | } 286 | 287 | HTMLGL.GLElement = document.registerElement(HTMLGL.CUSTOM_ELEMENT_TAG_NAME, { 288 | prototype: p 289 | }) 290 | 291 | HTMLGL.GLElement.createFromNode = function (node) { 292 | //Extending node with GLElement methods 293 | for (var i in p) { 294 | if (p.hasOwnProperty(i)) { 295 | node[i] = p[i]; 296 | } 297 | } 298 | 299 | p.createdCallback.apply(node); 300 | return node; 301 | } 302 | 303 | //Wrap to jQuery plugin 304 | if (w.jQuery !== undefined) { 305 | jQuery[HTMLGL.JQ_PLUGIN_NAME] = {}; 306 | jQuery[HTMLGL.JQ_PLUGIN_NAME].elements = []; 307 | 308 | jQuery.fn[HTMLGL.JQ_PLUGIN_NAME] = function () { 309 | return this.each(function () { 310 | if (!jQuery.data(this, 'plugin_' + HTMLGL.JQ_PLUGIN_NAME)) { 311 | var htmlGLobj = HTMLGL.GLElement.createFromNode(this); 312 | jQuery.data(this, 'plugin_' + HTMLGL.JQ_PLUGIN_NAME, htmlGLobj); 313 | jQuery[HTMLGL.JQ_PLUGIN_NAME].elements.push(htmlGLobj); 314 | } 315 | }); 316 | }; 317 | } 318 | })(window); -------------------------------------------------------------------------------- /src/images-loaded.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ImagesLoaded is a part of HTML GL library which is a robust solution for determining "are images loaded or not?" 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * */ 7 | 8 | (function (w) { 9 | var ImagesLoaded = function (element, callback) { 10 | this.element = element; 11 | this.images = this.element.querySelectorAll('img'); 12 | this.callback = callback; 13 | this.imagesLoaded = this.getImagesLoaded(); 14 | 15 | if (this.images.length === this.imagesLoaded) { 16 | this.onImageLoaded(); 17 | } else { 18 | this.addListeners(); 19 | } 20 | } 21 | 22 | var p = ImagesLoaded.prototype; 23 | 24 | p.getImagesLoaded = function () { 25 | var result = 0; 26 | for (var i = 0; i < this.images.length; i++) { 27 | if (this.images[i].complete === true) { 28 | result++; 29 | } 30 | } 31 | return result; 32 | } 33 | 34 | p.addListeners = function () { 35 | var result = 0; 36 | for (var i = 0; i < this.images.length; i++) { 37 | if (this.images[i].complete !== true) { 38 | this.images[i].addEventListener('load', this.onImageLoaded.bind(this)); 39 | this.images[i].addEventListener('error', this.onImageLoaded.bind(this)); 40 | } 41 | } 42 | return result; 43 | } 44 | 45 | p.onImageLoaded = function () { 46 | this.imagesLoaded++; 47 | if (this.images.length - this.imagesLoaded <= 0) { 48 | setTimeout(this.callback, 0); 49 | } 50 | } 51 | 52 | w.HTMLGL.ImagesLoaded = ImagesLoaded; 53 | })(window); -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Util is a part of HTML GL library 3 | * Copyright (c) 2015 pixelscommander.com 4 | * Distributed under MIT license 5 | * http://htmlgl.com 6 | * */ 7 | 8 | (function(w){ 9 | w.HTMLGL = w.HTMLGL || {}; 10 | w.HTMLGL.util = { 11 | getterSetter: function (variableParent, variableName, getterFunction, setterFunction) { 12 | if (Object.defineProperty) { 13 | Object.defineProperty(variableParent, variableName, { 14 | get: getterFunction, 15 | set: setterFunction 16 | }); 17 | } 18 | else if (document.__defineGetter__) { 19 | variableParent.__defineGetter__(variableName, getterFunction); 20 | variableParent.__defineSetter__(variableName, setterFunction); 21 | } 22 | 23 | variableParent["get" + variableName] = getterFunction; 24 | variableParent["set" + variableName] = setterFunction; 25 | }, 26 | emitEvent: function (element, event) { 27 | var newEvent = new MouseEvent(event.type, event); 28 | newEvent.dispatcher = 'html-gl'; 29 | event.stopPropagation(); 30 | element.dispatchEvent(newEvent); 31 | }, 32 | debounce: function(func, wait, immediate) { 33 | var timeout; 34 | return function() { 35 | var context = this, args = arguments; 36 | var later = function() { 37 | timeout = null; 38 | if (!immediate) func.apply(context, args); 39 | }; 40 | var callNow = immediate && !timeout; 41 | clearTimeout(timeout); 42 | timeout = setTimeout(later, wait); 43 | if (callNow) func.apply(context, args); 44 | }; 45 | } 46 | } 47 | })(window); --------------------------------------------------------------------------------