Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
enhance(ripple): rewrites ripple effect to match the spec
Browse files Browse the repository at this point in the history
Closes #348. Closes #359. Closes #412. Closes #440. Closes #548.
  • Loading branch information
robertmesserle committed Nov 17, 2014
1 parent 7bcd12f commit 4bb940a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 103 deletions.
143 changes: 73 additions & 70 deletions src/core/services/ripple/ripple.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
(function() {
'use strict';


angular.module('material.core')
.factory('$mdInkRipple', InkRippleService)
.directive('mdInkRipple', InkRippleDirective)
Expand All @@ -19,7 +18,7 @@ function InkRippleDirective($mdInkRipple) {
};
}

function InkRippleService($window, $$rAF, $mdUtil, $timeout, $mdConstant) {
function InkRippleService($window, $timeout) {

return {
attachButtonBehavior: attachButtonBehavior,
Expand All @@ -29,133 +28,137 @@ function InkRippleService($window, $$rAF, $mdUtil, $timeout, $mdConstant) {

function attachButtonBehavior(element) {
return attach(element, {
mousedown: true,
center: false,
animationDuration: 350,
mousedownPauseTime: 175,
animationName: 'inkRippleButton',
animationTimingFunction: 'linear'
dimBackground: true
});
}

function attachCheckboxBehavior(element) {
return attach(element, {
mousedown: true,
center: true,
animationDuration: 300,
mousedownPauseTime: 180,
animationName: 'inkRippleCheckbox',
animationTimingFunction: 'linear'
dimBackground: false
});
}

function attach(element, options) {
// Parent element with mdNoInk attr? Abort.

if (element.controller('mdNoInk')) return angular.noop;
var contentParent = element.controller('mdContent');

var rippleContainer, rippleEl,
node = element[0],
hammertime = new Hammer(node),
contentParent = element.controller('mdContent');

options = angular.extend({
mousedown: true,
hover: true,
focus: true,
center: false,
animationDuration: 300,
mousedownPauseTime: 150,
animationName: '',
animationTimingFunction: 'linear'
dimBackground: false
}, options || {});

var rippleContainer;
var node = element[0];
var hammertime = new Hammer(node);

if (options.mousedown) {
hammertime.on('hammer.input', onInput);
}
options.mousedown && hammertime.on('hammer.input', onInput);

// Publish self-detach method if desired...
return function detach() {
hammertime.destroy();
if (rippleContainer) {
rippleContainer.remove();
}
rippleContainer && rippleContainer.remove();
};

function rippleIsAllowed() {
return !element[0].hasAttribute('disabled') &&
!(element[0].parentNode && element[0].parentNode.hasAttribute('disabled'));
return !element[0].hasAttribute('disabled') &&
!(element[0].parentNode && element[0].parentNode.hasAttribute('disabled'));
}

function removeElement(element, wait) {
$timeout(function () {
element.remove();
}, wait, false);
}

function createRipple(left, top, positionsAreAbsolute) {

var rippleEl = angular.element('<div class="md-ripple">')
.css($mdConstant.CSS.ANIMATION_DURATION, options.animationDuration + 'ms')
.css($mdConstant.CSS.ANIMATION_NAME, options.animationName)
.css($mdConstant.CSS.ANIMATION_TIMING, options.animationTimingFunction)
.on($mdConstant.CSS.ANIMATIONEND, function() {
rippleEl.remove();
});
var rippleEl = angular.element('<div class="md-ripple">');

if (!rippleContainer) {
rippleContainer = angular.element('<div class="md-ripple-container">');
element.append(rippleContainer);
}
rippleContainer.append(rippleEl);

var containerWidth = rippleContainer.prop('offsetWidth');

if (options.center) {
left = containerWidth / 2;
top = rippleContainer.prop('offsetHeight') / 2;
} else if (positionsAreAbsolute) {
var elementRect = node.getBoundingClientRect();
left -= elementRect.left;
top -= elementRect.top;
}
var containerWidth = rippleContainer.prop('offsetWidth'),
containerHeight = rippleContainer.prop('offsetHeight'),
multiplier = element.hasClass('md-fab') ? 1.1 : 0.8,
diagonalWidth = Math.max(containerWidth, containerHeight) * multiplier;

if (contentParent) {
top += contentParent.$element.prop('scrollTop');
}

var css = {
'background-color': $window.getComputedStyle(rippleEl[0]).color ||
$window.getComputedStyle(node).color,
'border-radius': (containerWidth / 2) + 'px',
backgroundColor: $window.getComputedStyle(rippleEl[0]).color || $window.getComputedStyle(node).color,
width: diagonalWidth + 'px',
height: diagonalWidth + 'px',
marginLeft: (diagonalWidth * -0.5) + 'px',
marginTop: (diagonalWidth * -0.5) + 'px'
};

left: (left - containerWidth / 2) + 'px',
width: containerWidth + 'px',
if (options.center) {
css.left = '50%';
css.top = '50%';
} else if (positionsAreAbsolute) {
var elementRect = node.getBoundingClientRect();
left -= elementRect.left;
top -= elementRect.top;
css.left = Math.round(left / containerWidth * 100) + '%';
css.top = Math.round(top / containerHeight * 100) + '%';
}

top: (top - containerWidth / 2) + 'px',
height: containerWidth + 'px'
};
css[$mdConstant.CSS.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
rippleEl.css(css);

//-- Use minimum timeout to trigger CSS animation
$timeout(function () {
if (options.dimBackground) {
rippleContainer.addClass('full visible');
rippleContainer.css({ backgroundColor: css.backgroundColor.replace(')', ', 0.1').replace('(', 'a(') });
}
rippleEl.addClass('md-ripple-placed md-ripple-visible md-ripple-scaled md-ripple-full');
rippleEl.css({ left: '50%', top: '50%' });
$timeout(function () {
if (rippleEl) {
rippleEl.removeClass('md-ripple-full');
if (!rippleEl.hasClass('md-ripple-visible')) {
removeElement(rippleEl, 650);
rippleEl = null;
}
}
rippleEl && rippleEl.removeClass('md-ripple-full');
if (rippleContainer && options.dimBackground) {
rippleContainer.removeClass('md-ripple-full');
if (!rippleContainer.hasClass('md-ripple-visible')) rippleContainer.css({ backgroundColor: '' });
}
}, 225, false);
}, 0, false);
return rippleEl;
}

var pauseTimeout;
var rippleEl;
function onInput(ev) {
if (ev.eventType === Hammer.INPUT_START && ev.isFirst && rippleIsAllowed()) {

rippleEl = createRipple(ev.center.x, ev.center.y, true);
pauseTimeout = $timeout(function() {
rippleEl && rippleEl.css($mdConstant.CSS.ANIMATION_PLAY_STATE, 'paused');
}, options.mousedownPauseTime, false);

rippleEl.on('$destroy', function() {
rippleEl = null;
});

} else if (ev.eventType === Hammer.INPUT_END && ev.isFinal) {
$timeout.cancel(pauseTimeout);
rippleEl && rippleEl.css($mdConstant.CSS.ANIMATION_PLAY_STATE, '');
if (rippleEl) {
rippleEl.removeClass('md-ripple-visible');
removeElement(rippleEl, 650);
rippleEl = null;
}
if (rippleContainer && options.dimBackground) {
rippleContainer.removeClass('md-ripple-visible');
if (!rippleContainer.hasClass('md-ripple-full')) rippleContainer.css({ backgroundColor: '' });
}
}
}

}

}

/**
Expand Down
52 changes: 19 additions & 33 deletions src/core/style/structure.scss
Original file line number Diff line number Diff line change
Expand Up @@ -218,39 +218,6 @@ input {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

// Button ripple: keep same opacity, but expand to 0.75 scale.
// Then, fade out and expand to 2.0 scale.
@keyframes inkRippleButton {
0% {
transform: scale(0);
opacity: 0.15;
}
50% {
transform: scale(0.75);
opacity: 0.15;
}
100% {
transform: scale(2.0);
opacity: 0;
}
}

// Checkbox ripple: fully expand, then fade out.
@keyframes inkRippleCheckbox {
0% {
transform: scale(0);
opacity: 0.4;
}
50% {
transform: scale(1.0);
opacity: 0.4;
}
100% {
transform: scale(1.0);
opacity: 0;
}
}

/*
* A container inside of a rippling element (eg a button),
* which contains all of the individual ripples
Expand All @@ -263,8 +230,27 @@ input {
top: 0;
width: 100%;
height: 100%;
transition: all 0.65s $swift-ease-out-timing-function;
}

.md-ripple {
position: absolute;
transform: scale(0);
transform-origin: 50% 50%;
opacity: 0;
border-radius: 50%;
&.md-ripple-placed {
transition: all 0.65s $swift-ease-out-timing-function;
}
&.md-ripple-scaled {
transform: scale(1);
}
&.md-ripple-full, &.md-ripple-visible {
opacity: 0.15;
}
&.md-ripple-held {
opacity: 0.15;
transform: scale(0.35);
transition: none;
}
}
15 changes: 15 additions & 0 deletions src/core/style/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,18 @@ $z-index-bottom-sheet: 8;
$z-index-sidenav: 6;
$z-index-backdrop: 5;
$z-index-fab: 2;

// Easing Curves
//--------------------------------------------

$swift-ease-out-duration: 0.5s;
$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function;

$swift-ease-in-duration: 0.4s;
$swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function;

$swift-ease-in-out-duration: 0.5s;
$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1);
$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function;

0 comments on commit 4bb940a

Please sign in to comment.