vendor: 24,594 bytes, lines 1-899
1/** 2 * jQuery Ripples plugin v0.6.2 / https://github.com/sirxemic/jquery.ripples 3 * MIT License 4 * @author sirxemic / https://sirxemic.com/ 5 */ 6 7(function (global, factory) { 8 typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) : 9 typeof define === 'function' && define.amd ? define(['jquery'], factory) : 10 (factory(global.$)); 11}(this, (function ($) { 'use strict'; 12 13$ = $ && 'default' in $ ? $['default'] : $; 14 15var gl; 16var $window = $(window); // There is only one window, so why not cache the jQuery-wrapped window? 17 18function isPercentage(str) { 19 return str[str.length - 1] == '%'; 20} 21 22/** 23 * Load a configuration of GL settings which the browser supports. 24 * For example: 25 * - not all browsers support WebGL 26 * - not all browsers support floating point textures 27 * - not all browsers support linear filtering for floating point textures 28 * - not all browsers support rendering to floating point textures 29 * - some browsers *do* support rendering to half-floating point textures instead. 30 */ 31function loadConfig() { 32 var canvas = document.createElement('canvas'); 33 gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 34 35 if (!gl) { 36 // Browser does not support WebGL. 37 return null; 38 } 39 40 // Load extensions 41 var extensions = {}; 42 [ 43 'OES_texture_float', 44 'OES_texture_half_float', 45 'OES_texture_float_linear', 46 'OES_texture_half_float_linear' 47 ].forEach(function(name) { 48 var extension = gl.getExtension(name); 49 if (extension) { 50 extensions[name] = extension; 51 } 52 }); 53 54 // If no floating point extensions are supported we can bail out early. 55 if (!extensions.OES_texture_float) { 56 return null; 57 } 58 59 var configs = []; 60 61 function createConfig(type, glType, arrayType) { 62 var name = 'OES_texture_' + type, 63 nameLinear = name + '_linear', 64 linearSupport = nameLinear in extensions, 65 configExtensions = [name]; 66 67 if (linearSupport) { 68 configExtensions.push(nameLinear); 69 } 70 71 return { 72 type: glType, 73 arrayType: arrayType, 74 linearSupport: linearSupport, 75 extensions: configExtensions 76 }; 77 } 78 79 configs.push( 80 createConfig('float', gl.FLOAT, Float32Array) 81 ); 82 83 if (extensions.OES_texture_half_float) { 84 configs.push( 85 // Array type should be Uint16Array, but at least on iOS that breaks. In that case we 86 // just initialize the textures with data=null, instead of data=new Uint16Array(...). 87 // This makes initialization a tad slower, but it's still negligible. 88 createConfig('half_float', extensions.OES_texture_half_float.HALF_FLOAT_OES, null) 89 ); 90 } 91 92 // Setup the texture and framebuffer 93 var texture = gl.createTexture(); 94 var framebuffer = gl.createFramebuffer(); 95 96 gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 97 gl.bindTexture(gl.TEXTURE_2D, texture); 98 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 99 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 100 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 101 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 102 103 // Check for each supported texture type if rendering to it is supported 104 var config = null; 105 106 for (var i = 0; i < configs.length; i++) { 107 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 32, 0, gl.RGBA, configs[i].type, null); 108 109 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 110 if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) { 111 config = configs[i]; 112 break; 113 } 114 } 115 116 return config; 117} 118 119function createImageData(width, height) { 120 try { 121 return new ImageData(width, height); 122 } 123 catch (e) { 124 // Fallback for IE 125 var canvas = document.createElement('canvas'); 126 return canvas.getContext('2d').createImageData(width, height); 127 } 128} 129 130function translateBackgroundPosition(value) { 131 var parts = value.split(' '); 132 133 if (parts.length === 1) { 134 switch (value) { 135 case 'center': 136 return ['50%', '50%']; 137 case 'top': 138 return ['50%', '0']; 139 case 'bottom': 140 return ['50%', '100%']; 141 case 'left': 142 return ['0', '50%']; 143 case 'right': 144 return ['100%', '50%']; 145 default: 146 return [value, '50%']; 147 } 148 } 149 else { 150 return parts.map(function(part) { 151 switch (value) { 152 case 'center': 153 return '50%'; 154 case 'top': 155 case 'left': 156 return '0'; 157 case 'right': 158 case 'bottom': 159 return '100%'; 160 default: 161 return part; 162 } 163 }); 164 } 165} 166 167function createProgram(vertexSource, fragmentSource, uniformValues) { 168 function compileSource(type, source) { 169 var shader = gl.createShader(type); 170 gl.shaderSource(shader, source); 171 gl.compileShader(shader); 172 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 173 throw new Error('compile error: ' + gl.getShaderInfoLog(shader)); 174 } 175 return shader; 176 } 177 178 var program = {}; 179 180 program.id = gl.createProgram(); 181 gl.attachShader(program.id, compileSource(gl.VERTEX_SHADER, vertexSource)); 182 gl.attachShader(program.id, compileSource(gl.FRAGMENT_SHADER, fragmentSource)); 183 gl.linkProgram(program.id); 184 if (!gl.getProgramParameter(program.id, gl.LINK_STATUS)) { 185 throw new Error('link error: ' + gl.getProgramInfoLog(program.id)); 186 } 187 188 // Fetch the uniform and attribute locations 189 program.uniforms = {}; 190 program.locations = {}; 191 gl.useProgram(program.id); 192 gl.enableVertexAttribArray(0); 193 var match, name, regex = /uniform (\w+) (\w+)/g, shaderCode = vertexSource + fragmentSource; 194 while ((match = regex.exec(shaderCode)) != null) { 195 name = match[2]; 196 program.locations[name] = gl.getUniformLocation(program.id, name); 197 } 198 199 return program; 200} 201 202function bindTexture(texture, unit) { 203 gl.activeTexture(gl.TEXTURE0 + (unit || 0)); 204 gl.bindTexture(gl.TEXTURE_2D, texture); 205} 206 207function extractUrl(value) { 208 var urlMatch = /url\(["']?([^"']*)["']?\)/.exec(value); 209 if (urlMatch == null) { 210 return null; 211 } 212 213 return urlMatch[1]; 214} 215 216function isDataUri(url) { 217 return url.match(/^data:/); 218} 219 220var config = loadConfig(); 221var transparentPixels = createImageData(32, 32); 222 223// Extend the css 224$('head').prepend('<style>.jquery-ripples { position: relative; z-index: 0; }</style>'); 225 226// RIPPLES CLASS DEFINITION 227// ========================= 228 229var Ripples = function (el, options) { 230 var that = this; 231 232 this.$el = $(el); 233 234 // Init properties from options 235 this.interactive = options.interactive; 236 this.resolution = options.resolution; 237 this.textureDelta = new Float32Array([1 / this.resolution, 1 / this.resolution]); 238 239 this.perturbance = options.perturbance; 240 this.dropRadius = options.dropRadius; 241 242 this.crossOrigin = options.crossOrigin; 243 this.imageUrl = options.imageUrl; 244 245 // Init WebGL canvas 246 var canvas = document.createElement('canvas'); 247 canvas.width = this.$el.innerWidth(); 248 canvas.height = this.$el.innerHeight(); 249 this.canvas = canvas; 250 this.$canvas = $(canvas); 251 this.$canvas.css({ 252 position: 'absolute', 253 left: 0, 254 top: 0, 255 right: 0, 256 bottom: 0, 257 zIndex: -1 258 }); 259 260 this.$el.addClass('jquery-ripples').append(canvas); 261 this.context = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 262 263 // Load extensions 264 config.extensions.forEach(function(name) { 265 gl.getExtension(name); 266 }); 267 268 // Auto-resize when window size changes. 269 $(window).on('resize', function() { 270 that.updateSize(); 271 }); 272 273 // Init rendertargets for ripple data. 274 this.textures = []; 275 this.framebuffers = []; 276 this.bufferWriteIndex = 0; 277 this.bufferReadIndex = 1; 278 279 var arrayType = config.arrayType; 280 var textureData = arrayType ? new arrayType(this.resolution * this.resolution * 4) : null; 281 282 for (var i = 0; i < 2; i++) { 283 var texture = gl.createTexture(); 284 var framebuffer = gl.createFramebuffer(); 285 286 gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 287 288 gl.bindTexture(gl.TEXTURE_2D, texture); 289 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST); 290 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST); 291 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 292 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 293 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.resolution, this.resolution, 0, gl.RGBA, config.type, textureData); 294 295 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 296 297 this.textures.push(texture); 298 this.framebuffers.push(framebuffer); 299 } 300 301 // Init GL stuff 302 this.quad = gl.createBuffer(); 303 gl.bindBuffer(gl.ARRAY_BUFFER, this.quad); 304 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 305 -1, -1, 306 +1, -1, 307 +1, +1, 308 -1, +1 309 ]), gl.STATIC_DRAW); 310 311 this.initShaders(); 312 this.initTexture(); 313 this.setTransparentTexture(); 314 315 // Load the image either from the options or CSS rules 316 this.loadImage(); 317 318 // Set correct clear color and blend mode (regular alpha blending) 319 gl.clearColor(0, 0, 0, 0); 320 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 321 322 // Plugin is successfully initialized! 323 this.visible = true; 324 this.running = true; 325 this.inited = true; 326 this.destroyed = false; 327 328 this.setupPointerEvents(); 329 330 // Init animation 331 function step() { 332 if (!that.destroyed) { 333 that.step(); 334 335 requestAnimationFrame(step); 336 } 337 } 338 339 requestAnimationFrame(step); 340}; 341 342Ripples.DEFAULTS = { 343 imageUrl: null, 344 resolution: 256, 345 dropRadius: 20, 346 perturbance: 0.03, 347 interactive: true, 348 crossOrigin: '' 349}; 350 351Ripples.prototype = { 352 353 // Set up pointer (mouse + touch) events 354 setupPointerEvents: function() { 355 var that = this; 356 357 function pointerEventsEnabled() { 358 return that.visible && that.running && that.interactive; 359 } 360 361 function dropAtPointer(pointer, big) { 362 if (pointerEventsEnabled()) { 363 that.dropAtPointer( 364 pointer, 365 that.dropRadius * (big ? 1.5 : 1), 366 (big ? 0.14 : 0.01) 367 ); 368 } 369 } 370 371 // Start listening to pointer events 372 this.$el 373 374 // Create regular, small ripples for mouse move and touch events... 375 .on('mousemove.ripples', function(e) { 376 dropAtPointer(e); 377 }) 378 .on('touchmove.ripples, touchstart.ripples', function(e) { 379 var touches = e.originalEvent.changedTouches; 380 for (var i = 0; i < touches.length; i++) { 381 dropAtPointer(touches[i]); 382 } 383 }) 384 385 // ...and only a big ripple on mouse down events. 386 .on('mousedown.ripples', function(e) { 387 dropAtPointer(e, true); 388 }); 389 }, 390 391 // Load the image either from the options or the element's CSS rules. 392 loadImage: function() { 393 var that = this; 394 395 gl = this.context; 396 397 var newImageSource = this.imageUrl || 398 extractUrl(this.originalCssBackgroundImage) || 399 extractUrl(this.$el.css('backgroundImage')); 400 401 // If image source is unchanged, don't reload it. 402 if (newImageSource == this.imageSource) { 403 return; 404 } 405 406 this.imageSource = newImageSource; 407 408 // Falsy source means no background. 409 if (!this.imageSource) { 410 this.setTransparentTexture(); 411 return; 412 } 413 414 // Load the texture from a new image. 415 var image = new Image; 416 image.onload = function() { 417 gl = that.context; 418 419 // Only textures with dimensions of powers of two can have repeat wrapping. 420 function isPowerOfTwo(x) { 421 return (x & (x - 1)) == 0; 422 } 423 424 var wrapping = (isPowerOfTwo(image.width) && isPowerOfTwo(image.height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE; 425 426 gl.bindTexture(gl.TEXTURE_2D, that.backgroundTexture); 427 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapping); 428 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapping); 429 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 430 431 that.backgroundWidth = image.width; 432 that.backgroundHeight = image.height; 433 434 // Hide the background that we're replacing. 435 that.hideCssBackground(); 436 }; 437 438 // Fall back to a transparent texture when loading the image failed. 439 image.onerror = function() { 440 gl = that.context; 441 442 that.setTransparentTexture(); 443 }; 444 445 // Disable CORS when the image source is a data URI. 446 image.crossOrigin = isDataUri(this.imageSource) ? null : this.crossOrigin; 447 448 image.src = this.imageSource; 449 }, 450 451 step: function() { 452 gl = this.context; 453 454 if (!this.visible) { 455 return; 456 } 457 458 this.computeTextureBoundaries(); 459 460 if (this.running) { 461 this.update(); 462 } 463 464 this.render(); 465 }, 466 467 drawQuad: function() { 468 gl.bindBuffer(gl.ARRAY_BUFFER, this.quad); 469 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 470 gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); 471 }, 472 473 render: function() { 474 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 475 476 gl.viewport(0, 0, this.canvas.width, this.canvas.height); 477 478 gl.enable(gl.BLEND); 479 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 480 481 gl.useProgram(this.renderProgram.id); 482 483 bindTexture(this.backgroundTexture, 0); 484 bindTexture(this.textures[0], 1); 485 486 gl.uniform1f(this.renderProgram.locations.perturbance, this.perturbance); 487 gl.uniform2fv(this.renderProgram.locations.topLeft, this.renderProgram.uniforms.topLeft); 488 gl.uniform2fv(this.renderProgram.locations.bottomRight, this.renderProgram.uniforms.bottomRight); 489 gl.uniform2fv(this.renderProgram.locations.containerRatio, this.renderProgram.uniforms.containerRatio); 490 gl.uniform1i(this.renderProgram.locations.samplerBackground, 0); 491 gl.uniform1i(this.renderProgram.locations.samplerRipples, 1); 492 493 this.drawQuad(); 494 gl.disable(gl.BLEND); 495 }, 496 497 update: function() { 498 gl.viewport(0, 0, this.resolution, this.resolution); 499 500 gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]); 501 bindTexture(this.textures[this.bufferReadIndex]); 502 gl.useProgram(this.updateProgram.id); 503 504 this.drawQuad(); 505 506 this.swapBufferIndices(); 507 }, 508 509 swapBufferIndices: function() { 510 this.bufferWriteIndex = 1 - this.bufferWriteIndex; 511 this.bufferReadIndex = 1 - this.bufferReadIndex; 512 }, 513 514 computeTextureBoundaries: function() { 515 var backgroundSize = this.$el.css('background-size'); 516 var backgroundAttachment = this.$el.css('background-attachment'); 517 var backgroundPosition = translateBackgroundPosition(this.$el.css('background-position')); 518 519 // Here the 'container' is the element which the background adapts to 520 // (either the chrome window or some element, depending on attachment) 521 var container; 522 if (backgroundAttachment == 'fixed') { 523 container = { left: window.pageXOffset, top: window.pageYOffset }; 524 container.width = $window.width(); 525 container.height = $window.height(); 526 } 527 else { 528 container = this.$el.offset(); 529 container.width = this.$el.innerWidth(); 530 container.height = this.$el.innerHeight(); 531 } 532 533 // TODO: background-clip 534 if (backgroundSize == 'cover') { 535 var scale = Math.max(container.width / this.backgroundWidth, container.height / this.backgroundHeight); 536 537 var backgroundWidth = this.backgroundWidth * scale; 538 var backgroundHeight = this.backgroundHeight * scale; 539 } 540 else if (backgroundSize == 'contain') { 541 var scale = Math.min(container.width / this.backgroundWidth, container.height / this.backgroundHeight); 542 543 var backgroundWidth = this.backgroundWidth * scale; 544 var backgroundHeight = this.backgroundHeight * scale; 545 } 546 else { 547 backgroundSize = backgroundSize.split(' '); 548 var backgroundWidth = backgroundSize[0] || ''; 549 var backgroundHeight = backgroundSize[1] || backgroundWidth; 550 551 if (isPercentage(backgroundWidth)) { 552 backgroundWidth = container.width * parseFloat(backgroundWidth) / 100; 553 } 554 else if (backgroundWidth != 'auto') { 555 backgroundWidth = parseFloat(backgroundWidth); 556 } 557 558 if (isPercentage(backgroundHeight)) { 559 backgroundHeight = container.height * parseFloat(backgroundHeight) / 100; 560 } 561 else if (backgroundHeight != 'auto') { 562 backgroundHeight = parseFloat(backgroundHeight); 563 } 564 565 if (backgroundWidth == 'auto' && backgroundHeight == 'auto') { 566 backgroundWidth = this.backgroundWidth; 567 backgroundHeight = this.backgroundHeight; 568 } 569 else { 570 if (backgroundWidth == 'auto') { 571 backgroundWidth = this.backgroundWidth * (backgroundHeight / this.backgroundHeight); 572 } 573 574 if (backgroundHeight == 'auto') { 575 backgroundHeight = this.backgroundHeight * (backgroundWidth / this.backgroundWidth); 576 } 577 } 578 } 579 580 // Compute backgroundX and backgroundY in page coordinates 581 var backgroundX = backgroundPosition[0]; 582 var backgroundY = backgroundPosition[1]; 583 584 if (isPercentage(backgroundX)) { 585 backgroundX = container.left + (container.width - backgroundWidth) * parseFloat(backgroundX) / 100; 586 } 587 else { 588 backgroundX = container.left + parseFloat(backgroundX); 589 } 590 591 if (isPercentage(backgroundY)) { 592 backgroundY = container.top + (container.height - backgroundHeight) * parseFloat(backgroundY) / 100; 593 } 594 else { 595 backgroundY = container.top + parseFloat(backgroundY); 596 } 597 598 var elementOffset = this.$el.offset(); 599 600 this.renderProgram.uniforms.topLeft = new Float32Array([ 601 (elementOffset.left - backgroundX) / backgroundWidth, 602 (elementOffset.top - backgroundY) / backgroundHeight 603 ]); 604 this.renderProgram.uniforms.bottomRight = new Float32Array([ 605 this.renderProgram.uniforms.topLeft[0] + this.$el.innerWidth() / backgroundWidth, 606 this.renderProgram.uniforms.topLeft[1] + this.$el.innerHeight() / backgroundHeight 607 ]); 608 609 var maxSide = Math.max(this.canvas.width, this.canvas.height); 610 611 this.renderProgram.uniforms.containerRatio = new Float32Array([ 612 this.canvas.width / maxSide, 613 this.canvas.height / maxSide 614 ]); 615 }, 616 617 initShaders: function() { 618 var vertexShader = [ 619 'attribute vec2 vertex;', 620 'varying vec2 coord;', 621 'void main() {', 622 'coord = vertex * 0.5 + 0.5;', 623 'gl_Position = vec4(vertex, 0.0, 1.0);', 624 '}' 625 ].join('\n'); 626 627 this.dropProgram = createProgram(vertexShader, [ 628 'precision highp float;', 629 630 'const float PI = 3.141592653589793;', 631 'uniform sampler2D texture;', 632 'uniform vec2 center;', 633 'uniform float radius;', 634 'uniform float strength;', 635 636 'varying vec2 coord;', 637 638 'void main() {', 639 'vec4 info = texture2D(texture, coord);', 640 641 'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);', 642 'drop = 0.5 - cos(drop * PI) * 0.5;', 643 644 'info.r += drop * strength;', 645 646 'gl_FragColor = info;', 647 '}' 648 ].join('\n')); 649 650 this.updateProgram = createProgram(vertexShader, [ 651 'precision highp float;', 652 653 'uniform sampler2D texture;', 654 'uniform vec2 delta;', 655 656 'varying vec2 coord;', 657 658 'void main() {', 659 'vec4 info = texture2D(texture, coord);', 660 661 'vec2 dx = vec2(delta.x, 0.0);', 662 'vec2 dy = vec2(0.0, delta.y);', 663 664 'float average = (', 665 'texture2D(texture, coord - dx).r +', 666 'texture2D(texture, coord - dy).r +', 667 'texture2D(texture, coord + dx).r +', 668 'texture2D(texture, coord + dy).r', 669 ') * 0.25;', 670 671 'info.g += (average - info.r) * 2.0;', 672 'info.g *= 0.995;', 673 'info.r += info.g;', 674 675 'gl_FragColor = info;', 676 '}' 677 ].join('\n')); 678 gl.uniform2fv(this.updateProgram.locations.delta, this.textureDelta); 679 680 this.renderProgram = createProgram([ 681 'precision highp float;', 682 683 'attribute vec2 vertex;', 684 'uniform vec2 topLeft;', 685 'uniform vec2 bottomRight;', 686 'uniform vec2 containerRatio;', 687 'varying vec2 ripplesCoord;', 688 'varying vec2 backgroundCoord;', 689 'void main() {', 690 'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);', 691 'backgroundCoord.y = 1.0 - backgroundCoord.y;', 692 'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;', 693 'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);', 694 '}' 695 ].join('\n'), [ 696 'precision highp float;', 697 698 'uniform sampler2D samplerBackground;', 699 'uniform sampler2D samplerRipples;', 700 'uniform vec2 delta;', 701 702 'uniform float perturbance;', 703 'varying vec2 ripplesCoord;', 704 'varying vec2 backgroundCoord;', 705 706 'void main() {', 707 'float height = texture2D(samplerRipples, ripplesCoord).r;', 708 'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;', 709 'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;', 710 'vec3 dx = vec3(delta.x, heightX - height, 0.0);', 711 'vec3 dy = vec3(0.0, heightY - height, delta.y);', 712 'vec2 offset = -normalize(cross(dy, dx)).xz;', 713 'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);', 714 'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;', 715 '}' 716 ].join('\n')); 717 gl.uniform2fv(this.renderProgram.locations.delta, this.textureDelta); 718 }, 719 720 initTexture: function() { 721 this.backgroundTexture = gl.createTexture(); 722 gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture); 723 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); 724 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 725 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 726 }, 727 728 setTransparentTexture: function() { 729 gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture); 730 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, transparentPixels); 731 }, 732 733 hideCssBackground: function() { 734 735 // Check whether we're changing inline CSS or overriding a global CSS rule. 736 var inlineCss = this.$el[0].style.backgroundImage; 737 738 if (inlineCss == 'none') { 739 return; 740 } 741 742 this.originalInlineCss = inlineCss; 743 744 this.originalCssBackgroundImage = this.$el.css('backgroundImage'); 745 this.$el.css('backgroundImage', 'none'); 746 }, 747 748 restoreCssBackground: function() { 749 750 // Restore background by either changing the inline CSS rule to what it was, or 751 // simply remove the inline CSS rule if it never was inlined. 752 this.$el.css('backgroundImage', this.originalInlineCss || ''); 753 }, 754 755 dropAtPointer: function(pointer, radius, strength) { 756 var borderLeft = parseInt(this.$el.css('border-left-width')) || 0, 757 borderTop = parseInt(this.$el.css('border-top-width')) || 0; 758 759 this.drop( 760 pointer.pageX - this.$el.offset().left - borderLeft, 761 pointer.pageY - this.$el.offset().top - borderTop, 762 radius, 763 strength 764 ); 765 }, 766 767 /** 768 * Public methods 769 */ 770 drop: function(x, y, radius, strength) { 771 gl = this.context; 772 773 var elWidth = this.$el.innerWidth(); 774 var elHeight = this.$el.innerHeight(); 775 var longestSide = Math.max(elWidth, elHeight); 776 777 radius = radius / longestSide; 778 779 var dropPosition = new Float32Array([ 780 (2 * x - elWidth) / longestSide, 781 (elHeight - 2 * y) / longestSide 782 ]); 783 784 gl.viewport(0, 0, this.resolution, this.resolution); 785 786 gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]); 787 bindTexture(this.textures[this.bufferReadIndex]); 788 789 gl.useProgram(this.dropProgram.id); 790 gl.uniform2fv(this.dropProgram.locations.center, dropPosition); 791 gl.uniform1f(this.dropProgram.locations.radius, radius); 792 gl.uniform1f(this.dropProgram.locations.strength, strength); 793 794 this.drawQuad(); 795 796 this.swapBufferIndices(); 797 }, 798 799 updateSize: function() { 800 var newWidth = this.$el.innerWidth(), 801 newHeight = this.$el.innerHeight(); 802 803 if (newWidth != this.canvas.width || newHeight != this.canvas.height) { 804 this.canvas.width = newWidth; 805 this.canvas.height = newHeight; 806 } 807 }, 808 809 destroy: function() { 810 this.$el 811 .off('.ripples') 812 .removeClass('jquery-ripples') 813 .removeData('ripples'); 814 815 this.$canvas.remove(); 816 this.restoreCssBackground(); 817 818 this.destroyed = true; 819 }, 820 821 show: function() { 822 this.visible = true; 823 824 this.$canvas.show(); 825 this.hideCssBackground(); 826 }, 827 828 hide: function() { 829 this.visible = false; 830 831 this.$canvas.hide(); 832 this.restoreCssBackground(); 833 }, 834 835 pause: function() { 836 this.running = false; 837 }, 838 839 play: function() { 840 this.running = true; 841 }, 842 843 set: function(property, value) { 844 switch (property) { 845 case 'dropRadius': 846 case 'perturbance': 847 case 'interactive': 848 case 'crossOrigin': 849 this[property] = value; 850 break; 851 case 'imageUrl': 852 this.imageUrl = value; 853 this.loadImage(); 854 break; 855 } 856 } 857}; 858 859// RIPPLES PLUGIN DEFINITION 860// ========================== 861 862var old = $.fn.ripples; 863 864$.fn.ripples = function(option) { 865 if (!config) { 866 throw new Error('Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.'); 867 } 868 869 var args = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined; 870 871 return this.each(function() { 872 var $this = $(this), 873 data = $this.data('ripples'), 874 options = $.extend({}, Ripples.DEFAULTS, $this.data(), typeof option == 'object' && option); 875 876 if (!data && typeof option == 'string') { 877 return; 878 } 879 if (!data) { 880 $this.data('ripples', (data = new Ripples(this, options))); 881 } 882 else if (typeof option == 'string') { 883 Ripples.prototype[option].apply(data, args); 884 } 885 }); 886}; 887 888$.fn.ripples.Constructor = Ripples; 889 890 891// RIPPLES NO CONFLICT 892// ==================== 893 894$.fn.ripples.noConflict = function() { 895 $.fn.ripples = old; 896 return this; 897}; 898 899})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.