Доброе утро, я создал собственную карусель vue и внедрил ее в свое прогрессивное веб-приложение. Отлично работает на Chrome Android, тогда как на iOS Safari - id нет. Я получил отзыв о том, что карусель позволяет скользить только первой странице, а на второй - не реагирует.
Проблема в том, что у меня нет Ма c или Iphone, и пока я не могу сам это проверить. Может ли кто-нибудь помочь мне и протестировать карусель на вышеупомянутых устройствах? Вот скрипка.
https://jsfiddle.net/LSliwa/97cpgq3z/
HTML:
<div id="app">
<Carousel>
<div v-for="(todo, index) in todos" :key="index">
<div class="app__element">
{{ todo.text }}
</div>
</div>
</Carousel>
</div>
CSS:
#app {
padding-top: 1rem;
}
.app__element {
text-align: center;
border: 1px solid red;
padding: 1rem;
margin: 0 1rem;
}
/* carousel styles */
.carousel {
overflow-x: hidden;
overflow-y: visible;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.carousel-navdots {
display: flex;
justify-content: center;
margin-bottom: 2rem;
}
.carousel-navdot {
height: 10px;
width: 10px;
border-radius: 50%;
background-color: gray;
margin: 0 5px;
transition: all 0.5s;
cursor: pointer;
}
.active {
background-color: #05AA19;
}
.carousel-wrapper {
display: flex;
align-items: stretch;
cursor: grab;
}
.carousel-wrapper:active {
cursor: grabbing;
}
.carousel-wrapper>div,
.carousel-wrapper>p,
.carousel-wrapper>span,
.carousel-wrapper>ul {
width: 100%;
flex-shrink: 0;
position: relative;
}
.scrolling {
transition: transform 0.5s;
}
.inactive {
flex-direction: column;
}
@media (min-width: 1024px) {
.inactive {
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
}
JS :
Vue.component('Carousel', {
template: '<div class="carousel"><div class="carousel-navdots" v-if="isActive" v-show="pagination"><div class="carousel-navdot" :class="{ \'active\': n == currentPage + 1 }" v-for="n in numberOfPages" v-show="numberOfPages > 1" :key="n" ref="navdot" @click="scrollWithNavdots(n)"></div></div><div class="carousel-wrapper a-stretch" :class="{ \'inactive\': !isActive }" :style="{ transform: `translateX(${translate}px)` }" ref="wrapper" v-on="isActive && active ? { touchstart: onTouchStart, touchmove: onTouchMove, touchend: onTouchEnd, mousedown: onTouchStart, mousemove: onTouchMove, mouseup: onTouchEnd } : {}"><slot></slot></div></div>',
props: {
active: {
type: Boolean,
default: () => true
},
activeOnViewport: {
type: Array,
default: () => [[1, true]]
},
columns: {
type: Array,
default: () => [[1, 1]]
},
pagination: {
type: Boolean,
default: () => true
},
sensitivity: {
type: Number,
default: () => 40
},
startFromPage: {
type: Number,
default: () => 0
},
autoplay: {
type: Boolean,
default: () => false
},
autoplaySpeed: {
type: Number,
default: () => 5
},
// usuń custom length jeżeli rerendering z :key zadziała
customLength: {
type: Number
}
},
data() {
return {
currentPage: this.startFromPage,
numberOfColumns: 1,
moveStart: null,
move: null,
currentTranslate: 0,
length: this.customLength == null ? this.$slots.default.length : this.customLength,
viewportColumnsMatched: null,
isActive: null,
mousedown: false,
elementWidth: 0,
autoplayInterval: null,
animateTimeout: null,
test: {
touchmove: false,
touchstart: false,
}
}
},
computed: {
maxScrollLeft() {
return this.currentPage == 0 ? true : false;
},
maxScrollRight() {
return this.currentPage + 1 == this.numberOfPages ? true : false;
},
numberOfPages() {
return Math.ceil(this.length / this.numberOfColumns);
},
sortedColumns() {
return this.columns.sort((a, b) => {
return a[0] - b[0];
});
},
sortedActive() {
return this.activeOnViewport.sort((a, b) => {
return a[0] - b[0];
})
},
translate() {
return -this.elementWidth * this.currentPage
}
},
watch: {
currentPage() {
this.animateCarousel();
this.$emit('change-page', this.currentPage);
},
startFromPage() {
this.currentPage = this.startFromPage;
},
// usuń watch customLength jeżeli rerendering z :key zadziała
customLength() {
this.length = this.customLength;
if (this.currentPage > this.length - 1) {
this.currentPage = this.length - 1;
}
}
},
methods: {
animateCarousel() {
this.$refs.wrapper.classList.add('scrolling');
this.animateTimeout = setTimeout(() => {
this.$refs.wrapper.classList.remove('scrolling');
}, 500);
},
scrollWithNavdots(index) {
this.currentPage = index - 1;
this.currentTranslate = parseFloat(this.$refs.wrapper.style.transform.slice(11, -3));
},
onTouchStart() {
clearInterval(this.autoplayInterval);
if (event.type == 'touchstart') {
this.moveStart = event.touches[0].screenX
} else {
this.moveStart = event.screenX;
this.mousedown = true;
}
},
onTouchMove() {
let translate;
if (event.type == 'touchmove') {
this.move = event.touches[0].screenX - this.moveStart;
} else if (event.type == 'mousemove' && this.mousedown == true) {
this.move = event.screenX - this.moveStart
}
if (this.move < 0 && this.maxScrollRight || this.move > 0 && this.maxScrollLeft) {
translate = this.translate + this.move*0.2;
} else {
translate = this.translate + this.move*0.5;
}
this.$refs.wrapper.style.transform = `translateX(${translate}px)`;
},
onTouchEnd() {
this.test.touchstart = false;
this.test.touchmove = false;
if (Math.abs(this.move) > this.sensitivity) {
if (this.move > 0 && !this.maxScrollLeft) {
this.currentPage--
} else if (this.move < 0 && !this.maxScrollRight) {
this.currentPage++
} else {
this.animateCarousel();
}
} else if (Math.abs(this.move) < this.sensitivity && Math.abs(this.move) > 1) {
this.animateCarousel();
}
this.$refs.wrapper.style.transform = `translateX(${this.translate}px)`;
this.mousedown = false;
this.moveStart = null;
this.move = null;
},
setColumns() {
this.viewportColumnsMatched = false;
this.sortedColumns.forEach(cur => {
if (window.matchMedia(`(min-width: ${cur[0]}px)`).matches) {
this.viewportColumnsMatched = true;
this.numberOfColumns = cur[1];
this.$refs.wrapper.childNodes.forEach(cur => {
cur.style.width = `${100/this.numberOfColumns}%`;
});
}
});
if (!this.viewportColumnsMatched) {
this.numberOfColumns = 1;
this.$refs.wrapper.childNodes.forEach(cur => {
cur.style.width = '100%';
});
}
setTimeout(() => {
this.elementWidth = this.$slots.default[0].elm.offsetWidth;
});
},
setActive() {
this.sortedActive.forEach(cur => {
if (window.matchMedia(`(min-width: ${cur[0]}px)`).matches) {
this.isActive = cur[1];
}
});
},
runCarousel() {
if (this.autoplay) {
this.autoplayInterval = setInterval(() => {
this.currentPage++
if (this.currentPage == this.numberOfPages) this.currentPage = 0;
}, this.autoplaySpeed * 1000);
}
},
},
mounted() {
this.setColumns();
this.setActive();
this.runCarousel();
window.addEventListener('resize', () => {
this.setColumns();
this.setActive();
});
},
destroyed() {
clearInterval(this.autoplayInterval);
clearTimeout(this.animateTimeout);
}
});
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
})
Заранее благодарю за помощь, добрые люди.