Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
CivWeb
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ReactWeb5
CivWeb
Commits
0394433d
Commit
0394433d
authored
Aug 22, 2023
by
张瑶
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: edit conflict
parent
73c6bb47
Pipeline
#77817
passed with stages
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
967 additions
and
1012 deletions
+967
-1012
jquery.ripples.js
src/assets/js/ripples/jquery.ripples.js
+876
-892
ripples.js
src/assets/js/ripples/ripples.js
+91
-120
No files found.
src/assets/js/ripples/jquery.ripples.js
View file @
0394433d
...
...
@@ -4,900 +4,884 @@
* @author sirxemic / https://sirxemic.com/
*/
(
function
(
global
,
factory
)
{
typeof
exports
===
'object'
&&
typeof
module
!==
'undefined'
?
factory
(
require
(
'jquery'
))
:
typeof
define
===
'function'
&&
define
.
amd
?
define
([
'jquery'
],
factory
)
:
(
factory
(
global
.
$
));
}(
this
,
(
function
(
$
)
{
'use strict'
;
(
function
(
global
,
factory
)
{
typeof
exports
===
'object'
&&
typeof
module
!==
'undefined'
?
factory
(
require
(
'jquery'
))
:
typeof
define
===
'function'
&&
define
.
amd
?
define
([
'jquery'
],
factory
)
:
factory
(
global
.
$
);
})(
this
,
function
(
$
)
{
$
=
$
&&
'default'
in
$
?
$
.
default
:
$
;
let
gl
;
const
$window
=
$
(
window
);
// There is only one window, so why not cache the jQuery-wrapped window?
function
isPercentage
(
str
)
{
return
str
[
str
.
length
-
1
]
==
'%'
;
}
/**
* Load a configuration of GL settings which the browser supports.
* For example:
* - not all browsers support WebGL
* - not all browsers support floating point textures
* - not all browsers support linear filtering for floating point textures
* - not all browsers support rendering to floating point textures
* - some browsers *do* support rendering to half-floating point textures instead.
*/
function
loadConfig
()
{
const
canvas
=
document
.
createElement
(
'canvas'
);
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
if
(
!
gl
)
{
// Browser does not support WebGL.
return
null
;
}
// Load extensions
const
extensions
=
{};
[
'OES_texture_float'
,
'OES_texture_half_float'
,
'OES_texture_float_linear'
,
'OES_texture_half_float_linear'
,
].
forEach
(
function
(
name
)
{
const
extension
=
gl
.
getExtension
(
name
);
if
(
extension
)
{
extensions
[
name
]
=
extension
;
}
});
// If no floating point extensions are supported we can bail out early.
if
(
!
extensions
.
OES_texture_float
)
{
return
null
;
}
const
configs
=
[];
function
createConfig
(
type
,
glType
,
arrayType
)
{
const
name
=
`OES_texture_
${
type
}
`
;
const
nameLinear
=
`
${
name
}
_linear`
;
const
linearSupport
=
nameLinear
in
extensions
;
const
configExtensions
=
[
name
];
if
(
linearSupport
)
{
configExtensions
.
push
(
nameLinear
);
}
return
{
type
:
glType
,
arrayType
,
linearSupport
,
extensions
:
configExtensions
,
};
}
configs
.
push
(
createConfig
(
'float'
,
gl
.
FLOAT
,
Float32Array
));
if
(
extensions
.
OES_texture_half_float
)
{
configs
.
push
(
// Array type should be Uint16Array, but at least on iOS that breaks. In that case we
// just initialize the textures with data=null, instead of data=new Uint16Array(...).
// This makes initialization a tad slower, but it's still negligible.
createConfig
(
'half_float'
,
extensions
.
OES_texture_half_float
.
HALF_FLOAT_OES
,
null
),
);
}
// Setup the texture and framebuffer
const
texture
=
gl
.
createTexture
();
const
framebuffer
=
gl
.
createFramebuffer
();
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
framebuffer
);
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
gl
.
CLAMP_TO_EDGE
);
// Check for each supported texture type if rendering to it is supported
let
config
=
null
;
for
(
let
i
=
0
;
i
<
configs
.
length
;
i
++
)
{
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
32
,
32
,
0
,
gl
.
RGBA
,
configs
[
i
].
type
,
null
);
gl
.
framebufferTexture2D
(
gl
.
FRAMEBUFFER
,
gl
.
COLOR_ATTACHMENT0
,
gl
.
TEXTURE_2D
,
texture
,
0
);
if
(
gl
.
checkFramebufferStatus
(
gl
.
FRAMEBUFFER
)
===
gl
.
FRAMEBUFFER_COMPLETE
)
{
config
=
configs
[
i
];
break
;
}
}
return
config
;
}
function
createImageData
(
width
,
height
)
{
try
{
return
new
ImageData
(
width
,
height
);
}
catch
(
e
)
{
// Fallback for IE
const
canvas
=
document
.
createElement
(
'canvas'
);
return
canvas
.
getContext
(
'2d'
).
createImageData
(
width
,
height
);
}
}
function
translateBackgroundPosition
(
value
)
{
const
parts
=
value
.
split
(
' '
);
if
(
parts
.
length
===
1
)
{
switch
(
value
)
{
case
'center'
:
return
[
'50%'
,
'50%'
];
case
'top'
:
return
[
'50%'
,
'0'
];
case
'bottom'
:
return
[
'50%'
,
'100%'
];
case
'left'
:
return
[
'0'
,
'50%'
];
case
'right'
:
return
[
'100%'
,
'50%'
];
default
:
return
[
value
,
'50%'
];
}
}
else
{
return
parts
.
map
(
function
(
part
)
{
switch
(
value
)
{
case
'center'
:
return
'50%'
;
case
'top'
:
case
'left'
:
return
'0'
;
case
'right'
:
case
'bottom'
:
return
'100%'
;
default
:
return
part
;
}
});
}
}
function
createProgram
(
vertexSource
,
fragmentSource
,
uniformValues
)
{
function
compileSource
(
type
,
source
)
{
const
shader
=
gl
.
createShader
(
type
);
gl
.
shaderSource
(
shader
,
source
);
gl
.
compileShader
(
shader
);
if
(
!
gl
.
getShaderParameter
(
shader
,
gl
.
COMPILE_STATUS
))
{
throw
new
Error
(
`compile error:
${
gl
.
getShaderInfoLog
(
shader
)}
`
);
}
return
shader
;
}
const
program
=
{};
program
.
id
=
gl
.
createProgram
();
gl
.
attachShader
(
program
.
id
,
compileSource
(
gl
.
VERTEX_SHADER
,
vertexSource
));
gl
.
attachShader
(
program
.
id
,
compileSource
(
gl
.
FRAGMENT_SHADER
,
fragmentSource
));
gl
.
linkProgram
(
program
.
id
);
if
(
!
gl
.
getProgramParameter
(
program
.
id
,
gl
.
LINK_STATUS
))
{
throw
new
Error
(
`link error:
${
gl
.
getProgramInfoLog
(
program
.
id
)}
`
);
}
// Fetch the uniform and attribute locations
program
.
uniforms
=
{};
program
.
locations
=
{};
gl
.
useProgram
(
program
.
id
);
gl
.
enableVertexAttribArray
(
0
);
let
match
;
let
name
;
const
regex
=
/uniform
(\w
+
)
(\w
+
)
/g
;
const
shaderCode
=
vertexSource
+
fragmentSource
;
while
((
match
=
regex
.
exec
(
shaderCode
))
!=
null
)
{
name
=
match
[
2
];
program
.
locations
[
name
]
=
gl
.
getUniformLocation
(
program
.
id
,
name
);
}
return
program
;
}
function
bindTexture
(
texture
,
unit
)
{
gl
.
activeTexture
(
gl
.
TEXTURE0
+
(
unit
||
0
));
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
}
function
extractUrl
(
value
)
{
const
urlMatch
=
/url
\([
"'
]?([^
"'
]
*
)[
"'
]?\)
/
.
exec
(
value
);
if
(
urlMatch
==
null
)
{
return
null
;
}
return
urlMatch
[
1
];
}
function
isDataUri
(
url
)
{
return
url
.
match
(
/^data:/
);
}
const
config
=
loadConfig
();
const
transparentPixels
=
createImageData
(
32
,
32
);
// Extend the css
$
(
'head'
).
prepend
(
'<style>.jquery-ripples { position: relative; z-index: 0; }</style>'
);
// RIPPLES CLASS DEFINITION
// =========================
const
Ripples
=
function
(
el
,
options
)
{
const
that
=
this
;
this
.
$el
=
$
(
el
);
// Init properties from options
this
.
interactive
=
options
.
interactive
;
this
.
resolution
=
options
.
resolution
;
this
.
textureDelta
=
new
Float32Array
([
1
/
this
.
resolution
,
1
/
this
.
resolution
]);
this
.
perturbance
=
options
.
perturbance
;
this
.
dropRadius
=
options
.
dropRadius
;
this
.
crossOrigin
=
options
.
crossOrigin
;
this
.
imageUrl
=
options
.
imageUrl
;
// Init WebGL canvas
const
canvas
=
document
.
createElement
(
'canvas'
);
canvas
.
width
=
this
.
$el
.
innerWidth
();
canvas
.
height
=
this
.
$el
.
innerHeight
();
this
.
canvas
=
canvas
;
this
.
$canvas
=
$
(
canvas
);
this
.
$canvas
.
css
({
position
:
'absolute'
,
left
:
0
,
top
:
0
,
right
:
0
,
bottom
:
0
,
zIndex
:
-
1
,
});
this
.
$el
.
addClass
(
'jquery-ripples'
).
append
(
canvas
);
this
.
context
=
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
// Load extensions
config
.
extensions
.
forEach
(
function
(
name
)
{
gl
.
getExtension
(
name
);
});
// Auto-resize when window size changes.
this
.
updateSize
=
this
.
updateSize
.
bind
(
this
);
$
(
window
).
on
(
'resize'
,
this
.
updateSize
);
// Init rendertargets for ripple data.
this
.
textures
=
[];
this
.
framebuffers
=
[];
this
.
bufferWriteIndex
=
0
;
this
.
bufferReadIndex
=
1
;
const
{
arrayType
}
=
config
;
const
textureData
=
arrayType
?
new
arrayType
(
this
.
resolution
*
this
.
resolution
*
4
)
:
null
;
for
(
let
i
=
0
;
i
<
2
;
i
++
)
{
const
texture
=
gl
.
createTexture
();
const
framebuffer
=
gl
.
createFramebuffer
();
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
framebuffer
);
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
config
.
linearSupport
?
gl
.
LINEAR
:
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
config
.
linearSupport
?
gl
.
LINEAR
:
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
this
.
resolution
,
this
.
resolution
,
0
,
gl
.
RGBA
,
config
.
type
,
textureData
);
gl
.
framebufferTexture2D
(
gl
.
FRAMEBUFFER
,
gl
.
COLOR_ATTACHMENT0
,
gl
.
TEXTURE_2D
,
texture
,
0
);
this
.
textures
.
push
(
texture
);
this
.
framebuffers
.
push
(
framebuffer
);
}
// Init GL stuff
this
.
quad
=
gl
.
createBuffer
();
gl
.
bindBuffer
(
gl
.
ARRAY_BUFFER
,
this
.
quad
);
gl
.
bufferData
(
gl
.
ARRAY_BUFFER
,
new
Float32Array
([
-
1
,
-
1
,
+
1
,
-
1
,
+
1
,
+
1
,
-
1
,
+
1
]),
gl
.
STATIC_DRAW
);
this
.
initShaders
();
this
.
initTexture
();
this
.
setTransparentTexture
();
// Load the image either from the options or CSS rules
this
.
loadImage
();
// Set correct clear color and blend mode (regular alpha blending)
gl
.
clearColor
(
0
,
0
,
0
,
0
);
gl
.
blendFunc
(
gl
.
SRC_ALPHA
,
gl
.
ONE_MINUS_SRC_ALPHA
);
// Plugin is successfully initialized!
this
.
visible
=
true
;
this
.
running
=
true
;
this
.
inited
=
true
;
this
.
destroyed
=
false
;
this
.
setupPointerEvents
();
// Init animation
function
step
()
{
if
(
!
that
.
destroyed
)
{
that
.
step
();
requestAnimationFrame
(
step
);
}
}
requestAnimationFrame
(
step
);
};
Ripples
.
DEFAULTS
=
{
imageUrl
:
null
,
resolution
:
256
,
dropRadius
:
20
,
perturbance
:
0.03
,
interactive
:
true
,
crossOrigin
:
''
,
};
Ripples
.
prototype
=
{
// Set up pointer (mouse + touch) events
setupPointerEvents
()
{
const
that
=
this
;
function
pointerEventsEnabled
()
{
return
that
.
visible
&&
that
.
running
&&
that
.
interactive
;
}
function
dropAtPointer
(
pointer
,
big
)
{
if
(
pointerEventsEnabled
())
{
that
.
dropAtPointer
(
pointer
,
that
.
dropRadius
*
(
big
?
1.5
:
1
),
big
?
0.14
:
0.01
);
}
}
// Start listening to pointer events
this
.
$el
// Create regular, small ripples for mouse move and touch events...
.
on
(
'mousemove.ripples'
,
function
(
e
)
{
dropAtPointer
(
e
);
})
.
on
(
'touchmove.ripples touchstart.ripples'
,
function
(
e
)
{
const
touches
=
e
.
originalEvent
.
changedTouches
;
for
(
let
i
=
0
;
i
<
touches
.
length
;
i
++
)
{
dropAtPointer
(
touches
[
i
]);
}
})
$
=
$
&&
'default'
in
$
?
$
[
'default'
]
:
$
;
// ...and only a big ripple on mouse down events.
.
on
(
'mousedown.ripples'
,
function
(
e
)
{
dropAtPointer
(
e
,
true
);
});
},
// Load the image either from the options or the element's CSS rules.
loadImage
()
{
const
that
=
this
;
gl
=
this
.
context
;
const
newImageSource
=
this
.
imageUrl
||
extractUrl
(
this
.
originalCssBackgroundImage
)
||
extractUrl
(
this
.
$el
.
css
(
'backgroundImage'
));
// If image source is unchanged, don't reload it.
if
(
newImageSource
==
this
.
imageSource
)
{
return
;
}
var
gl
;
var
$window
=
$
(
window
);
// There is only one window, so why not cache the jQuery-wrapped window?
this
.
imageSource
=
newImageSource
;
function
isPercentage
(
str
)
{
return
str
[
str
.
length
-
1
]
==
'%'
;
}
// Falsy source means no background.
if
(
!
this
.
imageSource
)
{
this
.
setTransparentTexture
();
return
;
}
/**
* Load a configuration of GL settings which the browser supports.
* For example:
* - not all browsers support WebGL
* - not all browsers support floating point textures
* - not all browsers support linear filtering for floating point textures
* - not all browsers support rendering to floating point textures
* - some browsers *do* support rendering to half-floating point textures instead.
*/
function
loadConfig
()
{
var
canvas
=
document
.
createElement
(
'canvas'
);
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
if
(
!
gl
)
{
// Browser does not support WebGL.
return
null
;
}
// Load extensions
var
extensions
=
{};
[
'OES_texture_float'
,
'OES_texture_half_float'
,
'OES_texture_float_linear'
,
'OES_texture_half_float_linear'
].
forEach
(
function
(
name
)
{
var
extension
=
gl
.
getExtension
(
name
);
if
(
extension
)
{
extensions
[
name
]
=
extension
;
}
});
// If no floating point extensions are supported we can bail out early.
if
(
!
extensions
.
OES_texture_float
)
{
return
null
;
}
var
configs
=
[];
function
createConfig
(
type
,
glType
,
arrayType
)
{
var
name
=
'OES_texture_'
+
type
,
nameLinear
=
name
+
'_linear'
,
linearSupport
=
nameLinear
in
extensions
,
configExtensions
=
[
name
];
if
(
linearSupport
)
{
configExtensions
.
push
(
nameLinear
);
}
return
{
type
:
glType
,
arrayType
:
arrayType
,
linearSupport
:
linearSupport
,
extensions
:
configExtensions
};
}
configs
.
push
(
createConfig
(
'float'
,
gl
.
FLOAT
,
Float32Array
)
);
if
(
extensions
.
OES_texture_half_float
)
{
configs
.
push
(
// Array type should be Uint16Array, but at least on iOS that breaks. In that case we
// just initialize the textures with data=null, instead of data=new Uint16Array(...).
// This makes initialization a tad slower, but it's still negligible.
createConfig
(
'half_float'
,
extensions
.
OES_texture_half_float
.
HALF_FLOAT_OES
,
null
)
);
}
// Setup the texture and framebuffer
var
texture
=
gl
.
createTexture
();
var
framebuffer
=
gl
.
createFramebuffer
();
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
framebuffer
);
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
gl
.
CLAMP_TO_EDGE
);
// Check for each supported texture type if rendering to it is supported
var
config
=
null
;
for
(
var
i
=
0
;
i
<
configs
.
length
;
i
++
)
{
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
32
,
32
,
0
,
gl
.
RGBA
,
configs
[
i
].
type
,
null
);
gl
.
framebufferTexture2D
(
gl
.
FRAMEBUFFER
,
gl
.
COLOR_ATTACHMENT0
,
gl
.
TEXTURE_2D
,
texture
,
0
);
if
(
gl
.
checkFramebufferStatus
(
gl
.
FRAMEBUFFER
)
===
gl
.
FRAMEBUFFER_COMPLETE
)
{
config
=
configs
[
i
];
break
;
}
}
return
config
;
}
function
createImageData
(
width
,
height
)
{
try
{
return
new
ImageData
(
width
,
height
);
}
catch
(
e
)
{
// Fallback for IE
var
canvas
=
document
.
createElement
(
'canvas'
);
return
canvas
.
getContext
(
'2d'
).
createImageData
(
width
,
height
);
}
}
function
translateBackgroundPosition
(
value
)
{
var
parts
=
value
.
split
(
' '
);
if
(
parts
.
length
===
1
)
{
switch
(
value
)
{
case
'center'
:
return
[
'50%'
,
'50%'
];
case
'top'
:
return
[
'50%'
,
'0'
];
case
'bottom'
:
return
[
'50%'
,
'100%'
];
case
'left'
:
return
[
'0'
,
'50%'
];
case
'right'
:
return
[
'100%'
,
'50%'
];
default
:
return
[
value
,
'50%'
];
}
}
else
{
return
parts
.
map
(
function
(
part
)
{
switch
(
value
)
{
case
'center'
:
return
'50%'
;
case
'top'
:
case
'left'
:
return
'0'
;
case
'right'
:
case
'bottom'
:
return
'100%'
;
default
:
return
part
;
}
});
}
}
function
createProgram
(
vertexSource
,
fragmentSource
,
uniformValues
)
{
function
compileSource
(
type
,
source
)
{
var
shader
=
gl
.
createShader
(
type
);
gl
.
shaderSource
(
shader
,
source
);
gl
.
compileShader
(
shader
);
if
(
!
gl
.
getShaderParameter
(
shader
,
gl
.
COMPILE_STATUS
))
{
throw
new
Error
(
'compile error: '
+
gl
.
getShaderInfoLog
(
shader
));
}
return
shader
;
}
var
program
=
{};
program
.
id
=
gl
.
createProgram
();
gl
.
attachShader
(
program
.
id
,
compileSource
(
gl
.
VERTEX_SHADER
,
vertexSource
));
gl
.
attachShader
(
program
.
id
,
compileSource
(
gl
.
FRAGMENT_SHADER
,
fragmentSource
));
gl
.
linkProgram
(
program
.
id
);
if
(
!
gl
.
getProgramParameter
(
program
.
id
,
gl
.
LINK_STATUS
))
{
throw
new
Error
(
'link error: '
+
gl
.
getProgramInfoLog
(
program
.
id
));
}
// Fetch the uniform and attribute locations
program
.
uniforms
=
{};
program
.
locations
=
{};
gl
.
useProgram
(
program
.
id
);
gl
.
enableVertexAttribArray
(
0
);
var
match
,
name
,
regex
=
/uniform
(\w
+
)
(\w
+
)
/g
,
shaderCode
=
vertexSource
+
fragmentSource
;
while
((
match
=
regex
.
exec
(
shaderCode
))
!=
null
)
{
name
=
match
[
2
];
program
.
locations
[
name
]
=
gl
.
getUniformLocation
(
program
.
id
,
name
);
}
return
program
;
}
function
bindTexture
(
texture
,
unit
)
{
gl
.
activeTexture
(
gl
.
TEXTURE0
+
(
unit
||
0
));
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
}
function
extractUrl
(
value
)
{
var
urlMatch
=
/url
\([
"'
]?([^
"'
]
*
)[
"'
]?\)
/
.
exec
(
value
);
if
(
urlMatch
==
null
)
{
return
null
;
}
return
urlMatch
[
1
];
}
function
isDataUri
(
url
)
{
return
url
.
match
(
/^data:/
);
}
var
config
=
loadConfig
();
var
transparentPixels
=
createImageData
(
32
,
32
);
// Extend the css
$
(
'head'
).
prepend
(
'<style>.jquery-ripples { position: relative; z-index: 0; }</style>'
);
// RIPPLES CLASS DEFINITION
// =========================
var
Ripples
=
function
(
el
,
options
)
{
var
that
=
this
;
this
.
$el
=
$
(
el
);
// Init properties from options
this
.
interactive
=
options
.
interactive
;
this
.
resolution
=
options
.
resolution
;
this
.
textureDelta
=
new
Float32Array
([
1
/
this
.
resolution
,
1
/
this
.
resolution
]);
this
.
perturbance
=
options
.
perturbance
;
this
.
dropRadius
=
options
.
dropRadius
;
this
.
crossOrigin
=
options
.
crossOrigin
;
this
.
imageUrl
=
options
.
imageUrl
;
// Init WebGL canvas
var
canvas
=
document
.
createElement
(
'canvas'
);
canvas
.
width
=
this
.
$el
.
innerWidth
();
canvas
.
height
=
this
.
$el
.
innerHeight
();
this
.
canvas
=
canvas
;
this
.
$canvas
=
$
(
canvas
);
this
.
$canvas
.
css
({
position
:
'absolute'
,
left
:
0
,
top
:
0
,
right
:
0
,
bottom
:
0
,
zIndex
:
-
1
});
this
.
$el
.
addClass
(
'jquery-ripples'
).
append
(
canvas
);
this
.
context
=
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
// Load extensions
config
.
extensions
.
forEach
(
function
(
name
)
{
gl
.
getExtension
(
name
);
});
// Auto-resize when window size changes.
this
.
updateSize
=
this
.
updateSize
.
bind
(
this
);
$
(
window
).
on
(
'resize'
,
this
.
updateSize
);
// Init rendertargets for ripple data.
this
.
textures
=
[];
this
.
framebuffers
=
[];
this
.
bufferWriteIndex
=
0
;
this
.
bufferReadIndex
=
1
;
var
arrayType
=
config
.
arrayType
;
var
textureData
=
arrayType
?
new
arrayType
(
this
.
resolution
*
this
.
resolution
*
4
)
:
null
;
for
(
var
i
=
0
;
i
<
2
;
i
++
)
{
var
texture
=
gl
.
createTexture
();
var
framebuffer
=
gl
.
createFramebuffer
();
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
framebuffer
);
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
texture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
config
.
linearSupport
?
gl
.
LINEAR
:
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
config
.
linearSupport
?
gl
.
LINEAR
:
gl
.
NEAREST
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
gl
.
CLAMP_TO_EDGE
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
this
.
resolution
,
this
.
resolution
,
0
,
gl
.
RGBA
,
config
.
type
,
textureData
);
gl
.
framebufferTexture2D
(
gl
.
FRAMEBUFFER
,
gl
.
COLOR_ATTACHMENT0
,
gl
.
TEXTURE_2D
,
texture
,
0
);
this
.
textures
.
push
(
texture
);
this
.
framebuffers
.
push
(
framebuffer
);
}
// Init GL stuff
this
.
quad
=
gl
.
createBuffer
();
gl
.
bindBuffer
(
gl
.
ARRAY_BUFFER
,
this
.
quad
);
gl
.
bufferData
(
gl
.
ARRAY_BUFFER
,
new
Float32Array
([
-
1
,
-
1
,
+
1
,
-
1
,
+
1
,
+
1
,
-
1
,
+
1
]),
gl
.
STATIC_DRAW
);
this
.
initShaders
();
this
.
initTexture
();
this
.
setTransparentTexture
();
// Load the image either from the options or CSS rules
this
.
loadImage
();
// Set correct clear color and blend mode (regular alpha blending)
gl
.
clearColor
(
0
,
0
,
0
,
0
);
gl
.
blendFunc
(
gl
.
SRC_ALPHA
,
gl
.
ONE_MINUS_SRC_ALPHA
);
// Plugin is successfully initialized!
this
.
visible
=
true
;
this
.
running
=
true
;
this
.
inited
=
true
;
this
.
destroyed
=
false
;
this
.
setupPointerEvents
();
// Init animation
function
step
()
{
if
(
!
that
.
destroyed
)
{
that
.
step
();
requestAnimationFrame
(
step
);
}
}
requestAnimationFrame
(
step
);
};
Ripples
.
DEFAULTS
=
{
imageUrl
:
null
,
resolution
:
256
,
dropRadius
:
20
,
perturbance
:
0.03
,
interactive
:
true
,
crossOrigin
:
''
};
Ripples
.
prototype
=
{
// Set up pointer (mouse + touch) events
setupPointerEvents
:
function
()
{
var
that
=
this
;
function
pointerEventsEnabled
()
{
return
that
.
visible
&&
that
.
running
&&
that
.
interactive
;
}
function
dropAtPointer
(
pointer
,
big
)
{
if
(
pointerEventsEnabled
())
{
that
.
dropAtPointer
(
pointer
,
that
.
dropRadius
*
(
big
?
1.5
:
1
),
(
big
?
0.14
:
0.01
)
);
}
}
// Start listening to pointer events
this
.
$el
// Create regular, small ripples for mouse move and touch events...
.
on
(
'mousemove.ripples'
,
function
(
e
)
{
dropAtPointer
(
e
);
})
.
on
(
'touchmove.ripples touchstart.ripples'
,
function
(
e
)
{
var
touches
=
e
.
originalEvent
.
changedTouches
;
for
(
var
i
=
0
;
i
<
touches
.
length
;
i
++
)
{
dropAtPointer
(
touches
[
i
]);
}
})
// ...and only a big ripple on mouse down events.
.
on
(
'mousedown.ripples'
,
function
(
e
)
{
dropAtPointer
(
e
,
true
);
});
},
// Load the image either from the options or the element's CSS rules.
loadImage
:
function
()
{
var
that
=
this
;
gl
=
this
.
context
;
var
newImageSource
=
this
.
imageUrl
||
extractUrl
(
this
.
originalCssBackgroundImage
)
||
extractUrl
(
this
.
$el
.
css
(
'backgroundImage'
));
// If image source is unchanged, don't reload it.
if
(
newImageSource
==
this
.
imageSource
)
{
return
;
}
this
.
imageSource
=
newImageSource
;
// Falsy source means no background.
if
(
!
this
.
imageSource
)
{
this
.
setTransparentTexture
();
return
;
}
// Load the texture from a new image.
var
image
=
new
Image
;
image
.
onload
=
function
()
{
gl
=
that
.
context
;
// Only textures with dimensions of powers of two can have repeat wrapping.
function
isPowerOfTwo
(
x
)
{
return
(
x
&
(
x
-
1
))
==
0
;
}
var
wrapping
=
(
isPowerOfTwo
(
image
.
width
)
&&
isPowerOfTwo
(
image
.
height
))
?
gl
.
REPEAT
:
gl
.
CLAMP_TO_EDGE
;
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
that
.
backgroundTexture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
wrapping
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
wrapping
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
gl
.
RGBA
,
gl
.
UNSIGNED_BYTE
,
image
);
that
.
backgroundWidth
=
image
.
width
;
that
.
backgroundHeight
=
image
.
height
;
// Hide the background that we're replacing.
that
.
hideCssBackground
();
};
// Fall back to a transparent texture when loading the image failed.
image
.
onerror
=
function
()
{
gl
=
that
.
context
;
that
.
setTransparentTexture
();
};
// Disable CORS when the image source is a data URI.
image
.
crossOrigin
=
isDataUri
(
this
.
imageSource
)
?
null
:
this
.
crossOrigin
;
image
.
src
=
this
.
imageSource
;
},
step
:
function
()
{
gl
=
this
.
context
;
if
(
!
this
.
visible
)
{
return
;
}
this
.
computeTextureBoundaries
();
if
(
this
.
running
)
{
this
.
update
();
}
this
.
render
();
},
drawQuad
:
function
()
{
gl
.
bindBuffer
(
gl
.
ARRAY_BUFFER
,
this
.
quad
);
gl
.
vertexAttribPointer
(
0
,
2
,
gl
.
FLOAT
,
false
,
0
,
0
);
gl
.
drawArrays
(
gl
.
TRIANGLE_FAN
,
0
,
4
);
},
render
:
function
()
{
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
null
);
gl
.
viewport
(
0
,
0
,
this
.
canvas
.
width
,
this
.
canvas
.
height
);
gl
.
enable
(
gl
.
BLEND
);
gl
.
clear
(
gl
.
COLOR_BUFFER_BIT
|
gl
.
DEPTH_BUFFER_BIT
);
gl
.
useProgram
(
this
.
renderProgram
.
id
);
bindTexture
(
this
.
backgroundTexture
,
0
);
bindTexture
(
this
.
textures
[
0
],
1
);
gl
.
uniform1f
(
this
.
renderProgram
.
locations
.
perturbance
,
this
.
perturbance
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
topLeft
,
this
.
renderProgram
.
uniforms
.
topLeft
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
bottomRight
,
this
.
renderProgram
.
uniforms
.
bottomRight
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
containerRatio
,
this
.
renderProgram
.
uniforms
.
containerRatio
);
gl
.
uniform1i
(
this
.
renderProgram
.
locations
.
samplerBackground
,
0
);
gl
.
uniform1i
(
this
.
renderProgram
.
locations
.
samplerRipples
,
1
);
this
.
drawQuad
();
gl
.
disable
(
gl
.
BLEND
);
},
update
:
function
()
{
gl
.
viewport
(
0
,
0
,
this
.
resolution
,
this
.
resolution
);
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
this
.
framebuffers
[
this
.
bufferWriteIndex
]);
bindTexture
(
this
.
textures
[
this
.
bufferReadIndex
]);
gl
.
useProgram
(
this
.
updateProgram
.
id
);
this
.
drawQuad
();
this
.
swapBufferIndices
();
},
swapBufferIndices
:
function
()
{
this
.
bufferWriteIndex
=
1
-
this
.
bufferWriteIndex
;
this
.
bufferReadIndex
=
1
-
this
.
bufferReadIndex
;
},
computeTextureBoundaries
:
function
()
{
var
backgroundSize
=
this
.
$el
.
css
(
'background-size'
);
var
backgroundAttachment
=
this
.
$el
.
css
(
'background-attachment'
);
var
backgroundPosition
=
translateBackgroundPosition
(
this
.
$el
.
css
(
'background-position'
));
// Here the 'container' is the element which the background adapts to
// (either the chrome window or some element, depending on attachment)
var
container
;
if
(
backgroundAttachment
==
'fixed'
)
{
container
=
{
left
:
window
.
pageXOffset
,
top
:
window
.
pageYOffset
};
container
.
width
=
$window
.
width
();
container
.
height
=
$window
.
height
();
}
else
{
container
=
this
.
$el
.
offset
();
container
.
width
=
this
.
$el
.
innerWidth
();
container
.
height
=
this
.
$el
.
innerHeight
();
}
// TODO: background-clip
if
(
backgroundSize
==
'cover'
)
{
var
scale
=
Math
.
max
(
container
.
width
/
this
.
backgroundWidth
,
container
.
height
/
this
.
backgroundHeight
);
var
backgroundWidth
=
this
.
backgroundWidth
*
scale
;
var
backgroundHeight
=
this
.
backgroundHeight
*
scale
;
}
else
if
(
backgroundSize
==
'contain'
)
{
var
scale
=
Math
.
min
(
container
.
width
/
this
.
backgroundWidth
,
container
.
height
/
this
.
backgroundHeight
);
var
backgroundWidth
=
this
.
backgroundWidth
*
scale
;
var
backgroundHeight
=
this
.
backgroundHeight
*
scale
;
}
else
{
backgroundSize
=
backgroundSize
.
split
(
' '
);
var
backgroundWidth
=
backgroundSize
[
0
]
||
''
;
var
backgroundHeight
=
backgroundSize
[
1
]
||
backgroundWidth
;
if
(
isPercentage
(
backgroundWidth
))
{
backgroundWidth
=
container
.
width
*
parseFloat
(
backgroundWidth
)
/
100
;
}
else
if
(
backgroundWidth
!=
'auto'
)
{
backgroundWidth
=
parseFloat
(
backgroundWidth
);
}
if
(
isPercentage
(
backgroundHeight
))
{
backgroundHeight
=
container
.
height
*
parseFloat
(
backgroundHeight
)
/
100
;
}
else
if
(
backgroundHeight
!=
'auto'
)
{
backgroundHeight
=
parseFloat
(
backgroundHeight
);
}
if
(
backgroundWidth
==
'auto'
&&
backgroundHeight
==
'auto'
)
{
backgroundWidth
=
this
.
backgroundWidth
;
backgroundHeight
=
this
.
backgroundHeight
;
}
else
{
if
(
backgroundWidth
==
'auto'
)
{
backgroundWidth
=
this
.
backgroundWidth
*
(
backgroundHeight
/
this
.
backgroundHeight
);
}
if
(
backgroundHeight
==
'auto'
)
{
backgroundHeight
=
this
.
backgroundHeight
*
(
backgroundWidth
/
this
.
backgroundWidth
);
}
}
}
// Compute backgroundX and backgroundY in page coordinates
var
backgroundX
=
backgroundPosition
[
0
];
var
backgroundY
=
backgroundPosition
[
1
];
if
(
isPercentage
(
backgroundX
))
{
backgroundX
=
container
.
left
+
(
container
.
width
-
backgroundWidth
)
*
parseFloat
(
backgroundX
)
/
100
;
}
else
{
backgroundX
=
container
.
left
+
parseFloat
(
backgroundX
);
}
if
(
isPercentage
(
backgroundY
))
{
backgroundY
=
container
.
top
+
(
container
.
height
-
backgroundHeight
)
*
parseFloat
(
backgroundY
)
/
100
;
}
else
{
backgroundY
=
container
.
top
+
parseFloat
(
backgroundY
);
}
var
elementOffset
=
this
.
$el
.
offset
();
this
.
renderProgram
.
uniforms
.
topLeft
=
new
Float32Array
([
(
elementOffset
.
left
-
backgroundX
)
/
backgroundWidth
,
(
elementOffset
.
top
-
backgroundY
)
/
backgroundHeight
]);
this
.
renderProgram
.
uniforms
.
bottomRight
=
new
Float32Array
([
this
.
renderProgram
.
uniforms
.
topLeft
[
0
]
+
this
.
$el
.
innerWidth
()
/
backgroundWidth
,
this
.
renderProgram
.
uniforms
.
topLeft
[
1
]
+
this
.
$el
.
innerHeight
()
/
backgroundHeight
]);
var
maxSide
=
Math
.
max
(
this
.
canvas
.
width
,
this
.
canvas
.
height
);
this
.
renderProgram
.
uniforms
.
containerRatio
=
new
Float32Array
([
this
.
canvas
.
width
/
maxSide
,
this
.
canvas
.
height
/
maxSide
]);
},
initShaders
:
function
()
{
var
vertexShader
=
[
'attribute vec2 vertex;'
,
'varying vec2 coord;'
,
'void main() {'
,
'coord = vertex * 0.5 + 0.5;'
,
'gl_Position = vec4(vertex, 0.0, 1.0);'
,
'}'
].
join
(
'
\
n'
);
this
.
dropProgram
=
createProgram
(
vertexShader
,
[
'precision highp float;'
,
'const float PI = 3.141592653589793;'
,
'uniform sampler2D texture;'
,
'uniform vec2 center;'
,
'uniform float radius;'
,
'uniform float strength;'
,
'varying vec2 coord;'
,
'void main() {'
,
'vec4 info = texture2D(texture, coord);'
,
'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);'
,
'drop = 0.5 - cos(drop * PI) * 0.5;'
,
'info.r += drop * strength;'
,
'gl_FragColor = info;'
,
'}'
].
join
(
'
\
n'
));
this
.
updateProgram
=
createProgram
(
vertexShader
,
[
'precision highp float;'
,
'uniform sampler2D texture;'
,
'uniform vec2 delta;'
,
'varying vec2 coord;'
,
'void main() {'
,
'vec4 info = texture2D(texture, coord);'
,
'vec2 dx = vec2(delta.x, 0.0);'
,
'vec2 dy = vec2(0.0, delta.y);'
,
'float average = ('
,
'texture2D(texture, coord - dx).r +'
,
'texture2D(texture, coord - dy).r +'
,
'texture2D(texture, coord + dx).r +'
,
'texture2D(texture, coord + dy).r'
,
') * 0.25;'
,
'info.g += (average - info.r) * 2.0;'
,
'info.g *= 0.995;'
,
'info.r += info.g;'
,
'gl_FragColor = info;'
,
'}'
].
join
(
'
\
n'
));
gl
.
uniform2fv
(
this
.
updateProgram
.
locations
.
delta
,
this
.
textureDelta
);
this
.
renderProgram
=
createProgram
([
'precision highp float;'
,
'attribute vec2 vertex;'
,
'uniform vec2 topLeft;'
,
'uniform vec2 bottomRight;'
,
'uniform vec2 containerRatio;'
,
'varying vec2 ripplesCoord;'
,
'varying vec2 backgroundCoord;'
,
'void main() {'
,
'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);'
,
'backgroundCoord.y = 1.0 - backgroundCoord.y;'
,
'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;'
,
'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);'
,
'}'
].
join
(
'
\
n'
),
[
'precision highp float;'
,
'uniform sampler2D samplerBackground;'
,
'uniform sampler2D samplerRipples;'
,
'uniform vec2 delta;'
,
'uniform float perturbance;'
,
'varying vec2 ripplesCoord;'
,
'varying vec2 backgroundCoord;'
,
'void main() {'
,
'float height = texture2D(samplerRipples, ripplesCoord).r;'
,
'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;'
,
'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;'
,
'vec3 dx = vec3(delta.x, heightX - height, 0.0);'
,
'vec3 dy = vec3(0.0, heightY - height, delta.y);'
,
'vec2 offset = -normalize(cross(dy, dx)).xz;'
,
'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);'
,
'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;'
,
'}'
].
join
(
'
\
n'
));
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
delta
,
this
.
textureDelta
);
},
initTexture
:
function
()
{
this
.
backgroundTexture
=
gl
.
createTexture
();
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
this
.
backgroundTexture
);
gl
.
pixelStorei
(
gl
.
UNPACK_FLIP_Y_WEBGL
,
1
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
gl
.
LINEAR
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
gl
.
LINEAR
);
},
setTransparentTexture
:
function
()
{
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
this
.
backgroundTexture
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
gl
.
RGBA
,
gl
.
UNSIGNED_BYTE
,
transparentPixels
);
},
hideCssBackground
:
function
()
{
// Check whether we're changing inline CSS or overriding a global CSS rule.
var
inlineCss
=
this
.
$el
[
0
].
style
.
backgroundImage
;
if
(
inlineCss
==
'none'
)
{
return
;
}
this
.
originalInlineCss
=
inlineCss
;
this
.
originalCssBackgroundImage
=
this
.
$el
.
css
(
'backgroundImage'
);
this
.
$el
.
css
(
'backgroundImage'
,
'none'
);
},
restoreCssBackground
:
function
()
{
// Restore background by either changing the inline CSS rule to what it was, or
// simply remove the inline CSS rule if it never was inlined.
this
.
$el
.
css
(
'backgroundImage'
,
this
.
originalInlineCss
||
''
);
},
dropAtPointer
:
function
(
pointer
,
radius
,
strength
)
{
var
borderLeft
=
parseInt
(
this
.
$el
.
css
(
'border-left-width'
))
||
0
,
borderTop
=
parseInt
(
this
.
$el
.
css
(
'border-top-width'
))
||
0
;
this
.
drop
(
pointer
.
pageX
-
this
.
$el
.
offset
().
left
-
borderLeft
,
pointer
.
pageY
-
this
.
$el
.
offset
().
top
-
borderTop
,
radius
,
strength
);
},
/**
* Public methods
*/
drop
:
function
(
x
,
y
,
radius
,
strength
)
{
gl
=
this
.
context
;
var
elWidth
=
this
.
$el
.
innerWidth
();
var
elHeight
=
this
.
$el
.
innerHeight
();
var
longestSide
=
Math
.
max
(
elWidth
,
elHeight
);
radius
=
radius
/
longestSide
;
var
dropPosition
=
new
Float32Array
([
(
2
*
x
-
elWidth
)
/
longestSide
,
(
elHeight
-
2
*
y
)
/
longestSide
]);
gl
.
viewport
(
0
,
0
,
this
.
resolution
,
this
.
resolution
);
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
this
.
framebuffers
[
this
.
bufferWriteIndex
]);
bindTexture
(
this
.
textures
[
this
.
bufferReadIndex
]);
gl
.
useProgram
(
this
.
dropProgram
.
id
);
gl
.
uniform2fv
(
this
.
dropProgram
.
locations
.
center
,
dropPosition
);
gl
.
uniform1f
(
this
.
dropProgram
.
locations
.
radius
,
radius
);
gl
.
uniform1f
(
this
.
dropProgram
.
locations
.
strength
,
strength
);
this
.
drawQuad
();
this
.
swapBufferIndices
();
},
updateSize
:
function
()
{
var
newWidth
=
this
.
$el
.
innerWidth
(),
newHeight
=
this
.
$el
.
innerHeight
();
if
(
newWidth
!=
this
.
canvas
.
width
||
newHeight
!=
this
.
canvas
.
height
)
{
this
.
canvas
.
width
=
newWidth
;
this
.
canvas
.
height
=
newHeight
;
}
},
destroy
:
function
()
{
this
.
$el
.
off
(
'.ripples'
)
.
removeClass
(
'jquery-ripples'
)
.
removeData
(
'ripples'
);
// Make sure the last used context is garbage-collected
gl
=
null
;
$
(
window
).
off
(
'resize'
,
this
.
updateSize
);
this
.
$canvas
.
remove
();
this
.
restoreCssBackground
();
this
.
destroyed
=
true
;
},
show
:
function
()
{
this
.
visible
=
true
;
this
.
$canvas
.
show
();
this
.
hideCssBackground
();
},
hide
:
function
()
{
this
.
visible
=
false
;
this
.
$canvas
.
hide
();
this
.
restoreCssBackground
();
},
pause
:
function
()
{
this
.
running
=
false
;
},
play
:
function
()
{
this
.
running
=
true
;
},
set
:
function
(
property
,
value
)
{
switch
(
property
)
{
case
'dropRadius'
:
case
'perturbance'
:
case
'interactive'
:
case
'crossOrigin'
:
this
[
property
]
=
value
;
break
;
case
'imageUrl'
:
this
.
imageUrl
=
value
;
this
.
loadImage
();
break
;
}
}
};
// RIPPLES PLUGIN DEFINITION
// ==========================
var
old
=
$
.
fn
.
ripples
;
$
.
fn
.
ripples
=
function
(
option
)
{
if
(
!
config
)
{
throw
new
Error
(
'Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.'
);
}
var
args
=
(
arguments
.
length
>
1
)
?
Array
.
prototype
.
slice
.
call
(
arguments
,
1
)
:
undefined
;
// Load the texture from a new image.
const
image
=
new
Image
();
image
.
onload
=
function
()
{
gl
=
that
.
context
;
return
this
.
each
(
function
()
{
var
$this
=
$
(
this
),
data
=
$this
.
data
(
'ripples'
),
options
=
$
.
extend
({},
Ripples
.
DEFAULTS
,
$this
.
data
(),
typeof
option
==
'object'
&&
option
);
if
(
!
data
&&
typeof
option
==
'string'
)
{
return
;
}
if
(
!
data
)
{
$this
.
data
(
'ripples'
,
(
data
=
new
Ripples
(
this
,
options
)));
}
else
if
(
typeof
option
==
'string'
)
{
Ripples
.
prototype
[
option
].
apply
(
data
,
args
);
}
});
};
$
.
fn
.
ripples
.
Constructor
=
Ripples
;
// RIPPLES NO CONFLICT
// ====================
$
.
fn
.
ripples
.
noConflict
=
function
()
{
$
.
fn
.
ripples
=
old
;
return
this
;
};
})));
// Only textures with dimensions of powers of two can have repeat wrapping.
function
isPowerOfTwo
(
x
)
{
return
(
x
&
(
x
-
1
))
==
0
;
}
const
wrapping
=
isPowerOfTwo
(
image
.
width
)
&&
isPowerOfTwo
(
image
.
height
)
?
gl
.
REPEAT
:
gl
.
CLAMP_TO_EDGE
;
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
that
.
backgroundTexture
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_S
,
wrapping
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_WRAP_T
,
wrapping
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
gl
.
RGBA
,
gl
.
UNSIGNED_BYTE
,
image
);
that
.
backgroundWidth
=
image
.
width
;
that
.
backgroundHeight
=
image
.
height
;
// Hide the background that we're replacing.
that
.
hideCssBackground
();
};
// Fall back to a transparent texture when loading the image failed.
image
.
onerror
=
function
()
{
gl
=
that
.
context
;
that
.
setTransparentTexture
();
};
// Disable CORS when the image source is a data URI.
image
.
crossOrigin
=
isDataUri
(
this
.
imageSource
)
?
null
:
this
.
crossOrigin
;
image
.
src
=
this
.
imageSource
;
},
step
()
{
gl
=
this
.
context
;
if
(
!
this
.
visible
)
{
return
;
}
this
.
computeTextureBoundaries
();
if
(
this
.
running
)
{
this
.
update
();
}
this
.
render
();
},
drawQuad
()
{
gl
.
bindBuffer
(
gl
.
ARRAY_BUFFER
,
this
.
quad
);
gl
.
vertexAttribPointer
(
0
,
2
,
gl
.
FLOAT
,
false
,
0
,
0
);
gl
.
drawArrays
(
gl
.
TRIANGLE_FAN
,
0
,
4
);
},
render
()
{
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
null
);
gl
.
viewport
(
0
,
0
,
this
.
canvas
.
width
,
this
.
canvas
.
height
);
gl
.
enable
(
gl
.
BLEND
);
gl
.
clear
(
gl
.
COLOR_BUFFER_BIT
|
gl
.
DEPTH_BUFFER_BIT
);
gl
.
useProgram
(
this
.
renderProgram
.
id
);
bindTexture
(
this
.
backgroundTexture
,
0
);
bindTexture
(
this
.
textures
[
0
],
1
);
gl
.
uniform1f
(
this
.
renderProgram
.
locations
.
perturbance
,
this
.
perturbance
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
topLeft
,
this
.
renderProgram
.
uniforms
.
topLeft
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
bottomRight
,
this
.
renderProgram
.
uniforms
.
bottomRight
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
containerRatio
,
this
.
renderProgram
.
uniforms
.
containerRatio
);
gl
.
uniform1i
(
this
.
renderProgram
.
locations
.
samplerBackground
,
0
);
gl
.
uniform1i
(
this
.
renderProgram
.
locations
.
samplerRipples
,
1
);
this
.
drawQuad
();
gl
.
disable
(
gl
.
BLEND
);
},
update
()
{
gl
.
viewport
(
0
,
0
,
this
.
resolution
,
this
.
resolution
);
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
this
.
framebuffers
[
this
.
bufferWriteIndex
]);
bindTexture
(
this
.
textures
[
this
.
bufferReadIndex
]);
gl
.
useProgram
(
this
.
updateProgram
.
id
);
this
.
drawQuad
();
this
.
swapBufferIndices
();
},
swapBufferIndices
()
{
this
.
bufferWriteIndex
=
1
-
this
.
bufferWriteIndex
;
this
.
bufferReadIndex
=
1
-
this
.
bufferReadIndex
;
},
computeTextureBoundaries
()
{
let
backgroundSize
=
this
.
$el
.
css
(
'background-size'
);
const
backgroundAttachment
=
this
.
$el
.
css
(
'background-attachment'
);
const
backgroundPosition
=
translateBackgroundPosition
(
this
.
$el
.
css
(
'background-position'
));
// Here the 'container' is the element which the background adapts to
// (either the chrome window or some element, depending on attachment)
let
container
;
if
(
backgroundAttachment
==
'fixed'
)
{
container
=
{
left
:
window
.
pageXOffset
,
top
:
window
.
pageYOffset
};
container
.
width
=
$window
.
width
();
container
.
height
=
$window
.
height
();
}
else
{
container
=
this
.
$el
.
offset
();
container
.
width
=
this
.
$el
.
innerWidth
();
container
.
height
=
this
.
$el
.
innerHeight
();
}
// TODO: background-clip
if
(
backgroundSize
==
'cover'
)
{
var
scale
=
Math
.
max
(
container
.
width
/
this
.
backgroundWidth
,
container
.
height
/
this
.
backgroundHeight
);
var
backgroundWidth
=
this
.
backgroundWidth
*
scale
;
var
backgroundHeight
=
this
.
backgroundHeight
*
scale
;
}
else
if
(
backgroundSize
==
'contain'
)
{
var
scale
=
Math
.
min
(
container
.
width
/
this
.
backgroundWidth
,
container
.
height
/
this
.
backgroundHeight
);
var
backgroundWidth
=
this
.
backgroundWidth
*
scale
;
var
backgroundHeight
=
this
.
backgroundHeight
*
scale
;
}
else
{
backgroundSize
=
backgroundSize
.
split
(
' '
);
var
backgroundWidth
=
backgroundSize
[
0
]
||
''
;
var
backgroundHeight
=
backgroundSize
[
1
]
||
backgroundWidth
;
if
(
isPercentage
(
backgroundWidth
))
{
backgroundWidth
=
(
container
.
width
*
parseFloat
(
backgroundWidth
))
/
100
;
}
else
if
(
backgroundWidth
!=
'auto'
)
{
backgroundWidth
=
parseFloat
(
backgroundWidth
);
}
if
(
isPercentage
(
backgroundHeight
))
{
backgroundHeight
=
(
container
.
height
*
parseFloat
(
backgroundHeight
))
/
100
;
}
else
if
(
backgroundHeight
!=
'auto'
)
{
backgroundHeight
=
parseFloat
(
backgroundHeight
);
}
if
(
backgroundWidth
==
'auto'
&&
backgroundHeight
==
'auto'
)
{
backgroundWidth
=
this
.
backgroundWidth
;
backgroundHeight
=
this
.
backgroundHeight
;
}
else
{
if
(
backgroundWidth
==
'auto'
)
{
backgroundWidth
=
this
.
backgroundWidth
*
(
backgroundHeight
/
this
.
backgroundHeight
);
}
if
(
backgroundHeight
==
'auto'
)
{
backgroundHeight
=
this
.
backgroundHeight
*
(
backgroundWidth
/
this
.
backgroundWidth
);
}
}
}
// Compute backgroundX and backgroundY in page coordinates
let
backgroundX
=
backgroundPosition
[
0
];
let
backgroundY
=
backgroundPosition
[
1
];
if
(
isPercentage
(
backgroundX
))
{
backgroundX
=
container
.
left
+
((
container
.
width
-
backgroundWidth
)
*
parseFloat
(
backgroundX
))
/
100
;
}
else
{
backgroundX
=
container
.
left
+
parseFloat
(
backgroundX
);
}
if
(
isPercentage
(
backgroundY
))
{
backgroundY
=
container
.
top
+
((
container
.
height
-
backgroundHeight
)
*
parseFloat
(
backgroundY
))
/
100
;
}
else
{
backgroundY
=
container
.
top
+
parseFloat
(
backgroundY
);
}
const
elementOffset
=
this
.
$el
.
offset
();
this
.
renderProgram
.
uniforms
.
topLeft
=
new
Float32Array
([
(
elementOffset
.
left
-
backgroundX
)
/
backgroundWidth
,
(
elementOffset
.
top
-
backgroundY
)
/
backgroundHeight
,
]);
this
.
renderProgram
.
uniforms
.
bottomRight
=
new
Float32Array
([
this
.
renderProgram
.
uniforms
.
topLeft
[
0
]
+
this
.
$el
.
innerWidth
()
/
backgroundWidth
,
this
.
renderProgram
.
uniforms
.
topLeft
[
1
]
+
this
.
$el
.
innerHeight
()
/
backgroundHeight
,
]);
const
maxSide
=
Math
.
max
(
this
.
canvas
.
width
,
this
.
canvas
.
height
);
this
.
renderProgram
.
uniforms
.
containerRatio
=
new
Float32Array
([
this
.
canvas
.
width
/
maxSide
,
this
.
canvas
.
height
/
maxSide
,
]);
},
initShaders
()
{
const
vertexShader
=
[
'attribute vec2 vertex;'
,
'varying vec2 coord;'
,
'void main() {'
,
'coord = vertex * 0.5 + 0.5;'
,
'gl_Position = vec4(vertex, 0.0, 1.0);'
,
'}'
,
].
join
(
'
\
n'
);
this
.
dropProgram
=
createProgram
(
vertexShader
,
[
'precision highp float;'
,
'const float PI = 3.141592653589793;'
,
'uniform sampler2D texture;'
,
'uniform vec2 center;'
,
'uniform float radius;'
,
'uniform float strength;'
,
'varying vec2 coord;'
,
'void main() {'
,
'vec4 info = texture2D(texture, coord);'
,
'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);'
,
'drop = 0.5 - cos(drop * PI) * 0.5;'
,
'info.r += drop * strength;'
,
'gl_FragColor = info;'
,
'}'
,
].
join
(
'
\
n'
),
);
this
.
updateProgram
=
createProgram
(
vertexShader
,
[
'precision highp float;'
,
'uniform sampler2D texture;'
,
'uniform vec2 delta;'
,
'varying vec2 coord;'
,
'void main() {'
,
'vec4 info = texture2D(texture, coord);'
,
'vec2 dx = vec2(delta.x, 0.0);'
,
'vec2 dy = vec2(0.0, delta.y);'
,
'float average = ('
,
'texture2D(texture, coord - dx).r +'
,
'texture2D(texture, coord - dy).r +'
,
'texture2D(texture, coord + dx).r +'
,
'texture2D(texture, coord + dy).r'
,
') * 0.25;'
,
'info.g += (average - info.r) * 2.0;'
,
'info.g *= 0.995;'
,
'info.r += info.g;'
,
'gl_FragColor = info;'
,
'}'
,
].
join
(
'
\
n'
),
);
gl
.
uniform2fv
(
this
.
updateProgram
.
locations
.
delta
,
this
.
textureDelta
);
this
.
renderProgram
=
createProgram
(
[
'precision highp float;'
,
'attribute vec2 vertex;'
,
'uniform vec2 topLeft;'
,
'uniform vec2 bottomRight;'
,
'uniform vec2 containerRatio;'
,
'varying vec2 ripplesCoord;'
,
'varying vec2 backgroundCoord;'
,
'void main() {'
,
'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);'
,
'backgroundCoord.y = 1.0 - backgroundCoord.y;'
,
'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;'
,
'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);'
,
'}'
,
].
join
(
'
\
n'
),
[
'precision highp float;'
,
'uniform sampler2D samplerBackground;'
,
'uniform sampler2D samplerRipples;'
,
'uniform vec2 delta;'
,
'uniform float perturbance;'
,
'varying vec2 ripplesCoord;'
,
'varying vec2 backgroundCoord;'
,
'void main() {'
,
'float height = texture2D(samplerRipples, ripplesCoord).r;'
,
'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;'
,
'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;'
,
'vec3 dx = vec3(delta.x, heightX - height, 0.0);'
,
'vec3 dy = vec3(0.0, heightY - height, delta.y);'
,
'vec2 offset = -normalize(cross(dy, dx)).xz;'
,
'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);'
,
'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;'
,
'}'
,
].
join
(
'
\
n'
),
);
gl
.
uniform2fv
(
this
.
renderProgram
.
locations
.
delta
,
this
.
textureDelta
);
},
initTexture
()
{
this
.
backgroundTexture
=
gl
.
createTexture
();
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
this
.
backgroundTexture
);
gl
.
pixelStorei
(
gl
.
UNPACK_FLIP_Y_WEBGL
,
1
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MAG_FILTER
,
gl
.
LINEAR
);
gl
.
texParameteri
(
gl
.
TEXTURE_2D
,
gl
.
TEXTURE_MIN_FILTER
,
gl
.
LINEAR
);
},
setTransparentTexture
()
{
gl
.
bindTexture
(
gl
.
TEXTURE_2D
,
this
.
backgroundTexture
);
gl
.
texImage2D
(
gl
.
TEXTURE_2D
,
0
,
gl
.
RGBA
,
gl
.
RGBA
,
gl
.
UNSIGNED_BYTE
,
transparentPixels
);
},
hideCssBackground
()
{
// Check whether we're changing inline CSS or overriding a global CSS rule.
const
inlineCss
=
this
.
$el
[
0
].
style
.
backgroundImage
;
if
(
inlineCss
==
'none'
)
{
return
;
}
this
.
originalInlineCss
=
inlineCss
;
this
.
originalCssBackgroundImage
=
this
.
$el
.
css
(
'backgroundImage'
);
this
.
$el
.
css
(
'backgroundImage'
,
'none'
);
},
restoreCssBackground
()
{
// Restore background by either changing the inline CSS rule to what it was, or
// simply remove the inline CSS rule if it never was inlined.
this
.
$el
.
css
(
'backgroundImage'
,
this
.
originalInlineCss
||
''
);
},
dropAtPointer
(
pointer
,
radius
,
strength
)
{
const
borderLeft
=
parseInt
(
this
.
$el
.
css
(
'border-left-width'
))
||
0
;
const
borderTop
=
parseInt
(
this
.
$el
.
css
(
'border-top-width'
))
||
0
;
this
.
drop
(
pointer
.
pageX
-
this
.
$el
.
offset
().
left
-
borderLeft
,
pointer
.
pageY
-
this
.
$el
.
offset
().
top
-
borderTop
,
radius
,
strength
,
);
},
/**
* Public methods
*/
drop
(
x
,
y
,
radius
,
strength
)
{
gl
=
this
.
context
;
const
elWidth
=
this
.
$el
.
innerWidth
();
const
elHeight
=
this
.
$el
.
innerHeight
();
const
longestSide
=
Math
.
max
(
elWidth
,
elHeight
);
radius
/=
longestSide
;
const
dropPosition
=
new
Float32Array
([(
2
*
x
-
elWidth
)
/
longestSide
,
(
elHeight
-
2
*
y
)
/
longestSide
]);
gl
.
viewport
(
0
,
0
,
this
.
resolution
,
this
.
resolution
);
gl
.
bindFramebuffer
(
gl
.
FRAMEBUFFER
,
this
.
framebuffers
[
this
.
bufferWriteIndex
]);
bindTexture
(
this
.
textures
[
this
.
bufferReadIndex
]);
gl
.
useProgram
(
this
.
dropProgram
.
id
);
gl
.
uniform2fv
(
this
.
dropProgram
.
locations
.
center
,
dropPosition
);
gl
.
uniform1f
(
this
.
dropProgram
.
locations
.
radius
,
radius
);
gl
.
uniform1f
(
this
.
dropProgram
.
locations
.
strength
,
strength
);
this
.
drawQuad
();
this
.
swapBufferIndices
();
},
updateSize
()
{
const
newWidth
=
this
.
$el
.
innerWidth
();
const
newHeight
=
this
.
$el
.
innerHeight
();
if
(
newWidth
!=
this
.
canvas
.
width
||
newHeight
!=
this
.
canvas
.
height
)
{
this
.
canvas
.
width
=
newWidth
;
this
.
canvas
.
height
=
newHeight
;
}
},
destroy
()
{
this
.
$el
.
off
(
'.ripples'
)
.
removeClass
(
'jquery-ripples'
)
.
removeData
(
'ripples'
);
// Make sure the last used context is garbage-collected
gl
=
null
;
$
(
window
).
off
(
'resize'
,
this
.
updateSize
);
this
.
$canvas
.
remove
();
this
.
restoreCssBackground
();
this
.
destroyed
=
true
;
},
show
()
{
this
.
visible
=
true
;
this
.
$canvas
.
show
();
this
.
hideCssBackground
();
},
hide
()
{
this
.
visible
=
false
;
this
.
$canvas
.
hide
();
this
.
restoreCssBackground
();
},
pause
()
{
this
.
running
=
false
;
},
play
()
{
this
.
running
=
true
;
},
set
(
property
,
value
)
{
switch
(
property
)
{
case
'dropRadius'
:
case
'perturbance'
:
case
'interactive'
:
case
'crossOrigin'
:
this
[
property
]
=
value
;
break
;
case
'imageUrl'
:
this
.
imageUrl
=
value
;
this
.
loadImage
();
break
;
}
},
};
// RIPPLES PLUGIN DEFINITION
// ==========================
const
old
=
$
.
fn
.
ripples
;
$
.
fn
.
ripples
=
function
(
option
)
{
if
(
!
config
)
{
throw
new
Error
(
'Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.'
,
);
}
const
args
=
arguments
.
length
>
1
?
Array
.
prototype
.
slice
.
call
(
arguments
,
1
)
:
undefined
;
return
this
.
each
(
function
()
{
const
$this
=
$
(
this
);
let
data
=
$this
.
data
(
'ripples'
);
const
options
=
$
.
extend
({},
Ripples
.
DEFAULTS
,
$this
.
data
(),
typeof
option
===
'object'
&&
option
);
if
(
!
data
&&
typeof
option
===
'string'
)
{
return
;
}
if
(
!
data
)
{
$this
.
data
(
'ripples'
,
(
data
=
new
Ripples
(
this
,
options
)));
}
else
if
(
typeof
option
===
'string'
)
{
Ripples
.
prototype
[
option
].
apply
(
data
,
args
);
}
});
};
$
.
fn
.
ripples
.
Constructor
=
Ripples
;
// RIPPLES NO CONFLICT
// ====================
$
.
fn
.
ripples
.
noConflict
=
function
()
{
$
.
fn
.
ripples
=
old
;
return
this
;
};
});
src/assets/js/ripples/ripples.js
View file @
0394433d
...
...
@@ -2,26 +2,20 @@
/* globals jQuery, navigator */
(
function
(
$
,
window
,
document
,
undefined
)
{
"use strict"
;
/**
* Define the name of the plugin
*/
var
ripples
=
"ripples"
;
const
ripples
=
'ripples'
;
/**
* Get an instance of the plugin
*/
var
self
=
null
;
let
self
=
null
;
/**
* Define the defaults of the plugin
*/
var
defaults
=
{};
const
defaults
=
{};
/**
* Create the main plugin function
...
...
@@ -39,90 +33,79 @@
this
.
init
();
}
/**
* Initialize the plugin
*/
Ripples
.
prototype
.
init
=
function
()
{
var
$element
=
this
.
element
;
const
$element
=
this
.
element
;
$element
.
on
(
"mousedown touchstart"
,
function
(
event
)
{
$element
.
on
(
'mousedown touchstart'
,
function
(
event
)
{
/**
* Verify if the user is just touching on a device and return if so
*/
if
(
self
.
isTouch
()
&&
event
.
type
===
"mousedown"
)
{
if
(
self
.
isTouch
()
&&
event
.
type
===
'mousedown'
)
{
return
;
}
/**
* Verify if the current element already has a ripple wrapper element and
* creates if it doesn't
*/
if
(
!
(
$element
.
find
(
".ripple-container"
).
length
)
)
{
$element
.
append
(
"<div class=
\"
ripple-container
\"
></div>"
);
if
(
!
$element
.
find
(
'.ripple-container'
).
length
)
{
$element
.
append
(
'<div class="ripple-container"></div>'
);
}
/**
* Find the ripple wrapper
*/
var
$wrapper
=
$element
.
children
(
".ripple-container"
);
const
$wrapper
=
$element
.
children
(
'.ripple-container'
);
/**
* Get relY and relX positions
*/
var
relY
=
self
.
getRelY
(
$wrapper
,
event
);
var
relX
=
self
.
getRelX
(
$wrapper
,
event
);
const
relY
=
self
.
getRelY
(
$wrapper
,
event
);
const
relX
=
self
.
getRelX
(
$wrapper
,
event
);
/**
* If relY and/or relX are false, return the event
*/
if
(
!
relY
&&
!
relX
)
{
if
(
!
relY
&&
!
relX
)
{
return
;
}
/**
* Get the ripple color
*/
var
rippleColor
=
self
.
getRipplesColor
(
$element
);
const
rippleColor
=
self
.
getRipplesColor
(
$element
);
/**
* Create the ripple element
*/
var
$ripple
=
$
(
"<div></div>"
);
const
$ripple
=
$
(
'<div></div>'
);
$ripple
.
addClass
(
"ripple"
)
.
css
({
"left"
:
relX
,
"top"
:
relY
,
"background-color"
:
rippleColor
$ripple
.
addClass
(
'ripple'
).
css
({
left
:
relX
,
top
:
relY
,
'background-color'
:
rippleColor
,
});
/**
* Append the ripple to the wrapper
*/
$wrapper
.
append
(
$ripple
);
/**
* Make sure the ripple has the styles applied (ugly hack but it works)
*/
(
function
()
{
return
window
.
getComputedStyle
(
$ripple
[
0
]).
opacity
;
})();
(
function
()
{
return
window
.
getComputedStyle
(
$ripple
[
0
]).
opacity
;
})();
/**
* Turn on the ripple animation
*/
self
.
rippleOn
(
$element
,
$ripple
);
/**
* Call the rippleEnd function when the transition "on" ends
*/
...
...
@@ -130,115 +113,104 @@
self
.
rippleEnd
(
$ripple
);
},
500
);
/**
* Detect when the user leaves the element
*/
$element
.
on
(
"mouseup mouseleave touchend"
,
function
()
{
$ripple
.
data
(
"mousedown"
,
"off"
);
$element
.
on
(
'mouseup mouseleave touchend'
,
function
()
{
$ripple
.
data
(
'mousedown'
,
'off'
);
if
(
$ripple
.
data
(
"animating"
)
===
"off"
)
{
if
(
$ripple
.
data
(
'animating'
)
===
'off'
)
{
self
.
rippleOut
(
$ripple
);
}
});
});
};
/**
* Get the new size based on the element height/width and the ripple width
*/
Ripples
.
prototype
.
getNewSize
=
function
(
$element
,
$ripple
)
{
return
(
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
/
$ripple
.
outerWidth
())
*
2.5
;
};
/**
* Get the relX
*/
Ripples
.
prototype
.
getRelX
=
function
(
$wrapper
,
event
)
{
var
wrapperOffset
=
$wrapper
.
offset
();
Ripples
.
prototype
.
getRelX
=
function
(
$wrapper
,
event
)
{
const
wrapperOffset
=
$wrapper
.
offset
();
if
(
!
self
.
isTouch
())
{
if
(
!
self
.
isTouch
())
{
/**
* Get the mouse position relative to the ripple wrapper
*/
return
event
.
pageX
-
wrapperOffset
.
left
;
}
else
{
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event
=
event
.
originalEvent
;
if
(
event
.
touches
.
length
===
1
)
{
return
event
.
touches
[
0
].
pageX
-
wrapperOffset
.
left
;
}
return
false
;
}
};
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event
=
event
.
originalEvent
;
if
(
event
.
touches
.
length
===
1
)
{
return
event
.
touches
[
0
].
pageX
-
wrapperOffset
.
left
;
}
return
false
;
};
/**
* Get the relY
*/
Ripples
.
prototype
.
getRelY
=
function
(
$wrapper
,
event
)
{
var
wrapperOffset
=
$wrapper
.
offset
();
const
wrapperOffset
=
$wrapper
.
offset
();
if
(
!
self
.
isTouch
())
{
if
(
!
self
.
isTouch
())
{
/**
* Get the mouse position relative to the ripple wrapper
*/
return
event
.
pageY
-
wrapperOffset
.
top
;
}
else
{
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event
=
event
.
originalEvent
;
if
(
event
.
touches
.
length
===
1
)
{
return
event
.
touches
[
0
].
pageY
-
wrapperOffset
.
top
;
}
return
false
;
}
};
/**
* Make sure the user is using only one finger and then get the touch
* position relative to the ripple wrapper
*/
event
=
event
.
originalEvent
;
if
(
event
.
touches
.
length
===
1
)
{
return
event
.
touches
[
0
].
pageY
-
wrapperOffset
.
top
;
}
return
false
;
};
/**
* Get the ripple color
*/
Ripples
.
prototype
.
getRipplesColor
=
function
(
$element
)
{
var
color
=
$element
.
data
(
"ripple-color"
)
?
$element
.
data
(
"ripple-color"
)
:
window
.
getComputedStyle
(
$element
[
0
]).
color
;
const
color
=
$element
.
data
(
'ripple-color'
)
?
$element
.
data
(
'ripple-color'
)
:
window
.
getComputedStyle
(
$element
[
0
]).
color
;
return
color
;
};
/**
* Verify if the client browser has transistion support
*/
Ripples
.
prototype
.
hasTransitionSupport
=
function
()
{
var
thisBody
=
document
.
body
||
document
.
documentElement
;
var
thisStyle
=
thisBody
.
style
;
const
thisBody
=
document
.
body
||
document
.
documentElement
;
const
thisStyle
=
thisBody
.
style
;
var
support
=
(
const
support
=
thisStyle
.
transition
!==
undefined
||
thisStyle
.
WebkitTransition
!==
undefined
||
thisStyle
.
MozTransition
!==
undefined
||
thisStyle
.
MsTransition
!==
undefined
||
thisStyle
.
OTransition
!==
undefined
);
thisStyle
.
OTransition
!==
undefined
;
return
support
;
};
/**
* Verify if the client is using a mobile device
*/
...
...
@@ -246,79 +218,78 @@
return
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
.
test
(
navigator
.
userAgent
);
};
/**
* End the animation of the ripple
*/
Ripples
.
prototype
.
rippleEnd
=
function
(
$ripple
)
{
$ripple
.
data
(
"animating"
,
"off"
);
$ripple
.
data
(
'animating'
,
'off'
);
if
(
$ripple
.
data
(
"mousedown"
)
===
"off"
)
{
if
(
$ripple
.
data
(
'mousedown'
)
===
'off'
)
{
self
.
rippleOut
(
$ripple
);
}
};
/**
* Turn off the ripple effect
*/
Ripples
.
prototype
.
rippleOut
=
function
(
$ripple
)
{
$ripple
.
off
();
if
(
self
.
hasTransitionSupport
())
{
$ripple
.
addClass
(
"ripple-out"
);
if
(
self
.
hasTransitionSupport
())
{
$ripple
.
addClass
(
'ripple-out'
);
}
else
{
$ripple
.
animate
({
"opacity"
:
0
},
100
,
function
()
{
$ripple
.
trigger
(
"transitionend"
);
$ripple
.
animate
({
opacity
:
0
},
100
,
function
()
{
$ripple
.
trigger
(
'transitionend'
);
});
}
$ripple
.
on
(
"transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd"
,
function
()
{
$ripple
.
on
(
'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'
,
function
()
{
$ripple
.
remove
();
});
};
/**
* Turn on the ripple effect
*/
Ripples
.
prototype
.
rippleOn
=
function
(
$element
,
$ripple
)
{
var
size
=
self
.
getNewSize
(
$element
,
$ripple
);
const
size
=
self
.
getNewSize
(
$element
,
$ripple
);
if
(
self
.
hasTransitionSupport
())
{
if
(
self
.
hasTransitionSupport
())
{
$ripple
.
css
({
"-ms-transform"
:
"scale("
+
size
+
")"
,
"-moz-transform"
:
"scale("
+
size
+
")"
,
"-webkit-transform"
:
"scale("
+
size
+
")"
,
"transform"
:
"scale("
+
size
+
")"
})
.
addClass
(
"ripple-on"
)
.
data
(
"animating"
,
"on"
)
.
data
(
"mousedown"
,
"on"
);
.
css
({
'-ms-transform'
:
`scale(
${
size
}
)`
,
'-moz-transform'
:
`scale(
${
size
}
)`
,
'-webkit-transform'
:
`scale(
${
size
}
)`
,
transform
:
`scale(
${
size
}
)`
,
})
.
addClass
(
'ripple-on'
)
.
data
(
'animating'
,
'on'
)
.
data
(
'mousedown'
,
'on'
);
}
else
{
$ripple
.
animate
({
"width"
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
2
,
"height"
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
2
,
"margin-left"
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
(
-
1
),
"margin-top"
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
(
-
1
),
"opacity"
:
0.2
},
500
,
function
()
{
$ripple
.
trigger
(
"transitionend"
);
});
$ripple
.
animate
(
{
width
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
2
,
height
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
2
,
'margin-left'
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
-
1
,
'margin-top'
:
Math
.
max
(
$element
.
outerWidth
(),
$element
.
outerHeight
())
*
-
1
,
opacity
:
0.2
,
},
500
,
function
()
{
$ripple
.
trigger
(
'transitionend'
);
},
);
}
};
/**
* Create the jquery plugin function
*/
$
.
fn
.
ripples
=
function
(
options
)
{
return
this
.
each
(
function
()
{
if
(
!
$
.
data
(
this
,
"plugin_"
+
ripples
))
{
$
.
data
(
this
,
"plugin_"
+
ripples
,
new
Ripples
(
this
,
options
));
if
(
!
$
.
data
(
this
,
`plugin_
${
ripples
}
`
))
{
$
.
data
(
this
,
`plugin_
${
ripples
}
`
,
new
Ripples
(
this
,
options
));
}
});
};
})(
jQuery
,
window
,
document
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment