Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohann-S <johann.servoire@gmail.com>2018-10-15 00:10:13 +0300
committerXhmikosR <xhmikosr@gmail.com>2018-10-20 15:32:09 +0300
commit735c374e9c366446afb30e20592ac1d796c7bb69 (patch)
tree4ca77c65912c195237e4f768333e3b32f9c8d0c1
parentc08652cfe84051184570d704d3a0904e8d01358c (diff)
use pointer events if available
-rw-r--r--js/src/carousel.js77
-rw-r--r--js/tests/unit/carousel.js125
-rw-r--r--package.json2
-rw-r--r--scss/_carousel.scss4
4 files changed, 185 insertions, 23 deletions
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 5b6209460a..f0ad83bb0b 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -56,22 +56,28 @@ const Event = {
KEYDOWN : `keydown${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
- TOUCHEND : `touchend${EVENT_KEY}`,
TOUCHSTART : `touchstart${EVENT_KEY}`,
TOUCHMOVE : `touchmove${EVENT_KEY}`,
+ TOUCHEND : `touchend${EVENT_KEY}`,
+ POINTERDOWN : `pointerdown${EVENT_KEY}`,
+ POINTERMOVE : `pointermove${EVENT_KEY}`,
+ POINTERUP : `pointerup${EVENT_KEY}`,
+ POINTERLEAVE : `pointerleave${EVENT_KEY}`,
+ POINTERCANCEL : `pointercancel${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
- CAROUSEL : 'carousel',
- ACTIVE : 'active',
- SLIDE : 'slide',
- RIGHT : 'carousel-item-right',
- LEFT : 'carousel-item-left',
- NEXT : 'carousel-item-next',
- PREV : 'carousel-item-prev',
- ITEM : 'carousel-item'
+ CAROUSEL : 'carousel',
+ ACTIVE : 'active',
+ SLIDE : 'slide',
+ RIGHT : 'carousel-item-right',
+ LEFT : 'carousel-item-left',
+ NEXT : 'carousel-item-next',
+ PREV : 'carousel-item-prev',
+ ITEM : 'carousel-item',
+ POINTER_EVENT : 'pointer-event'
}
const Selector = {
@@ -84,6 +90,11 @@ const Selector = {
DATA_RIDE : '[data-ride="carousel"]'
}
+const PointerType = {
+ TOUCH : 'touch',
+ PEN : 'pen'
+}
+
/**
* ------------------------------------------------------------------------
* Class Definition
@@ -103,7 +114,8 @@ class Carousel {
this._config = this._getConfig(config)
this._element = element
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
- this._touchSupported = 'ontouchstart' in document.documentElement
+ this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
+ this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
this._addEventListeners()
}
@@ -265,22 +277,35 @@ class Carousel {
return
}
- $(this._element).on(Event.TOUCHSTART, (event) => {
- this.touchStartX = event.originalEvent.touches[0].pageX
- })
+ const start = (event) => {
+ event.preventDefault()
+ const originEvent = event.originalEvent
- $(this._element).on(Event.TOUCHMOVE, (event) => {
+ if (this._pointerEvent && (originEvent.pointerType === PointerType.TOUCH || originEvent.pointerType === PointerType.PEN)) {
+ this.touchStartX = originEvent.clientX
+ } else {
+ this.touchStartX = originEvent.touches[0].pageX
+ }
+ }
+
+ const move = (event) => {
event.preventDefault()
// ensure swiping with one touch and not pinching
- if (event.originalEvent.touches.length > 1) {
+ if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
return
}
- this.touchDeltaX = event.originalEvent.touches[0].pageX - this.touchStartX
- })
+ if (!this._pointerEvent) {
+ this.touchDeltaX = event.originalEvent.touches[0].pageX - this.touchStartX
+ }
+ }
+
+ const end = (event) => {
+ if (this._pointerEvent) {
+ this.touchDeltaX = event.originalEvent.clientX - this.touchStartX
+ }
- $(this._element).on(Event.TOUCHEND, () => {
this._handleSwipe()
if (this._config.pause === 'hover') {
@@ -298,7 +323,21 @@ class Carousel {
}
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
}
- })
+ }
+
+ if (this._pointerEvent) {
+ $(this._element).on(Event.POINTERDOWN, (event) => start(event))
+ $(this._element).on(Event.POINTERMOVE, (event) => move(event))
+ $(this._element).on(Event.POINTERUP, (event) => end(event))
+ $(this._element).on(Event.POINTERLEAVE, (event) => end(event))
+ $(this._element).on(Event.POINTERCANCEL, (event) => end(event))
+
+ this._element.classList.add(ClassName.POINTER_EVENT)
+ } else {
+ $(this._element).on(Event.TOUCHSTART, (event) => start(event))
+ $(this._element).on(Event.TOUCHMOVE, (event) => move(event))
+ $(this._element).on(Event.TOUCHEND, (event) => end(event))
+ }
}
_keydown(event) {
diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js
index e416ab20e4..68c28aec5e 100644
--- a/js/tests/unit/carousel.js
+++ b/js/tests/unit/carousel.js
@@ -3,6 +3,26 @@ $(function () {
window.Carousel = typeof bootstrap !== 'undefined' ? bootstrap.Carousel : Carousel
+ var originWinPointerEvent = window.PointerEvent
+ var originMsPointerEvent = window.MSPointerEvent
+ var supportPointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
+
+ function clearPointerEvents() {
+ window.PointerEvent = null
+ window.MSPointerEvent = null
+ }
+
+ function restorePointerEvents() {
+ window.PointerEvent = originWinPointerEvent
+ window.MSPointerEvent = originMsPointerEvent
+ }
+
+ var stylesCarousel = [
+ '<style>',
+ ' .carousel.pointer-event { -ms-touch-action: pan-x; touch-action: pan-x; }',
+ '</style>'
+ ].join('')
+
QUnit.module('carousel plugin')
QUnit.test('should be defined on jQuery object', function (assert) {
@@ -1006,8 +1026,55 @@ $(function () {
}, 80)
})
- QUnit.test('should allow swiperight and call prev', function (assert) {
+ QUnit.test('should allow swiperight and call prev with pointer events', function (assert) {
+ if (!supportPointerEvent) {
+ assert.expect(0)
+ return
+ }
+
+ Simulator.setType('pointer')
+ assert.expect(3)
+ var $styles = $(stylesCarousel).appendTo('head')
+ var done = assert.async()
+ document.documentElement.ontouchstart = $.noop
+
+ var carouselHTML =
+ '<div class="carousel" data-interval="false">' +
+ ' <div class="carousel-inner">' +
+ ' <div id="item" class="carousel-item">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' <div class="carousel-item active">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' </div>' +
+ '</div>'
+
+ var $carousel = $(carouselHTML)
+ $carousel.appendTo('#qunit-fixture')
+ var $item = $('#item')
+ $carousel.bootstrapCarousel()
+ var carousel = $carousel.data('bs.carousel')
+ var spy = sinon.spy(carousel, 'prev')
+
+ $carousel.one('slid.bs.carousel', function () {
+ assert.ok(true, 'slid event fired')
+ assert.ok($item.hasClass('active'))
+ assert.ok(spy.called)
+ delete document.documentElement.ontouchstart
+ $styles.remove()
+ done()
+ })
+
+ Simulator.gestures.swipe($carousel[0], {
+ deltaX: 300,
+ deltaY: 0
+ })
+ })
+
+ QUnit.test('should allow swiperight and call prev with touch events', function (assert) {
Simulator.setType('touch')
+ clearPointerEvents()
assert.expect(3)
var done = assert.async()
document.documentElement.ontouchstart = $.noop
@@ -1036,6 +1103,7 @@ $(function () {
assert.ok($item.hasClass('active'))
assert.ok(spy.called)
delete document.documentElement.ontouchstart
+ restorePointerEvents()
done()
})
@@ -1045,8 +1113,56 @@ $(function () {
})
})
- QUnit.test('should allow swipeleft and call next', function (assert) {
+ QUnit.test('should allow swipeleft and call next with pointer events', function (assert) {
+ if (!supportPointerEvent) {
+ assert.expect(0)
+ return
+ }
+
+ assert.expect(3)
+ Simulator.setType('pointer')
+
+ var $styles = $(stylesCarousel).appendTo('head')
+ var done = assert.async()
+ document.documentElement.ontouchstart = $.noop
+
+ var carouselHTML =
+ '<div class="carousel" data-interval="false">' +
+ ' <div class="carousel-inner">' +
+ ' <div id="item" class="carousel-item active">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' <div class="carousel-item">' +
+ ' <img alt="">' +
+ ' </div>' +
+ ' </div>' +
+ '</div>'
+
+ var $carousel = $(carouselHTML)
+ $carousel.appendTo('#qunit-fixture')
+ var $item = $('#item')
+ $carousel.bootstrapCarousel()
+ var carousel = $carousel.data('bs.carousel')
+ var spy = sinon.spy(carousel, 'next')
+
+ $carousel.one('slid.bs.carousel', function () {
+ assert.ok(true, 'slid event fired')
+ assert.ok(!$item.hasClass('active'))
+ assert.ok(spy.called)
+ $styles.remove()
+ done()
+ })
+
+ Simulator.gestures.swipe($carousel[0], {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0
+ })
+ })
+
+ QUnit.test('should allow swipeleft and call next with touch events', function (assert) {
assert.expect(3)
+ clearPointerEvents()
Simulator.setType('touch')
var done = assert.async()
@@ -1075,6 +1191,7 @@ $(function () {
assert.ok(true, 'slid event fired')
assert.ok(!$item.hasClass('active'))
assert.ok(spy.called)
+ restorePointerEvents()
done()
})
@@ -1085,8 +1202,9 @@ $(function () {
})
})
- QUnit.test('should not allow pinch', function (assert) {
+ QUnit.test('should not allow pinch with touch events', function (assert) {
assert.expect(0)
+ clearPointerEvents()
Simulator.setType('touch')
var done = assert.async()
document.documentElement.ontouchstart = $.noop
@@ -1102,6 +1220,7 @@ $(function () {
deltaY: 0,
touches: 2
}, function () {
+ restorePointerEvents()
done()
})
})
diff --git a/package.json b/package.json
index c7302c490d..39e883d18b 100644
--- a/package.json
+++ b/package.json
@@ -190,7 +190,7 @@
},
{
"path": "./dist/js/bootstrap.js",
- "maxSize": "21 kB"
+ "maxSize": "22 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
diff --git a/scss/_carousel.scss b/scss/_carousel.scss
index 97e792ea72..7d3728b40d 100644
--- a/scss/_carousel.scss
+++ b/scss/_carousel.scss
@@ -12,6 +12,10 @@
position: relative;
}
+.carousel.pointer-event {
+ touch-action: pan-x;
+}
+
.carousel-inner {
position: relative;
width: 100%;