Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
fix(toolbar): make scrollShrink work with transforms, better performance
Browse files Browse the repository at this point in the history
Closes #295
  • Loading branch information
ajoslin committed Sep 19, 2014
1 parent 6426551 commit cf1ab59
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 48 deletions.
2 changes: 1 addition & 1 deletion docs/app/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ pre > code.highlight {
}

.demo-container {
overflow: hidden;
margin-top: 30px;
-webkit-transition: 0.02s padding ease-in-out;
transition: 0.02s padding ease-in-out;
Expand All @@ -129,6 +128,7 @@ pre > code.highlight {
transform: translate3d(0, 0, 0);
min-height: 448px;
background: #fff;
overflow: hidden;
}

.show-source [demo-include] {
Expand Down
5 changes: 3 additions & 2 deletions src/components/content/_content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ material-content {
padding: 8px;
}

// min-width: 100%;
// min-height: 100%;
&[scroll-shrink] {
position: relative;
}
}

@media (min-width: $layout-breakpoint-sm) {
Expand Down
86 changes: 55 additions & 31 deletions src/components/toolbar/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ angular.module('material.components.toolbar', [
*
* Note: for scrollShrink to work, the toolbar must be a sibling of a
* `material-content` element, placed before it. See the scroll shrink demo.
*
* @param {number=} shrinkSpeedFactor How much to change the speed of the toolbar's
* shrinking by. For example, if 0.25 is given then the toolbar will shrink
* at one fourth the rate at which the user scrolls down. Default 0.5.
*/
function materialToolbarDirective($$rAF, $materialEffects) {

Expand All @@ -66,56 +70,76 @@ function materialToolbarDirective($$rAF, $materialEffects) {
}

function setupScrollShrink() {
//makes it take X times as long for header to dissapear
var HEIGHT_FACTOR = 2;
var height = element.prop("offsetHeight") * HEIGHT_FACTOR;
// Current "y" position of scroll
var y = 0;
// Store the last scroll top position
var prevScrollTop = 0;

var shrinkSpeedFactor = attr.shrinkSpeedFactor || 0.5;

var toolbarHeight;
var contentElement;

var debouncedContentScroll = $$rAF.debounce(onContentScroll);
var debouncedUpdateHeight = Util.debounce(updateToolbarHeight, 5 * 1000);

// Wait for $materialContentLoaded event from materialContent directive.
// If the materialContent element is a sibling of our toolbar, hook it up
// to scroll events.
scope.$on('$materialContentLoaded', onMaterialContentLoad);

var contentElement;
function onMaterialContentLoad($event, contentEl) {

if (Util.elementIsSibling(element, contentEl)) {
function onMaterialContentLoad($event, newContentEl) {
if (Util.elementIsSibling(element, newContentEl)) {
// unhook old content event listener if exists
contentElement && contentElement.off('scroll', onContentScroll);
contentEl.on('scroll', onContentScroll).css('position','relative');
contentElement = contentEl;
if (contentElement) {
contentElement.off('scroll', debouncedContentScroll);
}

newContentEl.on('scroll', debouncedContentScroll);
newContentEl.attr('scroll-shrink', 'true');

contentElement = newContentEl;
$$rAF(updateToolbarHeight);
}
}

function updateToolbarHeight() {
toolbarHeight = element.prop('offsetHeight');
// Add a negative margin-top the size of the toolbar to the content el.
// The content will start transformed down the toolbarHeight amount,
// so everything looks normal.
//
// As the user scrolls down, the content will be transformed up slowly
// to put the content underneath where the toolbar was.
contentElement.css(
'margin-top',
(-toolbarHeight * shrinkSpeedFactor) + 'px'
);
onContentScroll();
}

function onContentScroll(e) {
shrink(e.target.scrollTop);
prevScrollTop = e.target.scrollTop;
}
var scrollTop = e ? e.target.scrollTop : prevScrollTop;

// Shrink the given target element based on the scrolling
// of the scroller element.
function shrink(scrollTop) {
y = Math.min(height, Math.max(0, y + scrollTop - prevScrollTop));
// If we are scrolling back "up", show the header condensed again
// if (prevScrollTop > scrollTop && scrollTop > margin) {
// y = Math.max(y, margin);
// }
$$rAF(transform);
}
debouncedUpdateHeight();

function transform() {
var translate = y ?
'translate3d(0,' + (-y / HEIGHT_FACTOR) + 'px, 0)' :
'';
element.css($materialEffects.TRANSFORM, translate);
contentElement.css('margin-top', y ?
(-y / HEIGHT_FACTOR) + 'px' :
'');
y = Math.min(
toolbarHeight / shrinkSpeedFactor,
Math.max(0, y + scrollTop - prevScrollTop)
);

element.css(
$materialEffects.TRANSFORM,
'translate3d(0,' + (-y * shrinkSpeedFactor) + 'px,0)'
);
contentElement.css(
$materialEffects.TRANSFORM,
'translate3d(0,' + ((toolbarHeight - y) * shrinkSpeedFactor) + 'px,0)'
);

prevScrollTop = scrollTop;
}

}

}
Expand Down
48 changes: 34 additions & 14 deletions src/components/toolbar/toolbar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ describe('<material-toolbar>', function() {

beforeEach(module('material.components.toolbar', function($provide) {
//Create fake raf to instant-trigger the callback
$provide.value('$$rAF', function(cb) {
cb();
});
function raf(cb) { cb(); }
raf.debounce = function(cb) {
var args = arguments;
return function() {
return cb.apply(null, arguments);
};
};

$provide.value('$$rAF', raf);
}));

it('with scrollShrink, it should shrink scrollbar when going to bottom', inject(function($compile, $rootScope, $materialEffects, materialToolbarDirective) {

var parent = angular.element('<div>');
var toolbar = angular.element('<material-toolbar scroll-shrink>');
var toolbar = angular.element('<material-toolbar>');
var contentEl = angular.element('<div>');
// Make content and toolbar siblings
parent.append(toolbar).append(contentEl);
Expand All @@ -20,32 +26,46 @@ describe('<material-toolbar>', function() {
return 100;
});

// Fake the css function so we can read css values properly,
// no matter which browser the tests are being run on.
// (IE, firefox, chrome all give different results when reading element.style)
var toolbarCss = {};
spyOn(toolbar, 'css').andCallFake(function(k, v) {
toolbarCss[k] = v;
});
var contentCss = {};
spyOn(contentEl, 'css').andCallFake(function(k, v) {
contentCss[k] = v;
});

// Manually link so we can give our own elements with spies on them
materialToolbarDirective[0].link($rootScope, toolbar, { scrollShrink: true });
materialToolbarDirective[0].link($rootScope, toolbar, {
scrollShrink: true,
shrinkSpeedFactor: 1
});

$rootScope.$broadcast('$materialContentLoaded', contentEl);

// IE gives us back 'none', everything else gives us back an empty string
expect(toolbar.css($materialEffects.TRANSFORM)).toMatch(/none|/);
// IE gives us back '0px', everything else gives us back an empty string
expect(contentEl.css('margin-top')).toMatch(/0px|/);
//Expect everything to be in its proper initial state.
expect(toolbarCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');
expect(contentCss['margin-top']).toEqual('-100px');
expect(contentCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,100px,0)');

// Fake scroll to the bottom
TestUtil.triggerEvent(contentEl, 'scroll', {
target: { scrollTop: 500 }
});

// IE gives us back a matrix with -100 in it, everything else gives us -100px
expect(toolbar.css($materialEffects.TRANSFORM)).toContain('-100');
expect(contentEl.css('margin-top')).toContain('-100');
expect(toolbarCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,-100px,0)');
expect(contentCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');

// Fake scroll back to the top
TestUtil.triggerEvent(contentEl, 'scroll', {
target: { scrollTop: 0 }
});

expect(toolbar.css($materialEffects.TRANSFORM)).toMatch(/none|/);
expect(contentEl.css('margin-top')).toMatch(/0px|/);
expect(toolbarCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,0px,0)');
expect(contentCss[$materialEffects.TRANSFORM]).toEqual('translate3d(0,100px,0)');

}));
});

0 comments on commit cf1ab59

Please sign in to comment.