问题描述
我在 JS 中制作了一个自定义轮播,可以为移动设备启用触摸导航。在此轮播中,轮播的每张幻灯片都作为链接,单击此幻灯片可访问此幻灯片的相关页面。我的问题是,在将旋转木马拖到下一张幻灯片后的响应中,我点击了当前幻灯片而没有拖动,旋转木马转到上一张幻灯片,就像我向左拖动一样。我怎样才能禁用它?我需要在每个项目上使用嵌入链接。
触控支持类
class CarouselTouchPlugin {
/**
* @param {Carousel} carousel
*/
constructor(carousel) {
carousel.container.addEventListener('dragstart',e => e.preventDefault(),{passive: true})
carousel.container.addEventListener('mousedown',this.startDrag.bind(this))
carousel.container.addEventListener('touchstart',this.startDrag.bind(this))
carousel.container.addEventListener('mousemove',this.drag.bind(this))
carousel.container.addEventListener('touchmove',this.drag.bind(this))
carousel.container.addEventListener('touchend',this.endDrag.bind(this))
carousel.container.addEventListener('mouseup',this.endDrag.bind(this))
carousel.container.addEventListener('touchcancel',this.endDrag.bind(this))
this.carousel = carousel
}
/**
* Démarre le déplacement au touché
* @param {MouseEvent|TouchEvent} e
*/
startDrag(e) {
console.log('Start Drag')
if (e.touches) {
if (e.touches.length > 1) {
return
} else {
e = e.touches[0]
}
}
this.origin = {x: e.screenX,y: e.screenY}
this.width = this.carousel.containerWidth
this.carousel.disableTransition()
}
/**
* Déplacement
* @param {MouseEvent|TouchEvent} e
*/
drag(e) {
console.log('Drag')
if (this.origin) {
let point = e.touches ? e.touches[0] : e
let translate = {x: point.screenX - this.origin.x,y: point.screenY - this.origin.y}
if (e.touches && Math.abs(translate.x) > Math.abs(translate.y)) {
e.preventDefault()
e.stopPropagation()
}
let baseTranslate = this.carousel.currentItem * -100 / this.carousel.items.length
this.lastTranslate = translate
this.carousel.translate(baseTranslate + 100 * translate.x / this.width)
}
}
/**
* Fin du déplacement
* @param {MouseEvent|TouchEvent} e
*/
endDrag (e) {
console.log('End Drag')
if (this.origin && this.lastTranslate) {
this.carousel.enableTransition()
if (Math.abs(this.lastTranslate.x / this.carousel.carouselWidth) > 0.2) {
if (this.lastTranslate.x < 0) {
this.carousel.next()
} else if (this.lastTranslate.x > 0) {
this.carousel.prev()
}
} else {
this.carousel.gotoItem(this.carousel.currentItem)
}
}
this.origin = null
}
}
我的主要轮播类
在这个类中,CarouselTouchPlugin 类只使用了几个函数: translate() - gotoItem() - prev() - next() - disableTransition() - enableTransition()
class Carousel {
/**
* This callback type is called 'requestCallback' and is displayed as a global symbol.
*
* @callback moveCallback
* @param {number} index
*/
/**
* @param {HTMLElement} element
* @param {Object} options
* @param {Object} [options.slidesToScroll = 1] Nombres d'éléments à faire défiler
* @param {Object} [options.slidesVisible = 1] Nombres d'éléments visibles dans un slide
* @param {boolean} [options.loop = false] Doit-on boucler en fin de carousel
* @param {boolean} [options.infinite = false]
* @param {boolean} [options.pagination = false]
* @param {boolean} [options.navigation = true]
*
*/
// Methode avec deux paramètres ou objet vide
constructor (element,options = {}) {
this.element = element // sauvegarde l'élément dans une variable "element"
this.options = Object.assign({},{ // Création de la propriété "options" assigné à l'objet et les valeurs par default
// Propriétés par défault
slidesToScroll: 1,slidesVisible: 1,animation: true,loop: false,navigation: true,pagination: false,infinite: true,play: false,timer: 5000,overflow: false
},options)
if (this.options.loop && this.options.infinite) {
throw new Error('Un carousel ne peut être à la fois en boucle et en infinie')
}
let children = [].slice.call(element.children) // Conserve les éléments enfant dans un tableau
this.isMobile = false // Est-on sur Mobile ?
this.currentItem = 0 // Initialise l'item à zero
this.moveCallbacks = [] // Sauvegarde les callbacks dans une instance
this.offset = 0 // Initialise l'offset à zero
// Modification du DOM
this.root = this.createDivWithClass('carousel') // Création d'une div avec la class carousel
this.container = this.createDivWithClass('carousel__container') // Création d'une div avec la class carousel__container
this.overflow = this.createDivWithClass('carousel__overflow')
this.root.setAttribute('tabindex','0') // Ajoute un nouvel attribut a root et définis la navigation par les fleches du clavier
if (this.options.overflow) {
this.root.appendChild(this.overflow)
this.overflow.appendChild(this.container)
} else {
this.root.appendChild(this.container) // Insère la DIV carousel__container dans la DIV carousel
}
this.element.appendChild(this.root) // Crée une DIV avec l'élément "root" dans l'élément #blocCarousel
this.items = children.map((child) => { // Utilisation de la methode forEach sur mes éléments enfants,crée un nouveau tableau avec les résultat de la fonction fléchée
let item = this.createDivWithClass('carousel__item') // Création de mes container parents avec la class createDivWidthClass
item.appendChild(child) // On rajoute les enfants dans les "items"
return item
})
// Slide infini,si l'option est active
if (this.options.infinite) {
this.offset = this.options.slidesVisible + this.options.slidesToScroll // Récupere les items hors champs
if (this.offset > children.length) { // Vérifie qu'il y ai assez d'éléments dans le caroussel pour le slide infinis
console.error("Vous n'avez pas assez d'élément dans le carousel",element) // Retourne un message d'erreur
}
this.items = [ // Concatene les tableau des items
...this.items.slice(this.items.length - this.offset).map(item => item.cloneNode(true)),// Ajoute les slides precedant en les clonant et retourne les items clonés avec les enfants
...this.items,// Ajoute la liste d'items
...this.items.slice(0,this.offset).map(item => item.cloneNode(true)),// Ajoute les slides suivant en partant de l'index 0 et utilisation de map et cloneNode
]
this.gotoItem(this.offset) // Appel de la methode GotoItem avec l'offset en index
}
this.items.forEach(item => this.container.appendChild(item)) // On rajoute l'enssemble de nos items dans le container
this.setStyle()
// Creation de la navigation,si l'option est active
if (this.options.navigation) {
this.createNavigation()
}
// Creation de la navigation,si l'option est active
if (this.options.pagination) {
this.createPagination()
}
// Evenements
this.moveCallbacks.forEach(cb => cb(this.currentItem))
this.onWindowResize() // Appel de la methode pour redimensionement sur mobile
window.addEventListener('resize',this.onWindowResize.bind(this)) // Vérifie le redimensionnement de la fenetre pour le responsive du carousel
this.root.addEventListener('keyup',e => { // Ajoute un evenement au relachement des touches du clavier
if (e.key === 'ArrowRight' || e.key === 'Right') { // Definis la touche flêche de droite
this.next() // Lui passe la methode next
} else if (e.key === 'ArrowLeft' || e.key === 'Left') { // Definis la touche flêche de gauche
this.prev() // Lui passe la methode prev
}
})
if (this.options.infinite) {
this.container.addEventListener('transitionend',this.resetInfinite.bind(this)) // Attend que l'animation soit terminée
}
new CarouselTouchPlugin(this)
}
// Applique les bonnes dimensions aux éléments du carousel
setStyle () {
let ratio = this.items.length / this.slidesVisible // Nous donne le nombre d'éléments dans le carousel divisé par le nombre d'éléments visibles voulu
this.container.style.width = ( ratio * 100 ) + "%" // Applique à mon container une largeur égale au "ratio" X 100 en %
this.items.forEach(item => item.style.width = ((100 / this.slidesVisible) / ratio ) + "%" ) // régle le style des items : l'élément visible divisé par le ratio sur 100 et on ajoute le pourcentage
}
// Methode de création de la navigation dans le DOM
createNavigation() {
let nextButton = this.createDivWithClass('carousel__next') // Crée le bouton next
let prevButton = this.createDivWithClass('carousel__prev') // Crée le bouton prev
let playButton = this.createDivWithClass('carousel__play') // Crée le bouton play
this.root.appendChild(nextButton) // Place le bouton next dans le carousel
this.root.appendChild(prevButton) // Place le bouton prev dans le carousel
this.root.appendChild(playButton) // Place le bouton play dans le carousel
nextButton.addEventListener('click',this.next.bind(this)) // Ajoute un evenement sur le bouton,au click et effectue la methode next
prevButton.addEventListener('click',this.prev.bind(this)) // Ajoute un evenement sur le bouton,au click et effectue la methode prev
playButton.addEventListener('click',this.play.bind(this)) // Ajoute un evenement sur le bouton,au click et effectue la methode play
playButton.id = 'playButton' // Définis l'identifiant de playButton
// Vérifie que l'option loop soit true,et affiche les boutons de navigation. Si false alors ils sont supprimés
if (this.options.loop === true) {
return
}
// Appel de la methode onMove
this.onMove(index => {
// Supprime ou affiche le bouton prev
if (index === 0) { // Si l'index est égal à zero
prevButton.classList.add('carousel__prev--hidden') // Rajoute la CLASS hidden
} else {
prevButton.classList.remove('carousel__prev--hidden') // Supprime la CLASS hidden
}
// Supprime ou affiche le bouton next
if ( index >= this.items.length || (this.items[this.currentItem + this.slidesVisible] === undefined)) {
nextButton.classList.add('carousel__next--hidden')
} else {
nextButton.classList.remove('carousel__next--hidden')
}
})
}
/**
* Crée la pagination dans le DOM
*/
createPagination () {
let pagination = this.createDivWithClass('carousel__pagination')
let nextArrow = this.createImgWithClass('carousel__pagination__image','https://dev.aurela-promotion.fr/wp-content/uploads/2021/01/Fleche.svg')
let paginationText = this.createDivWithClass('carousel__pagination__text')
let buttons = []
this.root.appendChild(pagination)
for (let i = 0; i < this.items.length; i = i + this.options.slidesToScroll) {
let button = this.createDivWithClass('carousel__pagination__button')
button.addEventListener('click',() => this.gotoItem(i))
pagination.appendChild(button)
let number = i + 1
button.classList.add('carousel__pagination__button__' + number)
buttons.push(button)
}
pagination.appendChild(nextArrow)
pagination.appendChild(paginationText)
paginationText.addEventListener('click',() => this.next(this))
this.onMove(index => {
let activeButton = buttons[Math.floor(index / this.options.slidesToScroll)]
let activeText = buttons[Math.floor(index / this.options.slidesToScroll)]
if (activeButton) {
buttons.forEach(button => button.classList.remove('carousel__pagination__button--active'))
activeButton.classList.add('carousel__pagination__button--active')
} else if (activeText && activeButton) {
buttons.forEach(button => button.classList.remove('carousel__pagination__button--active'))
activeButton.classList.add('carousel__pagination__button--active')
}
})
}
translate (percent) {
this.container.style.transform = 'translate3d(' + percent + '%,0)'
}
/*
*
* Methodes pour les boutons next et prev
*
*/
next () {
console.log(this.currentItem)
this.gotoItem(this.currentItem + this.slidesToScroll) // Appel de la methode gotoItem et parametres : index de l'item + nombres de slide a defiler
}
prev () {
console.log(this.currentItem)
this.gotoItem(this.currentItem - this.slidesToScroll) // Appel de la methode gotoItem et parametres : index de l'item - nombres de slide a defiler
}
/*
*
* Methode pour le bouton play
*
*/
play () {
this.playButton = $('#playButton') // Init Bouton play
this.options.play ? this.options.play = false : this.options.play = true
if (this.options.play === true) { // Si l'option play est égal à true
this.playButton.removeClass('carousel__play') // Enlève la classe carousel__play
this.playButton.addClass('carousel__pause') // Ajoute la classe carousel__pause
this.interval = window.setInterval(() => { // Définis l'intervale
this.gotoItem(this.currentItem + 1,this.options.animation) // Appel de la methode GotoItem avec le slide auquel j'ajoute 1 et l'animation à true
},this.options.timer) // 5 secondes entre les défillements
} else if (this.options.play === false) { // Si option play est égal à false
this.playButton.removeClass('carousel__pause') // Enleve la classe carousel__pause
this.playButton.addClass('carousel__play') // Ajoute la classe carousel__play
clearInterval(this.interval) // Nettoyage de l'intervale
}
}
/**
*
* Déplace le carousel vers l'élément ciblé
* @param {number} index
* @param {boolean} [animation = this.options.animation]
*
*/
gotoItem (index,animation = this.options.animation) {
console.log(index)
if (index < 0) { // Si l'index est inférieur à 0 il faut revenir en arrière
if (this.options.loop) { // Si l'option loop est activée
index = this.items.length - this.slidesVisible // index = le nombres d'éléments,moins le nombres d'éléments visibles
} else {
return
}
// Si l'index est supérieur ou égal aux nombres d'éléments,ou currentItem + le nombres de slides visibles et vérifie si un item correspond
} else if (index >= this.items.length || (this.items[this.currentItem + this.slidesVisible] === undefined && index > this.currentItem)) {
if (this.options.loop) { // Si l'option loop est active
index = 0 // Alors l'index reviens à zero
} else {
return
}
}
// Animation de slide vers l'élément
let translateX = index * -100 / this.items.length // l'index de l'item courant X -100 / le nombres d'item
if (animation === false) { // Si l'option animation est false
this.disableTransition() // Aucune animation de transition
}
this.container.style.transform = 'translate3d(' + translateX + '%,0)' // Applique l'animation translate3d avec translateX,Y,Z
this.container.offsetHeight
if (animation === false) {
this.enableTransition()
}
this.currentItem = index // Définis l'item courant comme index
this.moveCallbacks.forEach(cb => cb(index)) // Appel des callbacks les uns après les autres avec en l'index courant en parametre
}
/**
* Déplace le container pour donner l'impression d'un slide infini
*/
resetInfinite () {
if (this.currentItem <= this.options.slidesToScroll) { // Si l'item courant est inférieur ou égal au nombre de slides à défiler
this.gotoItem(this.currentItem + (this.items.length - 2 * this.offset),false) // Déplacement vers la gauche,l'item courant auquel j'ajoute le nombre d'éléments
} else if (this.currentItem >= this.items.length - this.offset) {
this.gotoItem(this.currentItem - (this.items.length - 2 * this.offset),false) // Déplacement vers la droite
}
}
/**
*
* @param {moveCallback} cb
*/
onMove (cb) {
this.moveCallbacks.push(cb) // Ajoute les callbacks dans mon instance tableau
}
/*
*
* Methode pour le responsive du carousel
*
*/
onWindowResize () {
let mobile = window.innerWidth < 1200 // Déclare la variable mobile qui cible la fenetre avec une largeur inferieure à 1200px
if (mobile !== this.isMobile) { // Si la valeur de mobile est differente de celle de this.isMobile
this.isMobile = mobile // Change la valeur de la propriété d'instance
this.setStyle() // Aplique le style
this.moveCallbacks.forEach(cb => cb(this.currentItem)) // Rappel des callBacks avec l'item courant
}
}
/**
*
* Création de la methode pour créer les div
* @param {string} className
* @returns {HTMLElement}
*
*/
// Parametre nom de CLASS
createDivWithClass (className) {
let div = document.createElement('div') // Ajoute un élément HTML de type DIV
div.setAttribute('class',className) // lui ajoute l'attribut CLASS et en parametre le nom de la CLASS
return div
}
disableTransition () {
this.container.style.transition = 'none'
}
enableTransition () {
this.container.style.transition = ''
}
/**
*
* Création de la methode pour créer les balises image
* @param {string} className
* @returns {HTMLElement}
*
*/
// Parametre nom de CLASS
createImgWithClass (className,url) {
let img = document.createElement('img') // Ajoute un élément HTML de type IMG
img.setAttribute('class',className) // Lui ajoute l'attribut CLASS et en parametre le nom de la CLASS
img.setAttribute('src',url)
img.setAttribute('alt','Fleche de navigation du carousel')
return img
}
/**
* Nombre d'éléments à faire defiler
* @returns {number}
*/
get slidesToScroll () {
return this.isMobile ? 1 : this.options.slidesToScroll // Si le support est un mobile alors on retourne 1 ou sinon le parametre slidesToScroll
}
/**
* Nombres d'éléments à afficher
* @returns {number}
*/
get slidesVisible () {
return this.isMobile ? 1 : this.options.slidesVisible // Si le support est un mobile alors on retourne 1 ou sinon le parametre slidesVisibles
}
/**
* @returns {number}
*/
get containerWidth () {
return this.container.offsetWidth
}
/**
* @returns {number}
*/
get carouselWidth () {
return this.root.offsetWidth
}
}
感谢您的时间!
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)