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