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

Commit 4b15c70

Browse files
update(slider): discrete sliders now support live dragging between discrete values and snap-to animate to closest discrete value.
1 parent fd7697d commit 4b15c70

File tree

2 files changed

+63
-18
lines changed

2 files changed

+63
-18
lines changed

src/components/slider/slider.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ function SliderDirective() {
5353
'$element',
5454
'$attrs',
5555
'$$rAF',
56-
'$timeout',
5756
'$window',
5857
'$materialEffects',
5958
'$aria',
@@ -99,7 +98,7 @@ function SliderDirective() {
9998
* We use a controller for all the logic so that we can expose a few
10099
* things to unit tests
101100
*/
102-
function SliderController(scope, element, attr, $$rAF, $timeout, $window, $materialEffects, $aria) {
101+
function SliderController(scope, element, attr, $$rAF, $window, $materialEffects, $aria) {
103102

104103
this.init = function init(ngModelCtrl) {
105104
var thumb = angular.element(element[0].querySelector('.slider-thumb'));
@@ -136,6 +135,7 @@ function SliderController(scope, element, attr, $$rAF, $timeout, $window, $mater
136135
hammertime.on('hammer.input', onInput);
137136
hammertime.on('panstart', onPanStart);
138137
hammertime.on('pan', onPan);
138+
hammertime.on('panend', onPanEnd);
139139

140140
// On resize, recalculate the slider's dimensions and re-render
141141
var updateAll = $$rAF.debounce(function() {
@@ -282,18 +282,27 @@ function SliderController(scope, element, attr, $$rAF, $timeout, $window, $mater
282282
* Slide listeners
283283
*/
284284
var isSliding = false;
285+
var isDiscrete = angular.isDefined(attr.discrete);
286+
285287
function onInput(ev) {
286288
if (!isSliding && ev.eventType === Hammer.INPUT_START &&
287289
!element[0].hasAttribute('disabled')) {
288290

289291
isSliding = true;
292+
290293
element.addClass('active');
291294
element[0].focus();
292295
refreshSliderDimensions();
293-
doSlide(ev.center.x);
296+
297+
onPan(ev);
294298

295299
} else if (isSliding && ev.eventType === Hammer.INPUT_END) {
300+
301+
if ( isDiscrete ) onPanEnd(ev);
302+
296303
isSliding = false;
304+
isDiscrete = false;
305+
297306
element.removeClass('panning active');
298307
}
299308
}
@@ -303,8 +312,26 @@ function SliderController(scope, element, attr, $$rAF, $timeout, $window, $mater
303312
}
304313
function onPan(ev) {
305314
if (!isSliding) return;
306-
doSlide(ev.center.x);
315+
316+
// While panning discrete, update only the
317+
// visual positioning but not the model value.
318+
319+
if ( isDiscrete ) doPan( ev.center.x );
320+
else doSlide( ev.center.x );
321+
307322
ev.preventDefault();
323+
ev.srcEvent.stopPropagation();
324+
}
325+
326+
function onPanEnd(ev) {
327+
if ( isDiscrete ) {
328+
// Updating the model and slide position
329+
// and perform an animated snap-to operation
330+
doSlide( ev.center.x );
331+
ngModelRender();
332+
333+
ev.srcEvent.stopPropagation();
334+
}
308335
}
309336

310337
/**
@@ -314,9 +341,25 @@ function SliderController(scope, element, attr, $$rAF, $timeout, $window, $mater
314341
this._onPanStart = onPanStart;
315342
this._onPan = onPan;
316343

317-
function doSlide(x) {
344+
/**
345+
* Slide the UI by changing the model value
346+
* @param x
347+
*/
348+
function doSlide( x ) {
349+
var percent = (x - sliderDimensions.left) / (sliderDimensions.width);
350+
351+
scope.$evalAsync( function() {
352+
setModelValue(min + percent * (max - min));
353+
});
354+
}
355+
356+
/**
357+
* Slide the UI without changing the model (while dragging/panning)
358+
* @param x
359+
*/
360+
function doPan( x ) {
318361
var percent = (x - sliderDimensions.left) / (sliderDimensions.width);
319-
scope.$evalAsync(function() { setModelValue(min + percent * (max - min)); });
362+
setSliderPercent( percent );
320363
}
321364

322365
};

src/components/slider/slider.spec.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11

22
describe('material-slider', function() {
33

4+
function simulateEventAt( centerX, eventType ) {
5+
return {
6+
eventType: eventType,
7+
center: { x: centerX },
8+
preventDefault: angular.noop,
9+
srcEvent : {
10+
stopPropagation : angular.noop
11+
}
12+
};
13+
}
14+
415
beforeEach(module('material.components.slider','material.decorators'));
516

617
it('should set model on press', inject(function($compile, $rootScope, $timeout) {
@@ -14,25 +25,16 @@ describe('material-slider', function() {
1425
right: 0
1526
});
1627

17-
sliderCtrl._onInput({
18-
eventType: Hammer.INPUT_START,
19-
center: { x: 30 }
20-
});
28+
sliderCtrl._onInput( simulateEventAt( 30, Hammer.INPUT_START ));
2129
$timeout.flush();
2230
expect($rootScope.value).toBe(30);
2331

2432
//When going past max, it should clamp to max
25-
sliderCtrl._onPan({
26-
center: { x: 500 },
27-
preventDefault: angular.noop
28-
});
33+
sliderCtrl._onPan( simulateEventAt( 500 ));
2934
$timeout.flush();
3035
expect($rootScope.value).toBe(100);
3136

32-
sliderCtrl._onPan({
33-
center: { x: 50 },
34-
preventDefault: angular.noop
35-
});
37+
sliderCtrl._onPan( simulateEventAt( 50 ));
3638
$timeout.flush();
3739
expect($rootScope.value).toBe(50);
3840
}));

0 commit comments

Comments
 (0)