Skip to content

Commit beb0edf

Browse files
devversionkara
authored andcommitted
fix(slide-toggle): occasional element jumping (#3311)
1 parent 09c6386 commit beb0edf

File tree

3 files changed

+59
-62
lines changed

3 files changed

+59
-62
lines changed

src/lib/slide-toggle/slide-toggle.html

+19-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
11
<label class="mat-slide-toggle-label" #label>
22

3-
<div class="mat-slide-toggle-container">
4-
<div class="mat-slide-toggle-bar"></div>
5-
6-
<div class="mat-slide-toggle-thumb-container"
7-
(slidestart)="_onDragStart()"
8-
(slide)="_onDrag($event)"
9-
(slideend)="_onDragEnd()">
10-
11-
<div class="mat-slide-toggle-thumb">
12-
<div class="mat-slide-toggle-ripple" md-ripple
13-
[mdRippleTrigger]="label"
14-
[mdRippleCentered]="true"
15-
[mdRippleDisabled]="disableRipple || disabled">
16-
</div>
17-
</div>
18-
</div>
3+
<div class="mat-slide-toggle-bar">
194

205
<input #input class="mat-slide-toggle-input cdk-visually-hidden" type="checkbox"
216
[id]="inputId"
@@ -30,7 +15,25 @@
3015
(focus)="_onInputFocus()"
3116
(change)="_onChangeEvent($event)"
3217
(click)="_onInputClick($event)">
18+
19+
<div class="mat-slide-toggle-thumb-container"
20+
(slidestart)="_onDragStart()"
21+
(slide)="_onDrag($event)"
22+
(slideend)="_onDragEnd()">
23+
24+
<div class="mat-slide-toggle-thumb"></div>
25+
26+
<div class="mat-slide-toggle-ripple" md-ripple
27+
[mdRippleTrigger]="label"
28+
[mdRippleCentered]="true"
29+
[mdRippleDisabled]="disableRipple || disabled">
30+
</div>
31+
32+
</div>
33+
34+
3335
</div>
36+
3437
<span class="mat-slide-toggle-content">
3538
<ng-content></ng-content>
3639
</span>

src/lib/slide-toggle/slide-toggle.scss

+33-41
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
@import '../core/style/elevation';
44
@import '../core/a11y/a11y';
55

6-
7-
$mat-slide-toggle-width: 36px !default;
8-
$mat-slide-toggle-height: 24px !default;
9-
$mat-slide-toggle-bar-height: 14px !default;
106
$mat-slide-toggle-thumb-size: 20px !default;
7+
$mat-slide-toggle-bar-border-radius: 8px !default;
8+
$mat-slide-toggle-height: 24px !default;
119
$mat-slide-toggle-margin: 16px !default;
1210
$mat-slide-toggle-spacing: 8px !default;
1311
$mat-slide-toggle-ripple-radius: 23px !default;
12+
$mat-slide-toggle-bar-width: 36px !default;
13+
$mat-slide-toggle-bar-height: 14px !default;
14+
$mat-slide-toggle-bar-track-width: $mat-slide-toggle-bar-width - $mat-slide-toggle-thumb-size;
1415

1516

1617
.mat-slide-toggle {
@@ -30,13 +31,12 @@ $mat-slide-toggle-ripple-radius: 23px !default;
3031

3132
&.mat-checked {
3233
.mat-slide-toggle-thumb-container {
33-
transform: translate3d(100%, 0, 0);
34+
transform: translate3d($mat-slide-toggle-bar-track-width, 0, 0);
3435
}
3536
}
3637

3738
&.mat-disabled {
38-
39-
.mat-slide-toggle-label, .mat-slide-toggle-container {
39+
.mat-slide-toggle-label, .mat-slide-toggle-thumb-container {
4040
cursor: default;
4141
}
4242
}
@@ -55,69 +55,63 @@ $mat-slide-toggle-ripple-radius: 23px !default;
5555
.mat-slide-toggle-label {
5656
display: flex;
5757
flex: 1;
58+
flex-direction: row;
59+
align-items: center;
5860

5961
cursor: pointer;
6062
}
6163

6264
/* If the label should be placed before the thumb then we just change the orders. */
6365
.mat-slide-toggle-label-before {
6466
.mat-slide-toggle-label { order: 1; }
65-
.mat-slide-toggle-container { order: 2; }
66-
}
67-
68-
// Container for the composition of the slide-toggle / switch indicator.
69-
.mat-slide-toggle-container {
70-
cursor: grab;
71-
width: $mat-slide-toggle-width;
72-
height: $mat-slide-toggle-height;
73-
74-
position: relative;
67+
.mat-slide-toggle-bar { order: 2; }
7568
}
7669

77-
/* Apply the margin for slide-toggles and revert it for RTL toggles with labelPosition before. */
78-
[dir='rtl'] .mat-slide-toggle-label-before .mat-slide-toggle-container, .mat-slide-toggle-container {
70+
// Apply the margin for slide-toggles and revert it for RTL toggles with labelPosition before.
71+
[dir='rtl'] .mat-slide-toggle-label-before .mat-slide-toggle-bar,
72+
.mat-slide-toggle-bar {
7973
margin-right: $mat-slide-toggle-spacing;
8074
margin-left: 0;
8175
}
8276

83-
/* Switch the margins in RTL mode and also switch it if the labelPosition is set to before. */
84-
[dir='rtl'], .mat-slide-toggle-label-before {
85-
.mat-slide-toggle-container {
77+
// Switch the margins in RTL mode and also switch it if the labelPosition is set to before.
78+
[dir='rtl'],
79+
.mat-slide-toggle-label-before {
80+
.mat-slide-toggle-bar {
8681
margin-left: $mat-slide-toggle-spacing;
8782
margin-right: 0;
8883
}
8984
}
9085

9186
// The thumb container is responsible for the dragging functionality.
92-
// It moves around and holds the actual circle as a thumb.
87+
// The container includes the visual thumb and the ripple container element.
9388
.mat-slide-toggle-thumb-container {
89+
$thumb-bar-vertical-padding: ($mat-slide-toggle-thumb-size - $mat-slide-toggle-bar-height) / 2;
90+
9491
position: absolute;
95-
top: $mat-slide-toggle-height / 2 - $mat-slide-toggle-thumb-size / 2;
96-
left: 0;
9792
z-index: 1;
9893

99-
width: $mat-slide-toggle-width - $mat-slide-toggle-thumb-size;
94+
width: $mat-slide-toggle-thumb-size;
95+
height: $mat-slide-toggle-thumb-size;
96+
top: -$thumb-bar-vertical-padding;
97+
left: 0;
10098

10199
transform: translate3d(0, 0, 0);
102-
103100
transition: $swift-linear;
104101
transition-property: transform;
105102

103+
cursor: grab;
104+
106105
// Once the thumb container is being dragged around, we remove the transition duration to
107106
// make the drag feeling fast and not delayed.
108107
&.mat-dragging {
109108
transition-duration: 0ms;
110109
}
111110
}
112111

113-
// The thumb will be elevated from the slide-toggle bar.
114-
// Also the thumb is bound to its parent thumb-container, which manages the movement of the thumb.
112+
// The visual thumb element that moves inside of the thumb bar.
113+
// The parent thumb-container container is responsible for the movement of the visual thumb.
115114
.mat-slide-toggle-thumb {
116-
position: absolute;
117-
margin: 0;
118-
left: 0;
119-
top: 0;
120-
121115
height: $mat-slide-toggle-thumb-size;
122116
width: $mat-slide-toggle-thumb-size;
123117
border-radius: 50%;
@@ -131,16 +125,14 @@ $mat-slide-toggle-ripple-radius: 23px !default;
131125
}
132126

133127
// Horizontal bar for the slide-toggle.
134-
// The slide-toggle bar is shown behind the thumb container.
128+
// The slide-toggle bar is shown behind the movable thumb element.
135129
.mat-slide-toggle-bar {
136-
position: absolute;
137-
left: 1px;
138-
top: $mat-slide-toggle-height / 2 - $mat-slide-toggle-bar-height / 2;
130+
position: relative;
139131

140-
width: $mat-slide-toggle-width - 2px;
132+
width: $mat-slide-toggle-bar-width;
141133
height: $mat-slide-toggle-bar-height;
142134

143-
border-radius: 8px;
135+
border-radius: $mat-slide-toggle-bar-border-radius;
144136

145137
@include cdk-high-contrast {
146138
background: #fff;
@@ -173,4 +165,4 @@ $mat-slide-toggle-ripple-radius: 23px !default;
173165
border-radius: 50%;
174166
z-index: 1;
175167
pointer-events: none;
176-
}
168+
}

src/lib/slide-toggle/slide-toggle.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class SlideToggleRenderer {
296296
/** Previous checked state before drag started. */
297297
private _previousChecked: boolean;
298298

299-
/** Percentage of the thumb while dragging. */
299+
/** Percentage of the thumb while dragging. Percentage as fraction of 100. */
300300
dragPercentage: number;
301301

302302
/** Whether the thumb is currently being dragged. */
@@ -333,12 +333,14 @@ class SlideToggleRenderer {
333333

334334
/** Updates the thumb containers position from the specified distance. */
335335
updateThumbPosition(distance: number) {
336-
this.dragPercentage = this._getThumbPercentage(distance);
337-
applyCssTransform(this._thumbEl, `translate3d(${this.dragPercentage}%, 0, 0)`);
336+
this.dragPercentage = this._getDragPercentage(distance);
337+
// Calculate the moved distance based on the thumb bar width.
338+
let dragX = (this.dragPercentage / 100) * this._thumbBarWidth;
339+
applyCssTransform(this._thumbEl, `translate3d(${dragX}px, 0, 0)`);
338340
}
339341

340-
/** Retrieves the percentage of thumb from the moved distance. */
341-
private _getThumbPercentage(distance: number) {
342+
/** Retrieves the percentage of thumb from the moved distance. Percentage as fraction of 100. */
343+
private _getDragPercentage(distance: number) {
342344
let percentage = (distance / this._thumbBarWidth) * 100;
343345

344346
// When the toggle was initially checked, then we have to start the drag at the end.

0 commit comments

Comments
 (0)