diff --git a/assets/index.less b/assets/index.less
index 1777d2cc8..5171b133d 100644
--- a/assets/index.less
+++ b/assets/index.less
@@ -27,7 +27,6 @@
padding: 5px 0;
width: 100%;
border-radius: @border-radius-base;
- touch-action: none;
.borderBox();
&-rail {
@@ -58,7 +57,6 @@
border-radius: 50%;
border: solid 2px tint(@primary-color, 50%);
background-color: #fff;
- touch-action: pan-x;
&:focus {
border-color: tint(@primary-color, 20%);
@@ -145,6 +143,14 @@
cursor: not-allowed!important;
}
}
+
+ &-dragging {
+ touch-action: none;
+
+ .@{prefixClass}-handle {
+ touch-action: pan-x;
+ }
+ }
}
.@{prefixClass}-vertical {
@@ -167,7 +173,6 @@
&-handle {
margin-left: -5px;
margin-bottom: -7px;
- touch-action: pan-y;
}
&-mark {
diff --git a/src/common/createSlider.jsx b/src/common/createSlider.jsx
index b99858587..85fa40a44 100644
--- a/src/common/createSlider.jsx
+++ b/src/common/createSlider.jsx
@@ -125,19 +125,29 @@ export default function createSlider(Component) {
if (utils.isNotTouchEvent(e)) return;
const isVertical = this.props.vertical;
- let position = utils.getTouchPosition(isVertical, e);
+ const position = utils.getTouchPosition(isVertical, e);
if (!utils.isEventFromHandle(e, this.handlesRefs)) {
this.dragOffset = 0;
} else {
const handlePosition = utils.getHandleCenterPosition(isVertical, e.target);
this.dragOffset = position - handlePosition;
- position = handlePosition;
}
- this.onStart(position);
+
+ this.firstTouches = e.touches;
+ this.isDragging = true;
+ this.respectTouch = true;
this.addDocumentTouchEvents();
utils.pauseEvent(e);
}
+ onTouchEnd = (e) => {
+ if (utils.isNotTouchEvent(e)) return;
+
+ this.firstTouches = null;
+ this.isDragging = false;
+ this.respectTouch = true;
+ }
+
onFocus = (e) => {
const { onFocus, vertical } = this.props;
if (utils.isEventFromHandle(e, this.handlesRefs)) {
@@ -180,8 +190,20 @@ export default function createSlider(Component) {
return;
}
- const position = utils.getTouchPosition(this.props.vertical, e);
- this.onMove(e, position - this.dragOffset);
+ const isVertical = this.props.vertical;
+ const position = utils.getTouchPosition(isVertical, e);
+
+ if (this.firstTouches) {
+ if (utils.isCorrectTouchDirection(this.firstTouches[0], e.touches[0], isVertical)) {
+ this.onStart(position - this.dragOffset);
+ utils.pauseEvent(e);
+ } else {
+ this.respectTouch = false;
+ }
+ this.firstTouches = null;
+ } else if (this.respectTouch) {
+ this.onMove(e, position - this.dragOffset);
+ }
}
onKeyDown = (e) => {
@@ -302,6 +324,7 @@ export default function createSlider(Component) {
[`${prefixCls}-with-marks`]: Object.keys(marks).length,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-vertical`]: vertical,
+ [`${prefixCls}-dragging`]: this.isDragging,
[className]: className,
});
return (
@@ -309,6 +332,7 @@ export default function createSlider(Component) {
ref={this.saveSlider}
className={sliderClassName}
onTouchStart={disabled ? noop : this.onTouchStart}
+ onTouchEnd={disabled ? noop : this.onTouchEnd}
onMouseDown={disabled ? noop : this.onMouseDown}
onMouseUp={disabled ? noop : this.onMouseUp}
onKeyDown={disabled ? noop : this.onKeyDown}
diff --git a/src/utils.js b/src/utils.js
index af224a255..a4d1f0589 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -117,3 +117,29 @@ export function getKeyboardValueMutator(e) {
default: return undefined;
}
}
+
+// http://hammerjs.github.io/api/#directions
+const DIRECTION_NONE = 1; // 00001
+const DIRECTION_LEFT = 2; // 00010
+const DIRECTION_RIGHT = 4; // 00100
+const DIRECTION_UP = 8; // 01000
+const DIRECTION_DOWN = 16; // 10000
+
+export function isCorrectTouchDirection(firstTouch, secondTouch, vertical) {
+ const x = secondTouch.clientX - firstTouch.clientX;
+ const y = secondTouch.clientY - firstTouch.clientY;
+
+ let direction;
+
+ if (x === y) {
+ direction = DIRECTION_NONE;
+ } else if (Math.abs(x) >= Math.abs(y)) {
+ direction = x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
+ } else {
+ direction = y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
+ }
+
+ return vertical
+ ? (direction === DIRECTION_UP || direction === DIRECTION_DOWN)
+ : (direction === DIRECTION_LEFT || direction === DIRECTION_RIGHT);
+}
diff --git a/tests/common/createSlider.test.js b/tests/common/createSlider.test.js
index 4007501e6..374c92dda 100644
--- a/tests/common/createSlider.test.js
+++ b/tests/common/createSlider.test.js
@@ -207,7 +207,7 @@ describe('createSlider', () => {
wrapper.simulate('touchstart', {
type: 'touchstart',
target: leftHandle,
- touches: [{ pageX: 5 }],
+ touches: [{ pageX: 5, clientX: 5, clientY: 0 }],
stopPropagation() {},
preventDefault() {},
});
@@ -215,13 +215,43 @@ describe('createSlider', () => {
wrapper.instance().onTouchMove({ // to propagation
type: 'touchmove',
target: leftHandle,
- touches: [{ pageX: 14 }],
+ touches: [{ pageX: 14, clientX: 14, clientY: 0 }],
stopPropagation() {},
preventDefault() {},
});
expect(wrapper.instance().getValue()).toBe(9);
});
+ it('should ignore TouchEvents in the wrong direction', () => {
+ const wrapper = mount();
+ setWidth(wrapper.instance().sliderRef, 100);
+ const leftHandle = wrapper.find('.rc-slider-handle').at(1).instance();
+ wrapper.simulate('touchstart', {
+ type: 'touchstart',
+ target: leftHandle,
+ touches: [{ pageX: 5, clientX: 5, clientY: 0 }],
+ stopPropagation() {},
+ preventDefault() {},
+ });
+ expect(wrapper.instance().dragOffset).toBe(5);
+ wrapper.instance().onTouchMove({
+ type: 'touchmove',
+ target: leftHandle,
+ touches: [{ pageX: 10, clientX: 10, clientY: 8 }],
+ stopPropagation() {},
+ preventDefault() {},
+ });
+ expect(wrapper.instance().getValue()).toBe(0);
+ wrapper.instance().onTouchMove({
+ type: 'touchmove',
+ target: leftHandle,
+ touches: [{ pageX: 14, clientX: 14, clientY: 8 }],
+ stopPropagation() {},
+ preventDefault() {},
+ });
+ expect(wrapper.instance().getValue()).toBe(0);
+ });
+
it('should set `dragOffset` to 0 when the TouchEvent target isn\'t a handle', () => {
const wrapper = mount();
setWidth(wrapper.instance().sliderRef, 100);
diff --git a/tests/utils.test.js b/tests/utils.test.js
index d6da21a80..0cd71e6dd 100644
--- a/tests/utils.test.js
+++ b/tests/utils.test.js
@@ -47,4 +47,45 @@ describe('utils', () => {
expect(utils.getClosestPoint(value, props)).toBe(96);
});
});
+
+ describe('isCorrectTouchDirection', () => {
+ let vertical;
+ let firstTouch;
+ let verticalTouch;
+ let horizontalTouch;
+
+ beforeEach(() => {
+ firstTouch = {clientX: 0, clientY: 0}
+ verticalTouch = {clientX: 0, clientY: 5}
+ horizontalTouch = {clientX: 5, clientY: 0}
+ })
+
+ describe('when vertical=FALSE', () => {
+ beforeEach(() => { vertical = false; })
+
+ it('is FALSE when secondTouch is vertically positioned from firstTouch', () => {
+ expect(utils.isCorrectTouchDirection(firstTouch, verticalTouch, vertical)).toBe(false);
+ });
+
+ it('is TRUE when secondTouch is horizontally positioned from firstTouch', () => {
+ expect(utils.isCorrectTouchDirection(firstTouch, horizontalTouch, vertical)).toBe(true);
+ });
+ });
+
+ describe('when vertical=TRUE', () => {
+ beforeEach(() => { vertical = true; })
+
+ it('is TRUE when secondTouch is vertically positioned from firstTouch', () => {
+ expect(utils.isCorrectTouchDirection(firstTouch, verticalTouch, vertical)).toBe(true);
+ });
+
+ it('is FALSE when secondTouch is horizontally positioned from firstTouch', () => {
+ expect(utils.isCorrectTouchDirection(firstTouch, horizontalTouch, vertical)).toBe(false);
+ });
+ });
+
+ it('is FALSE when secondTouch is identical to firstTouch', () => {
+ expect(utils.isCorrectTouchDirection(firstTouch, firstTouch, false)).toBe(false);
+ })
+ });
});