|
|
<template> <view> <swiper class="swiper" :animation="animationData" :style="{ 'opacity': animation ? '0' : '1', 'background-color': perspective ? 'rgba(0,0,0,0.5)' : '#000000'}" :current="swiper_current" :circular="circular" v-if="show_assembly" @click="hideAssembly" @transition="transition" @touchmove.stop.prevent="touchmove" @change="e => switchItem(e.detail.current)"> <swiper-item v-for="(item, index) in images" :key="index" class="swiper-item"> <!-- 可移动缩放操作的容器 --> <movable-area class="movable-area" :scale-area="true"> <movable-view class="movable-view" direction="all" :scale="true" :inertia="true" @scale="scale" :scale-value="scale_values[swiper_current]"> <image :src="item" class="swiper-image" mode="aspectFit" @mousewheel.stop="mousewheel" @longpress="longpress"></image> </movable-view> </movable-area> <!-- 控制器 --> <view class="controls" v-if="controls"> <view> <view @click.stop="switchItem(swiper_current - 1)" v-if="swiper_current != 0"> <uni-icons type="arrowleft" size="36" color="#fff"></uni-icons> </view> <view v-else></view> </view> <view> <view @click.stop="switchItem(swiper_current + 1)" v-if="swiper_current != images.length - 1"> <uni-icons type="arrowright" size="36" color="#fff"></uni-icons> </view> <view v-else></view> </view> </view> <!-- 指示器 --> <view class="indicators" v-if="indicators && images.length > 1"> <view class="number" v-if="indicatorType == 'number'">{{ swiper_current + 1 }} / {{ images.length }}</view> <view class="square" v-else-if="indicatorType == 'square'"> <view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view> </view> <view class="dot" v-else-if="indicatorType == 'dot'"> <view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view> </view> </view> <!-- 底部菜单弹出层 --> <view class="menu-popup" v-if="menu && show_menu" @click.stop> <button @click="download">保存图片</button> </view> </swiper-item> </swiper> </view></template>
<script> export default { props: { controls: { type: Boolean, // 是否显示左右箭头控制
default: true }, indicators: { type: Boolean, default: true // 是否显示指示器
}, indicatorType: { type: String, // 指示器类型,dot圆点,square方形,number数字
default: 'number' }, perspective: { type: Boolean, // 是否开启背景透视,遮罩透明可以看见底下其他元素
default: true }, circular: { type: Boolean, // 是否可以衔接滑动
default: false }, themeColor: { type: String, // 主题色
default: '#1833F2' }, animation: { type: Boolean, // 是否开启动画
default: false }, menu: { type: Boolean, // 长按图片时是否显示菜单按钮,H5不支持,传参无效果
default: true } }, data(){ return { swiper_current: 0, // 当前显示的图片下标
images: [], show_assembly: false, // 显示预览图片
duration: 500, // 动画持续时间
animationObj: {}, // 动画配置对象
animationData: {}, // 动画导出执行对象
scale_values: [], // 每张图的缩放比例
clientY: 0, show_menu: false } }, created(){ // TODO PC端屏蔽遮罩滚动穿透
// TODO PC端滚轮放大缩小
// TODO 双击时会被关掉
}, methods: { // 初始化加载动画
initAnimation(){ let animationObj = uni.createAnimation({ duration: this.duration }); this.animationObj = animationObj; animationObj.opacity(0).step(); animationObj.opacity(1).step(); this.animationData = animationObj.export(); }, // swiper touchmove事件
touchmove(){ return false; }, // swiper 位置发生变化
transition(){ if(this.show_menu){ this.show_menu = false; } }, // swiper被改变
switchItem(current){ this.swiper_current = current; // this.clientY = 0; // 切换时需要复位
}, // movable 缩放事件
scale(event){ let scale = parseInt(event.detail.scale * 100) +'%'; uni.showToast({ title: scale, icon: 'none', position: 'bottom' }) }, // 鼠标滚动缩放事件,目前会触发浏览器页面滚动事件,暂时先不要这个功能
mousewheel(event){ return; let scale = this.scale_values[this.swiper_current]; if(this.clientY > event.clientY){ if(scale < 10){ scale += 0.5; } }else{ if(scale > 0.5){ scale -= 0.5; } } this.clientY = event.clientY; this.scale_values.splice(this.swiper_current, 1, scale); }, // 显示图片
show(options){ this.images = options.images || []; this.swiper_current = options.current || 0; this.scale_values = this.images.map(item => 1); this.show_assembly = true; if(this.$props.animation){ this.initAnimation(); } }, // 关闭隐藏图片
hideAssembly(){ if(this.show_menu){ this.show_menu = false; return; } if(this.$props.animation){ this.animationObj.opacity(0).step(); this.animationData = this.animationObj.export(); setTimeout(() => { this.show_assembly = false; }, this.duration); }else{ this.show_assembly = false; } }, // 长按事件
longpress(event){ // #ifndef H5
if(this.$props.menu){ this.show_menu = true; } // #endif
}, // 保存图片
download(){ uni.showLoading({ title: '正在保存' }) this.show_menu = false; uni.downloadFile({ url: this.images[this.swiper_current], success: res => { let tempFilePath = res.tempFilePath; uni.saveFile({ tempFilePath, success: result => { uni.showToast({ title: '保存成功', icon: 'success' }) }, fail: err => { uni.showToast({ title: '保存失败', icon: 'none' }) }, complete: () => uni.hideLoading() }) }, complete: () => uni.hideLoading() }) } } }</script>
<style lang="scss" scoped="scoped"> .swiper{ width: 100vw; height: 100vh; position: fixed; top: 0; left: 0; z-index: 1000; .swiper-item{ position: relative; .movable-area, .movable-view, .swiper-image{ width: 100%; height: 100%; } .controls{ position: absolute; padding: 0 16rpx; box-sizing: border-box; display: flex; justify-content: space-between; top: 47vh; left: 0; font-size: 40rpx; width: 100%; z-index: 1002; } .indicators{ width: 100%; position: absolute; left: 0; bottom: 10vh; z-index: 1002; display: flex; justify-content: center; .number, .square, .dot{ width: max-content; height: max-content; background-color: #dfe4ea; } .number{ padding: 4rpx 26rpx; border-radius: 40rpx; color: #555555; } .square{ border-radius: 40rpx; display: flex; padding: 2rpx 4rpx; view{ border-radius: 50%; } } .dot{ display: flex; padding: 4rpx 4rpx; } .indicators-icon{ width: 20rpx; height: 20rpx; background-color: #bdc5bd; margin: 2rpx; } } } } .menu-popup{ position: fixed; bottom: 0; left: 0; height: max-content; width: 100%; background-color: #FFFFFF; z-index: 1004; padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); button{ height: 100rpx; line-height: 100rpx; background-color: transparent; border-bottom: 1rpx solid #e5e5e5; &:last-child{ border-bottom: none; } } } // 轻提示框样式
/deep/.uni-sample-toast{ z-index: 1002; }</style>
|