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