uniapp 关于swiper组件和moveable-area、moveable-view组件搭配的图片预览高度集成组件

swper组件内嵌moveable-area

该组件包含:自动分页,手势滑动,双指缩放和双击缩放,放大拖动到边缘自动下一张等功能


	<template>
    <view class="img">
		<uni-transition class="topLine" :mode-class="['fade','slide-top']" :show="show">
			<view class="top-text">{{plist[index].current}}</view>
		</uni-transition>
		<uni-transition class="navback" :mode-class="['fade','slide-top']" :show="show">
			<view @click="goback">
				<uni-icons type="close" size="27" color="#fff"></uni-icons>
			</view>
		</uni-transition>
		<view class="img"
		 @touchstart="touchstart"
		 @touchend="touchend"
		 >
			<uni-transition :show="show">
				<view class="top">
					<text>{{tip}}</text>
					<text v-if="tabIndex==2&&platform==='ios'">{{iosTip}}</text>
				</view>
			</uni-transition>
			<!-- 压缩图 -->
			<swiper class="swiper"
			 v-if="tabIndex==1"
			 :current="index"
			 duration="500"
			 :disable-touch="!swiper"
			 @transition="scale = false"
			 @animationfinish="scale = true"
			 @touchstart="handletouchstart"
			 @touchend="handletouchend">
			    <swiper-item class="swiper-item" v-for="(info,pos) in plist" :key="pos">
					<movable-area class="marea" scale-area>
						<movable-view
							class="mview"
							direction="all"
							damping="100"
							friction="2"
							:scale="scale"
							scale-min="1"
							scale-max="8"
							:out-of-bounds="temporaryScaleValue!=1"
							:scale-value="scaleValue"
							:animation="animation"
							@change="changeMove"
							@click="dbclick"
							@scale="handleScale">
							<image
								@load="loadover"
								class="image"
								:src="info.logo_name"
								mode="widthFix" />
						</movable-view>
					</movable-area>
			    </swiper-item>
			</swiper>
			<!-- 高清图 -->
			<image
				v-if="tabIndex==2"
				@load="loadover"
				class="img-src"
				:src="info.cloud_name"
				mode="widthFix"
			/>
		    
		</view>
		<uni-transition class="anbottom" :mode-class="['fade','slide-bottom']" :show="show">
			
			<view class="tab">
				<view class="tab-btn">
					<view class="label" :class="tabIndex===1 ? 'active' : ''" @click="tabChange(1)">水印高清图</view>
					<view class="label" :class="tabIndex===2 ? 'active' : ''" @click="tabChange(2)">查看原图({{plist[index].size}})</view>
				</view>
			</view>
		</uni-transition>
		<!-- 遮罩层 -->
		<view class="mask" v-if="!imgloadover"></view>
		
    </view>
</template>

<script>
	import {toast,checkWeChat} from '@/common/checkForm.js'
    export default {
		props:{
			activity_id: {
				type: [String,Number],
				default: ''
			},
			img_id: {
				type: [String,Number],
				default: ''
			},
			keywords: [Number,String],
			type_id: [Number,String],
			shooting_time: String,
			page_size: {
				type: [String,Number],
				default: 12
			},
			data: {
				type: Object,
				default: {}
			}
		},
        data() {
            return {
                info: {},
                screen_width: '',
				tip: '长按图片进行保存',
				platform: '',
				iosTip: 'IOS系统建议保存后查看',
                // value: '',
				tabIndex: 1,
				moveStartX: '',
				imgloadover: false,
				plist: [],
				current_page: null,
				// page_size: 12,
				total: 0,
				index: 0,
				isClick: true,
				leftController: false,
				strat: 0,
				end: 0,
				tipShow: true,
				pay_text: '',
				
				touchNum: 0, //双击控制
				show: true, //动画控制
				fingers: false,
				swiper: true,
				scale: true,
				animation: true,
				scaleValue: 1,
				temporaryScaleValue: 1,
            }
        },
        created() {
            this.screen_width = uni.getSystemInfoSync().windowWidth
			this.platform = uni.getSystemInfoSync().platform
			this.getPhoto()
        },
        methods: {
			goback() {
				if(!this.isClick) return
				this.$emit('click')
			},
			tabChange(e){
				if(this.tabIndex == e){
					return
				}
				//复原部分配置
				this.scaleValue = 1
				this.swiper = true
				this.animation = true
				this.temporaryScaleValue = 1
				
				this.tabIndex = e || this.tabIndex
				if(this.tabIndex == 1){
					this.info = this.plist[this.index]
				}
				if(this.tabIndex == 2){
					this.getImg()
				}
			},
			//获取照片
			getPhoto(){
				this.plist = this.data.data;
				this.current_page = this.data.current_page;
				this.total = this.data.total;
				if(!this.plist || this.plist.length === 0){
					uni.showToast({
					    icon: 'none',
					    title: '没有可展示的图片'
					})
				}
				
				
				//二次优化
				if(this.current_page){
					let index = 0
					let subGroupLength = this.page_size
					this.newArray = []
					while(index < this.plist.length) { //按页分组
						this.newArray.push(this.plist.slice(index, index += subGroupLength));
					}
					this.newArray.forEach((item,index)=>{
						for(let i=0;i<item.length;++i){
							if(this.img_id == item[i].id){
								this.current_page = index + 1 //重新赋值
								if(i===0 && index > 0){
									this.index = this.page_size
									this.plist = this.newArray[index-1].concat(item)
									this.leftController = true
								}else{
									this.index = i
									this.plist = item
								}
								this.info = item[i]
								return
							}
						}
					})
				}else{
					this.plist.forEach((item,index)=>{
						if(this.img_id == item.id){
							this.index = index
							this.info = item
						}
					})
				}
			},
			loadover(e){
				uni.hideLoading()
				this.imgloadover = true
			},
			touchstart(e){
				this.strat = new Date().getTime()
			},
			touchend(e){
				this.end = new Date().getTime()
				if(this.end - this.strat < 700){ //解决ios微信浏览器图片长按和点击事件冲突的问题
					this.isClick = true
				}else{
					this.isClick = false
				}
			},
			changeMove(e){
				this.scaleValue = this.temporaryScaleValue
				if(e.detail.source === 'touch-out-of-bounds'
				 && (e.detail.x > 0 || e.detail.x < (this.scaleValue > 1 ? (this.scaleValue < 2 ?-this.screen_width*this.scaleValue*0.1 : -this.screen_width) : 0))){
					
					this.scaleValue = 1
					this.swiper = true
					this.animation = false
					this.temporaryScaleValue = 1
					
					this.tabChange(1)
				}
			},
			dbclick(e){
				  let curTime = new Date().getTime();
				  let lastTime = this.touchNum;
				  this.touchNum = curTime;
				  let diff = curTime - lastTime;
				  if (diff < 300) { //双击
					this.scaleValue = this.scaleValue === 1 ? 2 : 1
					clearTimeout(this.lastTapTimeoutFunc); // 成功触发双击事件时,取消单击事件的执行
				 } else { //单击
				   this.lastTapTimeoutFunc = setTimeout(()=> {
						this.show = !this.show
				   	}, 300);
				}
			},
			swiperChange(){
				this.scale = false
			},
			animationfinish(){
				this.scale = true
				this.animation = true
			},
			handleScale(e){
				this.temporaryScaleValue = e.detail.scale
				clearTimeout(this.timer)
				if(e.detail.scale != 1){
					this.swiper = false
				}else{
					this.timer = setTimeout(() => {
						this.scaleValue = 1
						this.swiper = true
					}, 200);
				}
			},
			handletouchstart(e){
				this.fingers = e.touches.length >= 2 ? true : false
				this.moveStartX = e.changedTouches[0].clientX
			},
			handletouchend(e){
				if(!this.swiper || this.fingers){
					return
				}
				let moveX = e.changedTouches[0].clientX - this.moveStartX
				if(moveX > 20){ // && this.moveStartX < 75
					// console.log('左滑');
					if(this.index === 0){
						uni.showToast({
						    icon: 'none',
						    title: '没有更多图片了'
						})
						return
					}
					//二次优化
					if(this.current_page && this.current_page > 1 && this.index === 1 && !this.leftController){
						this.current_page --
						this.plist = this.newArray[this.current_page-1].concat(this.plist)
						this.total = this.plist.length
						this.index = this.page_size
						return
					}
					this.index --
					this.tabChange()
				}
				if(moveX < -20){ // && this.moveStartX > 300
					// console.log('右滑');
					if(this.index === this.total-1){
						uni.showToast({
						    icon: 'none',
						    title: '没有更多图片了'
						})
						return
					}
					//请求下一页
					if(this.current_page && this.index === this.plist.length-1 && this.index < this.total-1){
						this.current_page ++
						uni.showLoading({
							title: '图片加载中'
						})
						//调用api 自己封装
						this.$api.get('list', {
						    activity_id: this.activity_id,
							keywords: this.keywords,
							type_id: this.type_id,
							shooting_time: this.shooting_time,
						    page_size: this.page_size,
						    page: this.current_page,
						},'none').then(res => {
						    this.plist = this.plist.concat(res.data.data);
							this.total = res.data.total
							this.index ++
							this.leftController = this.index >= this.page_size
						})
						return
					}
					this.index ++
					this.tabChange()
				}
			},
            //获取原始图片
            getImg() {
				this.imgloadover = false
				uni.showLoading({
					title: '图片加载中'
				})
                this.$api.get('sport/photo/info', {
                    id: this.plist[this.index].id
                },'none').then(res => {
                    this.info = res.data
                })
            },
        }
    }
</script>

<style lang="scss" scoped>
	.topLine{
		position: fixed;
		top:20rpx;
		left: 20rpx;
		z-index: 100;
		.top-text{
			color: #FFFFFF;
		}
	}
	.navback{
		position: fixed;
		top:20rpx;
		right: 20rpx;
		z-index: 100;
	}
    .img {
        display: flex;
        flex-direction: column;
        justify-content: center;
        background-color: #000;
        width: 750rpx;
		height: 100%;
		font-size: 28rpx;
		.swiper{
			width: 100%;
			height: 100%;
		}
		.swiper-item{
			display: flex;
			align-items: center;
			justify-content: center;
		}
        .img-src {
            width: 750rpx;
			height: auto;
			overflow: hidden;
			position: absolute;
        }
		
		.movable-view{
			display: flex;
			width: 100%;
			height: auto;
		}
		.img-source{
			width: 100%;
		}
    }
	.marea {
		height: 100%;
		width: 100%;
		position: fixed;
		overflow: hidden;
		.mview {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 100%;
			height: auto;
			min-height: 100%;
			.image {
				width: 100%;
				transform: translate3d(0, 0, 0) scale(1, 1);
			}
		}
	}
	.mask {
	    width: 100%;
		height: 100%;
		background-color: rgba(0, 0, 0, 0);
		position: fixed;
		top: 0;
		z-index: 9;
	}
	.top{
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 8rpx 24rpx;
		background-color: rgba(0, 0, 0, .4);
		border-radius: 40rpx;
		color: #FFFFFF;
		position: fixed;
		top: 200rpx;
		left: 50%;
		transform: translate(-50%);
		z-index: 99;
	}
	.anbottom{
		position: fixed;
		bottom: 0;
		z-index: 99;
	}
	.tab{
		padding: 30rpx;
		width: 750rpx;
		display: flex;
		justify-content: space-between;
		flex-direction: row;
		color: #FFFFFF;
		background-color: rgba(0, 0, 0, .4);
		.tab-btn{
			flex-direction: row;
			align-items: center;
			.label{
				&.active{
					color: #ffff00;
				}
				&+.label{
					padding-left: 50rpx;
				}
			}
			.img-icon{
				width: 36rpx;
				height: 32rpx;
			}
		}
	}
    .content {
        font-size: 30rpx;
        color: #BFBFBF;
		width: 750rpx;
		height: 330rpx;
		background-color: rgba(0, 0, 0, .8);
		position: fixed;
		bottom: 0;
		z-index: 99;
		.payBtn{
			position: fixed;
			right: 30rpx;
			bottom: 30rpx;
			.pay-button{
				color: #333333;
				background-color: #efefef;
				border-radius: 8rpx;
				margin: 0;
				padding: 10rpx 20rpx;
			}
			.money{
				margin-bottom: 10rpx;
				display: flex;
				justify-content: center;
				align-items: baseline;
				color: #FFFFFF;
				.number{
					font-size: 40rpx;
				}
			}
		}
		.tab-content{
			padding: 140rpx 40rpx 0 40rpx;
		}
        .simple-text {
            padding: 20rpx;
            display: flex;
            flex-direction: column;
            box-sizing: border-box;

            .line {
                display: flex;
                padding: 10rpx 0;

                .px {
                    margin-right: 40rpx;
                }
            }

            .color-75 {
                color: #757575;
            }
        }

        .download {
            position: fixed;
            bottom: 0;
            color: #BFBFBF;
            font-size: 40rpx;
            width: 750rpx;
            background-color: rgba(0, 0, 0, .8);
            display: flex;
            justify-content: center;
            align-items: baseline;
            line-height: 88rpx;

            .money {
                font-size: 28rpx;
                margin-right: 40rpx;
            }
        }
    }
</style>


vue页面引用
包含一些参数用到用不到看自己需求

<view class="preview" @touchmove.stop.prevent="moveHandle"> //阻止底层页面滚动
	<image-preview
	 v-if="preview" //必须可以改写在组件内部使用$refs获取组件后用open方法调用
	 :activity_id="activity_id"
	 :img_id="img_id" //图片id必须
	 :keywords="keywords"
	 :type_id="activeId"
	 :shooting_time="timeRange"
	 :page_size="pageSize" //分页必须
	 :data="plist" //json对象数组列表必须
	 @click="preview = false">
	</image-preview>
</view>

插件地址

简化版插件地址:https://ext.dcloud.net.cn/plugin?id=17517

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值