mirror of
https://github.com/twbs/bootstrap.git
synced 2025-02-20 17:54:23 +01:00
use pointer events if available
This commit is contained in:
parent
c08652cfe8
commit
735c374e9c
@ -56,22 +56,28 @@ const Event = {
|
|||||||
KEYDOWN : `keydown${EVENT_KEY}`,
|
KEYDOWN : `keydown${EVENT_KEY}`,
|
||||||
MOUSEENTER : `mouseenter${EVENT_KEY}`,
|
MOUSEENTER : `mouseenter${EVENT_KEY}`,
|
||||||
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
|
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
|
||||||
TOUCHEND : `touchend${EVENT_KEY}`,
|
|
||||||
TOUCHSTART : `touchstart${EVENT_KEY}`,
|
TOUCHSTART : `touchstart${EVENT_KEY}`,
|
||||||
TOUCHMOVE : `touchmove${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}`,
|
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
|
||||||
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClassName = {
|
const ClassName = {
|
||||||
CAROUSEL : 'carousel',
|
CAROUSEL : 'carousel',
|
||||||
ACTIVE : 'active',
|
ACTIVE : 'active',
|
||||||
SLIDE : 'slide',
|
SLIDE : 'slide',
|
||||||
RIGHT : 'carousel-item-right',
|
RIGHT : 'carousel-item-right',
|
||||||
LEFT : 'carousel-item-left',
|
LEFT : 'carousel-item-left',
|
||||||
NEXT : 'carousel-item-next',
|
NEXT : 'carousel-item-next',
|
||||||
PREV : 'carousel-item-prev',
|
PREV : 'carousel-item-prev',
|
||||||
ITEM : 'carousel-item'
|
ITEM : 'carousel-item',
|
||||||
|
POINTER_EVENT : 'pointer-event'
|
||||||
}
|
}
|
||||||
|
|
||||||
const Selector = {
|
const Selector = {
|
||||||
@ -84,6 +90,11 @@ const Selector = {
|
|||||||
DATA_RIDE : '[data-ride="carousel"]'
|
DATA_RIDE : '[data-ride="carousel"]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PointerType = {
|
||||||
|
TOUCH : 'touch',
|
||||||
|
PEN : 'pen'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
* Class Definition
|
* Class Definition
|
||||||
@ -103,7 +114,8 @@ class Carousel {
|
|||||||
this._config = this._getConfig(config)
|
this._config = this._getConfig(config)
|
||||||
this._element = element
|
this._element = element
|
||||||
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
|
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()
|
this._addEventListeners()
|
||||||
}
|
}
|
||||||
@ -265,22 +277,35 @@ class Carousel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this._element).on(Event.TOUCHSTART, (event) => {
|
const start = (event) => {
|
||||||
this.touchStartX = event.originalEvent.touches[0].pageX
|
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()
|
event.preventDefault()
|
||||||
|
|
||||||
// ensure swiping with one touch and not pinching
|
// ensure swiping with one touch and not pinching
|
||||||
if (event.originalEvent.touches.length > 1) {
|
if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
|
||||||
return
|
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()
|
this._handleSwipe()
|
||||||
|
|
||||||
if (this._config.pause === 'hover') {
|
if (this._config.pause === 'hover') {
|
||||||
@ -298,7 +323,21 @@ class Carousel {
|
|||||||
}
|
}
|
||||||
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
|
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) {
|
_keydown(event) {
|
||||||
|
@ -3,6 +3,26 @@ $(function () {
|
|||||||
|
|
||||||
window.Carousel = typeof bootstrap !== 'undefined' ? bootstrap.Carousel : Carousel
|
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.module('carousel plugin')
|
||||||
|
|
||||||
QUnit.test('should be defined on jQuery object', function (assert) {
|
QUnit.test('should be defined on jQuery object', function (assert) {
|
||||||
@ -1006,8 +1026,55 @@ $(function () {
|
|||||||
}, 80)
|
}, 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')
|
Simulator.setType('touch')
|
||||||
|
clearPointerEvents()
|
||||||
assert.expect(3)
|
assert.expect(3)
|
||||||
var done = assert.async()
|
var done = assert.async()
|
||||||
document.documentElement.ontouchstart = $.noop
|
document.documentElement.ontouchstart = $.noop
|
||||||
@ -1036,6 +1103,7 @@ $(function () {
|
|||||||
assert.ok($item.hasClass('active'))
|
assert.ok($item.hasClass('active'))
|
||||||
assert.ok(spy.called)
|
assert.ok(spy.called)
|
||||||
delete document.documentElement.ontouchstart
|
delete document.documentElement.ontouchstart
|
||||||
|
restorePointerEvents()
|
||||||
done()
|
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)
|
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')
|
Simulator.setType('touch')
|
||||||
|
|
||||||
var done = assert.async()
|
var done = assert.async()
|
||||||
@ -1075,6 +1191,7 @@ $(function () {
|
|||||||
assert.ok(true, 'slid event fired')
|
assert.ok(true, 'slid event fired')
|
||||||
assert.ok(!$item.hasClass('active'))
|
assert.ok(!$item.hasClass('active'))
|
||||||
assert.ok(spy.called)
|
assert.ok(spy.called)
|
||||||
|
restorePointerEvents()
|
||||||
done()
|
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)
|
assert.expect(0)
|
||||||
|
clearPointerEvents()
|
||||||
Simulator.setType('touch')
|
Simulator.setType('touch')
|
||||||
var done = assert.async()
|
var done = assert.async()
|
||||||
document.documentElement.ontouchstart = $.noop
|
document.documentElement.ontouchstart = $.noop
|
||||||
@ -1102,6 +1220,7 @@ $(function () {
|
|||||||
deltaY: 0,
|
deltaY: 0,
|
||||||
touches: 2
|
touches: 2
|
||||||
}, function () {
|
}, function () {
|
||||||
|
restorePointerEvents()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -190,7 +190,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.js",
|
"path": "./dist/js/bootstrap.js",
|
||||||
"maxSize": "21 kB"
|
"maxSize": "22 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.min.js",
|
"path": "./dist/js/bootstrap.min.js",
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.carousel.pointer-event {
|
||||||
|
touch-action: pan-x;
|
||||||
|
}
|
||||||
|
|
||||||
.carousel-inner {
|
.carousel-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user