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:
@@ -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;
|
||||
|
@@ -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
|
||||
[
|
||||
|
@@ -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,
|
||||
|
@@ -9,7 +9,9 @@
|
||||
"sinon": false,
|
||||
"Util": false,
|
||||
"Alert": false,
|
||||
"Button": false
|
||||
"Button": false,
|
||||
"Carousel": false,
|
||||
"Simulator": false
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
|
@@ -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
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user