1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-08-09 07:06:36 +02:00

Add touch support in our carousel with HammerJS.

This commit is contained in:
Johann-S
2018-03-03 23:04:11 +02:00
committed by XhmikosR
parent bf57389647
commit caefd70463
17 changed files with 267 additions and 41 deletions

View File

@@ -1,4 +1,5 @@
import $ from 'jquery'
import Hammer from 'hammerjs'
import Util from './util'
/**
@@ -23,13 +24,15 @@ const JQUERY_NO_CONFLICT = $.fn[NAME]
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const HAMMER_ENABLED = typeof Hammer !== 'undefined'
const Default = {
interval : 5000,
keyboard : true,
slide : false,
pause : 'hover',
wrap : true
wrap : true,
touch : true
}
const DefaultType = {
@@ -37,7 +40,8 @@ const DefaultType = {
keyboard : 'boolean',
slide : '(boolean|string)',
pause : '(string|boolean)',
wrap : 'boolean'
wrap : 'boolean',
touch : 'boolean'
}
const Direction = {
@@ -55,7 +59,9 @@ const Event = {
MOUSELEAVE : `mouseleave${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_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}`,
SWIPELEFT : 'swipeleft',
SWIPERIGHT : 'swiperight'
}
const ClassName = {
@@ -84,21 +90,30 @@ const Selector = {
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
this._items = null
this._interval = null
this._activeElement = null
this._items = null
this._interval = null
this._activeElement = null
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this.hammer = null
this._isPaused = false
this._isSliding = false
this._config = this._getConfig(config)
this._element = element
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
this.touchTimeout = null
this._config = this._getConfig(config)
this._element = $(element)[0]
this._indicatorsElement = this._element.querySelector(Selector.INDICATORS)
if (HAMMER_ENABLED && this._touchSupported && this._config.touch) {
this.hammer = new Hammer(this._element, {
recognizers: [[
Hammer.Swipe, {
direction: Hammer.DIRECTION_HORIZONTAL
}
]]
})
}
this._addEventListeners()
}
@@ -226,11 +241,16 @@ class Carousel {
.on(Event.KEYDOWN, (event) => this._keydown(event))
}
if (this.hammer) {
this.hammer.on(Event.SWIPELEFT, () => this.next())
this.hammer.on(Event.SWIPERIGHT, () => this.prev())
}
if (this._config.pause === 'hover') {
$(this._element)
.on(Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event))
if ('ontouchstart' in document.documentElement) {
if (this._touchSupported) {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;

View File

@@ -20,6 +20,7 @@
}())
</script>
<script src="../../node_modules/popper.js/dist/umd/popper.min.js"></script>
<script src="../../node_modules/hammerjs/hammer.min.js"></script>
<!-- QUnit -->
<link rel="stylesheet" href="../../node_modules/qunit/qunit/qunit.css" media="screen">
@@ -28,6 +29,9 @@
<!-- Sinon -->
<script src="../../node_modules/sinon/pkg/sinon-no-sourcemaps.js"></script>
<!-- Hammer simulator -->
<script src="../../node_modules/hammer-simulator/index.js"></script>
<script>
// Disable jQuery event aliases to ensure we don't accidentally use any of them
[

View File

@@ -12,16 +12,16 @@ const jqueryFile = process.env.USE_OLD_JQUERY ? 'https://code.jquery.com/jquery-
const bundle = process.env.BUNDLE === 'true'
const browserStack = process.env.BROWSER === 'true'
const frameworks = [
'qunit',
'sinon'
]
const plugins = [
'karma-qunit',
'karma-sinon'
]
const frameworks = [
'qunit',
'sinon'
]
const reporters = ['dots']
const detectBrowsers = {
@@ -46,7 +46,12 @@ const customLaunchers = {
}
}
let files = ['node_modules/popper.js/dist/umd/popper.min.js']
let files = [
'node_modules/popper.js/dist/umd/popper.min.js',
'node_modules/hammerjs/hammer.min.js',
'node_modules/hammer-simulator/index.js'
]
const conf = {
basePath: '../..',
port: 9876,

View File

@@ -9,7 +9,9 @@
"sinon": false,
"Util": false,
"Alert": false,
"Button": false
"Button": false,
"Carousel": false,
"Simulator": false
},
"parserOptions": {
"ecmaVersion": 5,

View File

@@ -1,6 +1,10 @@
$(function () {
'use strict'
window.Carousel = typeof bootstrap !== 'undefined' ? bootstrap.Carousel : Carousel
var touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
QUnit.module('carousel plugin')
QUnit.test('should be defined on jQuery object', function (assert) {
@@ -25,6 +29,20 @@ $(function () {
assert.strictEqual(typeof $.fn.carousel, 'undefined', 'carousel was set back to undefined (orig value)')
})
QUnit.test('should return version', function (assert) {
assert.expect(1)
assert.strictEqual(typeof Carousel.VERSION, 'string')
})
QUnit.test('should return default parameters', function (assert) {
assert.expect(1)
var defaultConfig = Carousel.Default
assert.strictEqual(defaultConfig.touch, true)
})
QUnit.test('should throw explicit error on undefined method', function (assert) {
assert.expect(1)
var $el = $('<div/>')
@@ -989,4 +1007,113 @@ $(function () {
}, 80)
}, 80)
})
QUnit.test('should allow swiperight and call prev', function (assert) {
if (!touchSupported) {
assert.expect(0)
return
}
assert.expect(2)
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()
$carousel.one('slid.bs.carousel', function () {
assert.ok(true, 'slid event fired')
assert.ok($item.hasClass('active'))
delete document.documentElement.ontouchstart
done()
})
Simulator.gestures.swipe($carousel[0], {
deltaX: 300,
deltaY: 0
})
})
QUnit.test('should not use HammerJS when touch option is false', function (assert) {
assert.expect(1)
var $carousel = $('<div></div>').appendTo('#qunit-fixture')
$carousel.bootstrapCarousel({
touch: false
})
var carousel = $carousel.data('bs.carousel')
assert.strictEqual(carousel.hammer, null)
})
QUnit.test('should use HammerJS when touch option is true', function (assert) {
assert.expect(1)
document.documentElement.ontouchstart = $.noop
var $carousel = $('<div></div>').appendTo('#qunit-fixture')
$carousel.bootstrapCarousel()
var carousel = $carousel.data('bs.carousel')
assert.ok(carousel.hammer !== null)
})
QUnit.test('should allow swipeleft and call next', function (assert) {
if (!touchSupported) {
assert.expect(0)
return
}
assert.expect(2)
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()
$carousel.one('slid.bs.carousel', function () {
assert.ok(true, 'slid event fired')
assert.ok(!$item.hasClass('active'))
delete document.documentElement.ontouchstart
done()
})
Simulator.gestures.swipe($carousel[0], {
pos: [300, 10],
deltaX: -300,
deltaY: 0
})
})
})

View File

@@ -46,6 +46,7 @@
</div>
<script src="../../../site/docs/4.1/assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../../node_modules/hammerjs/hammer.min.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/carousel.js"></script>
<script>