<template>
	<view class="sy-swiper" :style="{
		backgroundColor: bgColor,
		height: addUnit(height),
		padding: padding,
		borderRadius: addUnit(radius)
	}">
		<view v-if="showButton" class="sy-swiper__operation left" @click="slideLeft" :style="{
			width: addUnit(buttonSize),
			height: addUnit(buttonSize),
			background: buttonBgColor,
			fontSize: addUnit(buttonSize)
		}">
			<slot name="left-button"></slot>
		</view>
		<view class="sy-swiper__panel" @touchstart="onTouchStart" @touchend="onTouchEnd">
			<view class="sy-swiper__panel-item" v-for="(item, index) in images" :key="index" :style="{
	                    transform: itemStyles[index].transform,
	                    zIndex: itemStyles[index].zIndex,
	                    opacity: itemStyles[index].opacity,
						transitionDuration: millisecondsToSeconds(duration) + 's',
						transitionTimingFunction: easing
	                }">
				<view class="sy-swiper__panel-item__content" @click="$emit('click', item, index)">
					<template v-if="showFirstImageOnly">
						<image class="slide" :src="item.url" :mode="imgMode" v-if="itemStyles[index].isTop" :style="{
							borderRadius: addUnit(slideRadius)
						}"></image>
						<view class="slide-mask" v-else :style="{ background: maskBgColor, borderRadius: addUnit(slideRadius) }"></view>
					</template>
					<template v-else>
						<image class="slide" :src="item.url" :mode="imgMode" :style="{ borderRadius: addUnit(slideRadius) }">
						</image>
					</template>
					<text class="desc" :class="{ 'text': descNoWrap }" v-if="showDesc && item.desc" :style="{ background: descBgColor, color: descColor, fontSize: addUnit(descSize), borderRadius: `0 0 ${addUnit(slideRadius)} ${addUnit(slideRadius)}` }">{{ item.desc }}</text>
				</view>
			</view>
		</view>
		<view v-if="showButton" class="sy-swiper__operation right" @click="slideRight" :style="{
			width: addUnit(buttonSize),
			height: addUnit(buttonSize),
			background: buttonBgColor,
			fontSize: addUnit(buttonSize)
		}">
			<slot name="right-button"></slot>
		</view>
	</view>
</template>

<script>
	import {
		addUnit,
		millisecondsToSeconds
	} from "./util.js";
	export default {
		props: {
			// 图片列表
			images: {
				type: Array,
				default: () => []
			},
			padding: {
				type: String,
				default: "10px"
			},
			// 是否自动切换
			autoplay: {
				type: Boolean,
				default: false
			},
			// 动画类型
			easing: {
				type: String,
				default: "ease-in-out"
			},
			// 是否启用不透明度设定
			enableOpacity: {
				type: Boolean,
				default: true
			},
			// 最顶层图片的不透明度
			baseOpacity: {
				type: Number,
				default: 0.8
			},
			// 当前所在滑块的 index
			current: {
				type: Number,
				default: 0
			},
			// 滑块自动切换时间间隔（ms）
			interval: {
				type: Number,
				default: 2000
			},
			// 滑块切换过程所需时间（ms）
			duration: {
				type: Number,
				default: 500
			},
			// 左右边距
			horizontalMargin: {
				type: [String, Number],
				default: 10
			},
			// 上下边距
			verticalMargin: {
				type: [String, Number],
				default: 10
			},
			// 组件高度
			height: {
				type: [String, Number],
				default: 130
			},
			// 是否显示操作按钮
			showButton: {
				type: Boolean,
				default: false
			},
			// 按钮字体大小
			buttonSize: {
				type: [String, Number],
				default: 24
			},
			// 操作按钮背景颜色
			buttonBgColor: {
				type: String,
				default: "rgba(0, 0, 0, 0.26)"
			},
			// 图片裁剪模式，详情见微信原生imageMode
			imgMode: {
				type: String,
				default: "aspectFill"
			},
			// 是否只显示第一张图片，其他图片被蒙版遮住
			showFirstImageOnly: {
				type: Boolean,
				default: false
			},
			// 遮住蒙版的颜色
			maskBgColor: {
				type: String,
				default: "#ffffff"
			},
			// 是否显示图片描述（需要images传递的数据中存在desc属性）
			showDesc: {
				type: Boolean,
				default: true,
			},
			// 底部描述的背景
			descBgColor: {
				type: String,
				default: "rgba(0, 0, 0, 0.5)"
			},
			// 底部描述字体颜色
			descColor: {
				type: String,
				default: "#ffffff"
			},
			// 底部描述字体大小
			descSize: {
				type: [Number, String],
				default: 10
			},
			// 描述是否不换行，超出后用...省略
			descNoWrap: {
				type: Boolean,
				default: false
			},
			// 轮播图片的圆角值
			radius: {
				type: [String, Number],
				default: 4
			},
			// 容器背景色
			bgColor: {
				type: String,
				default: "#ffffff"
			},
			slideRadius: {
				type: [String, Number],
				default: 10
			}
		},
		// 切换
		emits: ["click", "change"],
		data() {
			return {
				slideNote: {
					x: 0,
					y: 0
				},
				itemStyles: [],
				screenWidth: 0,
				currentIndex: 0,
				autoSlideInterval: null,
				autoSlideTimeout: null
			}
		},
		mounted() {
			this.autoplay && this.doSomething();
		},
		onUnload() {
			this.autoSlideInterval = null;
			this.autoSlideTimeout = null;
		},
		beforeDestroy() {
			clearInterval(this.autoSlideInterval);
			clearTimeout(this.autoSlideTimeout);
			this.autoSlideInterval = null;
			this.autoSlideTimeout = null;
		},
		watch: {
			images: {
				handler(newImages) {
					var macInfo = uni.getSystemInfoSync();
					this.screenWidth = macInfo.screenWidth;
					this.itemStyles = newImages.map((_, index) => (this.getStyle(index)));
				},
				deep: true,
				immediate: true
			},
			current: {
				handler(newCurrentIndex) {
					this.scrollToCurrent(newCurrentIndex);
				},
				immediate: true
			}
		},
		methods: {
			addUnit,
			millisecondsToSeconds,
			doSomething() {
				this.$nextTick(() => {
					this.autoSlideInterval = setInterval(() => {
						this.slideRight();
					}, this.interval)
				})
			},
			getStyle(eIndex) {
				if (eIndex > this.images.length / 2) {
					var right = this.images.length - eIndex;
					return {
						transform: "scale(" + (1 - right / this.verticalMargin) + ") translate(-" + right * this
							.horizontalMargin + "%, 0px)",
						zIndex: 100 - right,
						opacity: this.enableOpacity ? this.baseOpacity / right : 1,
						isTop: eIndex === 0
					}
				} else {
					return {
						transform: "scale(" + (1 - eIndex / this.verticalMargin) + ") translate(" + eIndex * this
							.horizontalMargin + "%, 0px)",
						zIndex: 100 - eIndex,
						opacity: this.enableOpacity ? this.baseOpacity / eIndex : 1,
						isTop: eIndex === 0
					}
				}
			},
			restartTimer() {
				clearInterval(this.autoSlideInterval);
				this.autoSlideInterval = null;
			},
			scrollToCurrent(currentIndex) {
				// 清除现有的定时器，避免干扰
				this.restartTimer();
				// 这里选择用转动的方式，更改图片顺序会导致整个轮播的顺序错落
				for (let i = 0; i < currentIndex; i++) {
					this.slideRight();
				}
			},
			simulateStartMove(x) {
				this.slideNote.x = x;
			},
			onTouchStart(e) {
				this.restartTimer();
				this.slideNote.x = e.changedTouches[0] ? e.changedTouches[0].pageX : 0;
				this.slideNote.y = e.changedTouches[0] ? e.changedTouches[0].pageY : 0;
			},
			onTouchEnd(e) {
				var newList = JSON.parse(JSON.stringify(this.itemStyles));
				if (e.changedTouches[0].pageX - this.slideNote.x < -10) {
					// 向左滑动
					var last = [newList.pop()];
					newList = last.concat(newList);
					this.currentIndex = (this.currentIndex + 1) % this.images.length;
				} else if (e.changedTouches[0].pageX - this.slideNote.x >= 10) {
					// 向右滑动
					newList.push(newList[0]);
					newList.splice(0, 1);
					this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
				}

				this.itemStyles = newList;

				if (this.autoplay) {
					clearInterval(this.autoSlideInterval);
					clearTimeout(this.autoSlideTimeout);
					// 设置新的定时器，使用 this.interval 值作为间隔时间
					this.autoSlideTimeout = setTimeout(() => {
						this.slideRight();
					}, this.interval);
				}
			},
			slideLeft() {
				this.restartTimer();
				this.simulateStartMove(this.slideNote.x); // 模拟 touchstart，确保 slideNote.x 更新
				this.onTouchEnd({
					changedTouches: [{
						pageX: this.slideNote.x + 20
					}]
				});
				// 触发 change 事件并传递当前的索引
				this.$emit('change', this.currentIndex);
			},
			slideRight() {
				this.restartTimer();
				this.simulateStartMove(this.slideNote.x); // 模拟 touchstart，确保 slideNote.x 更新
				this.onTouchEnd({
					changedTouches: [{
						pageX: this.slideNote.x - 20
					}]
				});
				// 触发change 事件并传递当前的索引
				this.$emit('change', this.currentIndex);
			}
		}
	}
</script>

<style lang="scss" scoped>
	.sy-swiper {
		position: relative;
		box-sizing: border-box;

		.sy-swiper__operation {
			display: flex;
			justify-content: center;
			align-items: center;
			position: absolute;
			border-radius: 50%;
			background: rgba(0, 0, 0, 0.26);
			z-index: 999;

			&:active {
				filter: brightness(70%);
			}
		}

		.sy-swiper__operation.left {
			top: 50%;
			left: 10px;
			transform: translateY(-50%);
		}

		.sy-swiper__operation.right {
			bottom: 50%;
			right: 10px;
			transform: translateY(50%);
		}

		.sy-swiper__panel {
			width: 100%;
			height: 100%;
			overflow: hidden;
			position: relative;

			.sy-swiper__panel-item {
				height: 100%;
				width: 100%;
				position: absolute;
				top: 0;
				left: 0;
				transition-property: transform, opacity;
				// transition-duration: 0.4s;
				// transition-timing-function: ease-in-out;

				&__content {
					height: 100%;
					width: 330rpx;
					margin: 2rpx auto;
					position: relative;

					.slide {
						height: 100%;
						width: 100%;
					}

					.slide-mask {
						position: relative;
						height: 100%;
						width: 100%;
						z-index: 1;
					}

					.desc {
						position: absolute;
						width: 100%;
						bottom: 0;
						left: 0;
						padding: 10rpx 20rpx;
						box-sizing: border-box;
						box-shadow: 0rpx 4rpx 21rpx 0rpx rgba(0, 0, 0, 0.07);
						// border-radius: 0 0 20px 20px;
						font-family: PingFang SC;
						font-weight: 400;
						line-height: 32rpx;
						text-transform: none;
					}
				}
			}
		}
	}

	.text {
		overflow: hidden;
		white-space: nowrap;
		text-overflow: ellipsis;
	}
</style>