关于微信小程序 canvas 裁剪图片 放大 缩放功能

喜你入骨 提交于 2020-05-05 15:45:09
 1 关于微信小程序裁剪 放大任务功能
    第一步  先明确需要做成什么样的功能,目标需要明确。我们这边的需求就是上传一次图片进行裁剪 canvas 生成后图片直接进行商家购买<br>                                                            
    第二步   裁剪的过程注意网络加载情况,可能上传一张图片有几兆这个时候需要 给他一个等待时间,防止用户重先提交,导致信息不同意,这个时候我们要做的就是加一个loging  
	
    第三步 生成后需要对图片进行处理,好了直接上代码。
	
	创建一个文件夹,直接进行perbig文件夹 在js中我们直接进行变量的更新更业务操作   有些变量不需要 这个按照自己的情况进行获取
	data:{
			ratio: 750 / 750, //剪裁比例
			originUrl: '', //原始图片url
			cropperResult: '', //变化后结果
			windowWidt:'',
			originShow: '',
			cropperResultShow:'',
			originImgPics:'../../lib/images/shade.png',   //背景图
			base64: '', //base64
			cartypeId:'',   // 车型id
			carColorId:'',  // 颜色ID
			windowWidt:'',
			topImg:'',
			ismove:false,
			containerHeight:0,
			diffX:0,
			diffY:0,
			areaWidth:0,
			bgPic:null,
			bgPicTmp: null,
			hatsImgListArrOrder:[],
			hatsImgList:[],  // 单个商品信息
			hatsId:1,
			partId:'',
			pics:'',
			picSize:100,
			currentTab:0,
			hatsIdActive:'0',
			orderImgArray:[],  //  创建订单信息
			canWidth:'',
			canHeight:'',
			files:'',
			originUrlBase:'', //原图
			partMarkStatus:'',
			statusBig:false,
			dataNumber:0,
			appliqueIndex:0,
			hatsImgListArrNext:[],
			isPlayingMusic:true,
			src: '',
			uploadStatus:true,   // 上传状态信息
			animationData:{},
			showModalStatus:false,   // 默认不显示
			showModalStatusInfo:false,
			photographList:[],  // 图库
			currentIndex:-1,
			originUrlNumber:''
	}
	 getCropperImg(e) {
//将原图片url置空,表示已经完成剪裁,readFile剪裁后图片地址储存
var _this = this
wx.uploadFile({
  url: app.globalData.baseUrl+'rest/v1/file/upload', //后台上传api路径
  filePath:e.detail.url,
  name: 'file',
  header: { 
    "content-type": "multipart/form-data",
    "content-type": "application/x-www-form-urlencoded"
    },
  success: function(res){
      var dataImg = JSON.parse(res.data)
        _this.setData({
        base64:dataImg.data,
        cropperResult:e.detail.url
      })
      if(_this.data.base64) {
        _this.combinePic();  // 原始文件
      }
  },
     fail:e=>{
  }
 })
},
  onLoad: function (options) {
	var that = this
	var get1 = that.get1('cartypeId')
	var get2 = that.get2('carColorId')
	 wx.showLoading({
		//显示loading
		title: '加载中',
		mask: true //显示透明蒙层,防止触摸穿透
	 })
	 console.log(options.cartypeId,options.carColorId)
	that.setData({        
	  windowWidt: wx.getSystemInfoSync().windowWidth,     //this.setData的方法用于把传递过来的id转化成小程序模板语言
	  cartypeId: options.cartypeId,     //id是a页面传递过来的名称,a_id是保存在本页面的全局变量   {{b_id}}方法使用
	  carColorId: options.carColorId,
	  topImg: '../../lib/images/top.png',
	  bgPic: app.globalData.bgPic,
	  dataNumber:app.globalData.locamapListOrder.size, // 判断有没有值
	  hatsImgListArrNext:app.globalData.hatsImgListArrNext,
	  src:app.globalData.mediaAudio
	});
	wx.request({
	  url:app.globalData.baseUrl+'rest/small/queryPartsByTypeIdAndColorId',  //
	  data:{
		carTypeId:this.data.cartypeId,
		carColorId:this.data.carColorId,
	  },
	  'content-type': 'application/json', // 设置请求的 header  1 3 4 5 
		success: res => {
		  this.setData({
			hatsImgListArrOrder : res.data.data,
			pics :res.data.data[0].partShowImg,  // 点击触发图片
			partId:res.data.data[0].id,     // 商品id
			partMarkStatus:res.data.data[0].carPartId
		  });
		   wx.hideLoading();
		  }
	  });
	  if(wx.getStorageSync("hatsId")){
			var hastId = wx.getStorageSync("hatsId") ? wx.getStorageSync("hatsId") :'1';
			var dataImg  = this.data.hatsImgListArrOrder[hastId].partImg;
			this.setData({
			  pics : dataImg
			});
	 }
	 // 选择图库照片
	 wx.request({
	  url:app.globalData.baseUrl+'rest/small/queryGallery',  //
	  'content-type': 'application/json', // 设置请求的 header  1 3 4 5 
	  success: res => { 
			   var that = this
			   if(res.data.data.length==0){
				wx.showToast({
				  title: "没有图库信息",
				  icon: 'success',
				  duration: 2000
				})
			   } else {
				var depotImg =res.data.data;
				   that.setData({
					photographList : depotImg  // 选择图库
				   })
          }
      }
   });
},
onShow(options) {  
	var that = this
	if(app.globalData.showPies == 2) {
	  wx.showLoading({  // 刷新商品信息
		//显示loading
		title: '加载中',
		mask: true //显示透明蒙层,防止触摸穿透
	  })
	  app.globalData.showPies = 1
	  that.setData({
		pics : '',  // 点击触发图片
		originUrl:'',
		hatsImgListArrOrder:wx.getStorageSync('hatsImgListArrNext'),
	  })
	  var hatsImgList = wx.getStorageSync('hatsImgListArrNext');
	  for (let index = 0; index < hatsImgList.length; index++) {
		  if(hatsImgList[index].statusBig ==false&&hatsImgList[index].statusBig!=undefined) {
			that.setData({
			  pics :hatsImgList[index].partShowImg,  // 点击触发图片
			  base64:'',
			  hatsIdActive:-1,
			  originUrl:'',  // 清除返回后的画板信息
			})  
		  }
	  }
	  wx.hideLoading();
	} else {
  that.setData({
    pics : that.data.pics,  // 点击触发图片
    base64:that.data.base64,
    originUrl:that.data.originUrl  // 清除返回后的画板信息
  })
}
this.audioCtx = wx.createAudioContext('myAudio')
this.audioCtx.pause()

}, uploadTap() {

//首次上传本地图片
let _this = this
 if(!_this.data.base64) {
 wx.chooseImage({
  count: 1, // 默认9
  sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
  sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  success(res) {
    wx.showLoading({  // 刷新商品信息
      //显示loading
      title: '加载中',
      mask: true //显示透明蒙层,防止触摸穿透
     });
        var tempFilesSize = res.tempFiles[0].size;  //获取图片的大小,单位B
        var tempFilePaths = res.tempFilePaths[0]; //获取图片
          _this.setData({
             originUrl: tempFilePaths,//图片的本地路径
             originImgPics:'../../lib/images/shade.png',
             originUrlNumber:1,
             cropperResult: ''     
          })
         
          _this.uploadFile(tempFilePaths)
     }
 })
 return true
} else {
  wx.showToast({
    title: "请先重新设计",
    icon: 'success',
    duration: 2000
  })
  return false
}
  },
  combinePic(){
	var that =this;
	if(that.data.base64){
	  wx.request({
		methods:"get",
		url:app.globalData.baseUrl+'rest/small/queryGoodsByPartId?carTypeId='+that.data.cartypeId+'&carColorId='+that.data.carColorId+'&partId='+that.data.partId,
		'content-type': 'application/json', // 设置请求的 header
		  success: res => {
			if(res.data.data.length==0){
			  wx.showToast({
				title: "没有商品信息",
				icon: 'success',
				duration: 2000
			  })
			 } else {
			  var hatsImg =res.data.data;
				 that.setData({
				   hatsImgList : hatsImg
				 })
			  var partInfo = {}   // 创建提交订单支付信息
			  var partInfoOrder = {}  // 创建订单信息
			  for (let index = 0; index < hatsImg.length; index++) {
				const element = hatsImg[index];
				partInfo={
				   finalImage:that.data.base64,
				   number:1,
				   id:parseInt(element.carPartId),
				   masterImg:that.data.originUrlBase,
				   good_id:parseInt(element.id),
				}
				partInfoOrder={
				  carColorId: element.carColorId,
				  carColorName: element.carColorName,
				  carPartId: element.carPartId,
				  carTypeId:element.carTypeId,
				  carTypeName:element.carTypeName,
				  createTime: element.createTime,
				  goodCode: element.goodCode,
				  goodImg: element.goodImg,
				  name: element.goodName,
				  finalImage:that.data.base64,
				  price:element.goodPrice,
				  id:parseInt(element.id),
				  partName:element.partName,
				  number:1,
				}
			  }
			  if(app.globalData.locamap){
			   app.globalData.locamap.set(parseInt(partInfo.id),partInfo);
			  }
			  if(app.globalData.locamapListOrder){
			   app.globalData.locamapListOrder.set(parseInt(partInfoOrder.carPartId),partInfoOrder) // 商品订单信息
			  }
			  wx.hideLoading();
		   }
		},
		fail:err => {
		  console.log('接口请求出错')
		  }
	  });
	}
  },
  myevent(e){
   var hatsImg= this.data.hatsImgListArrOrder;
   for (let index = 0; index < hatsImg.length; index++) {
	   if(hatsImg[index].id==e.detail.BigpartId) {
		  hatsImg[index].statusBig = e.detail.statusBig;
	   }
   }
	this.setData({
	  statusBig:e.detail.statusBig,
	  hatsImgListArrOrder :hatsImg,
	  applique:false,
	  partMarkStatus:e.detail.BigpartId   // 返回过来的id
	});
	if(e.detail.statusBig) {
	  this.setData({
		uploadStatus:false  //上传颜色状态
	  });
	}
  },
	audioPlay() {
	this.audioCtx.play()
	},
	audioPause() {
	  this.audioCtx.pause()
	},
	onMusicTap: function(event) {
		  var isPlayingMusic = this.data.isPlayingMusic;
		  this.audioCtx.play()
		  if (isPlayingMusic){
			this.audioCtx.play()
			this.setData({
				 isPlayingMusic: true
			})
		  } else {
				 this.audioCtx.play()
				 this.setData({
				  isPlayingMusic: true
			  })
		  }
  },
chooseImg:function(e){
	var that = this;
	var index = e.currentTarget.dataset.index
	var hatsId = e.target.id; // 1  3 .4
	if(e.target.id) {
	  app.globalData.originImgs = that.data.hatsImgListArrOrder[index].partShowImg
	  that.setData({
		pics : that.data.hatsImgListArrOrder[index].partShowImg,  // 点击触发图片
		partId:hatsId, // 商品id
		hatsId:index,
		hatsIdActive:index,
		uploadStatus:true  //上传颜色状态
	  })
	 var listOrderImg = app.globalData.locamapListOrder.get(parseInt(hatsId));
	 if(listOrderImg==null || listOrderImg==undefined){
	   //画板清空
	   that.setData({
		 base64:'',
		 originUrl:'',
		 statusBig:false,
		 partMarkStatus:'', //  确认信息 
	   })
	   // 再次添加
	 }else{
	  that.setData({
		base64:listOrderImg.finalImage,
		partMarkStatus:listOrderImg.carPartId
	  })
	 }
	if(that.data.base64) {
	  that.setData({
		uploadStatus:false  //上传颜色状态
	  })
	}
  }
  wx.clearStorageSync('hatsImgListArrNext')   // 清楚缓冲存
	  // 与页面衔接 触发页面中的方法并传数据
	  this.triggerEvent('getCropperImg', that.data.hatsImgListArrOrder[index].partShowImg);
	  wx.clearStorageSync('hatsImgListArrNext');
  },
  empatyImg(){     // 重新设计
	var that = this;
	 app.globalData.locamapListOrder.delete(parseInt(that.data.partId));
	 app.globalData.locamap.delete(parseInt(that.data.partId));
	  var hatsImg= that.data.hatsImgListArrOrder;  // 设置icon 状态信息
	  for (var index = 0; index < hatsImg.length; index++) {
		  if(hatsImg[index].id==that.data.partMarkStatus) {
			  hatsImg[index].statusBig = false;
		   }
	  }
	  that.setData({
		base64:'',
		originUrl:'',
		partMarkStatus:'', //  确认信息 
		hatsImgListArrOrder:hatsImg,
		uploadStatus:true
	  })
	  this.onMusicTap(); // 添加音乐
 },
 listClick:function(e){
  wx.showLoading({  // 刷新商品信息
	//显示loading
	title: '加载中',
	mask: true //显示透明蒙层,防止触摸穿透
  });
  var that = this;
  var index = e.currentTarget.dataset.index
  var hatsId = e.target.id; // 1  3 .4
  //首次上传本地图片
  if(!that.data.base64) {
	   if(e.target.id) {
		  that.setData({   // ,每次清空一次图片
			  originUrl:''
		  })
	   var tempFilePaths = that.data.photographList[index].imgUrl
	   wx.getImageInfo({
		src: tempFilePaths,    //请求的网络图片路径
		success: function (res) {
		  //请求成功后将会生成一个本地路径即res.path,然后将该路径缓存到storageKeyUrl关键字中
		  wx.hideLoading();
		  that.setData({
			  originUrl:res.path,
			  originUrlNumber:2
			})
		 }
	  })
	  that.setData({
		uploadStatus:true, //上传颜色状态
		currentIndex:e.currentTarget.dataset.index,
		originImgPics:'../../lib/images/shade.png',
		originUrlBase:tempFilePaths
	   })
	  }
	}
   else {
	wx.showToast({
	  title: "请先重新设计",
	  icon: 'success',
	  duration: 2000
	})
	return false
  }
},
 combinePicSubmit:function(){   // 提交
  this.onMusicTap(); // 添加音乐
  wx.setStorageSync('hatsImgListArrNext', this.data.hatsImgListArrOrder)
  wx.navigateTo({
	   url: '../combine/combine',
   });
},
	//滑动切换
 swiperTab: function (e) {
	  var that = this;
	  that.setData({
		currentTab: e.detail.current
	  });
	},
  get1:function(a){
	return wx.getStorageSync(a)
  },
  get2:function(a){
	return wx.getStorageSync(a)
  },
  uploadFile:function(tempFilePath){
	var _this = this;
	app.globalData.appliqueIndex = 1
	wx.uploadFile({
	  url: app.globalData.baseUrl+'rest/v1/file/upload', //后台上传api路径
	  filePath:tempFilePath,
	  name: 'file',
	  header: { 
		"content-type": "multipart/form-data",
		"content-type": "application/x-www-form-urlencoded"
		},
	  success: function(res){
		  var dataImg = JSON.parse(res.data)
		  _this.setData({
			uploadStatus:true,
			originUrlBase:dataImg.data
		 })  
		 wx.hideLoading();
	  },
	  fail:e=>{
		console.log(e)
	  }
	 })
  },
  clickme:function(){
	if(this.data.base64) {
	  wx.showToast({
		title: "请先重新设计",
		icon: 'success',
		duration: 2000
	  })
	  return false
	}else{
	  this.setData({
		currentIndex:-1
	  })
	  this.showModal();  // 点击切换效果
	  return true
	}
 },
 submitModal:function(){   // 提交图片
   this.hideModal();  // 点击切换效果
 },
 //显示对话框
showModal: function () {
	 // 显示遮罩层
	 var animation = wx.createAnimation({
	   duration: 100,
	   timingFunction: "linear",
	   delay: 0
	 })
	 this.animation = animation
	 animation.translateY(750).step()
	 this.setData({
	   animationData: animation.export(),
	   showModalStatus: true,
	   showModalStatusInfo:true
	 })
	 setTimeout(function () {
	   animation.translateY(0).step()
	   this.setData({
		 animationData: animation.export()
	   })
	 }.bind(this), 100)
   },
   //隐藏对话框
   hideModal: function () {// 关闭按钮
	 // 隐藏遮罩层
	 var animation = wx.createAnimation({
	   duration: 100,
	   timingFunction: "linear",
	   delay: 0
	 })
	 this.animation = animation
	 animation.translateY(750).step()
	 this.setData({
	   animationData: animation.export(),
	 })
	 setTimeout(function () {
	   animation.translateY(0).step()
	   this.setData({
		 animationData: animation.export(),
		 showModalStatus: false,
		 showModalStatusInfo:false
	   })
	 }.bind(this), 100)
   },
})
  第四步 wxml进行添加view
  <view class='applique' >
  <view class='cropper' wx:if="{{originUrl}}">
       <cropper bind:getCropperImg="getCropperImg" bind:myevent="myevent" showbar="{{ pics }}" partId="{{ partId }}"  url="{{ originUrl }}" ratio="{{ ratio }}" appbg="{{ originImgPics }}" originUrlNumber="{{originUrlNumber}}"></cropper>
  </view> 
<view class="applique">
  <view class="bigproscenium" >
   <view><audio src="{{src}}"  id="myAudio" ></audio></view>
    <view class="app-next bubbles"  bindtap="combinePicSubmit" wx:if="{{ base64 }}">
        <text>提交</text>
    </view>
    <!--舞台-->
    <view class="proscenium" style="height:{{windowWidt}}px">
        <view class="touchPicShade" >
          <image wx:if="{{base64}}" class='img' style="height:100%" mode='widthFix' src="{{ base64 }}" ></image>
          <image wx:else class="bgs"   style="height:100%" mode='widthFix'  src="{{originImgPics}}"></image>
        </view>
       <!--前大罩-->
       <view class="touch-area" >
         <image class="hat"  src="{{pics}}"></image>
      </view>
    </view>
    <view class="app-empaty bubbles" data-index="{{index}}"  bindtap="empatyImg" wx:if="{{ base64}}">
        <text>重新设计</text>
    </view>
</view>
   <!--添加图片上传信息-->
   <view class="groupSliding">
        <swiper current="{{currentTab}}" duration="100"  bindchange="swiperTab">
       <swiper-item >
        <scroll-view class="scrollView" scroll-x="true" scroll-with-animation="true">
          <view  class="scrollViewSwiper {{hatsIdActive == index ? 'scrollViewActive':''}}"  wx:for="{{hatsImgListArrOrder}}"  bindtap="chooseImg" data-index="{{index}}"  wx:key="index" >
          <image class="imgList" src="{{item.partImg}}"  bindtap="chooseImg"  id="{{item.id}}" data-index="{{index}}"> </image>
                <text class="partName" id="{{item.id}}"   >{{item.partName}}</text>
                <text class="partMark" id="{{item.id}}"  >{{item.partMark}}</text>
                <view class="{{item.statusBig ? 'Viewbetween-no ViewIconNike':''}}"></view>
        </view>
         </scroll-view>
      </swiper-item>
    </swiper>
   </view>
      <!--底部上传图片-->
      <view class="upload-bottom-list">
            <view class="upload-bottom bubble {{uploadStatus ? 'upload-bottom' : 'upload-bottomop'}}" bindtap="uploadTap" data-statu="open">
                <text class="upload-top {{!uploadStatus ? 'opacity':''}}">
                  上传图片 
                </text>
            </view>
            <view class="upload-bottomRight" bindtap="clickme">
            <text class="{{!uploadStatus ? 'opacity':''}}">
                  图库选择
                </text>
            </view>
     </view>
 <!--屏幕背景变暗的背景  -->
 <view class="commodity_screen"  bindtap="hideModal" wx:if="{{showModalStatus}}"></view> 
  <!--弹出框  -->
		<view animation="{{animationData}}"  class="commodity_attr_box" wx:if="{{showModalStatusInfo}}">
				<view class="viewMenus">
					  <view class="ensure" bindtap="hideModal" wx:if="{{showModalStatus}}" style=" visibility: hidden;">确定</view>
					  <view class="close" bindtap="submitModal" wx:if="{{showModalStatus}}">关闭</view>
				</view>
					  <swiper-item>
						<scroll-view class="scrollViews"  scroll-y="true" scroll-with-animation="true">
							  <view class="viewMenusPicture">
								  <view  wx:for="{{photographList}}" wx:key="index" id="{{item.id}}" data-index="{{index}}"   class='photographList {{currentIndex==index?"active":"activeNo"}}' bindtap="listClick" >
									  <image src="{{item.preUrl}}" mode='aspectFill' id="{{item.id}}" data-index="{{index}}" class="photograph"> </image>
								  </view>
							  </view>
						</scroll-view>
					 </swiper-item>
		</view>
	</view>
	</view>
  
  第五步 进行样式调整
      /**index.wxss**/
page {
  width: 100%;
  height: 100%;
}
/* pages/uploadImg/uploadImg.wxss */
[@import](https://my.oschina.net/u/3201731) "../../lib/style/font.wxss";
.applique{
position: relative;
min-height: 100vh;
box-sizing: border-box;
background:rgba(0,0,0,1);
}
.bigproscenium{
	position: relative;
	width: 100%;
}
.proscenium{
  position: relative;
  box-sizing: border-box;
}
.bigproscenium .app-next{
  position: absolute;
  top: 0rpx;
  padding: 14rpx 43rpx;
  right: 0rpx;
  z-index:1;
}
.bigproscenium .app-next,
.bigproscenium .app-empaty{
	color: #fff;
	font-size:40rpx;
	font-family:'HYZhuZiMuTouRenJ';
	color:rgba(255,255,255,1);
	line-height:45rpx;
	padding: 14rpx 36rpx;
	border:6rpx solid #fff;
	display: inline-block;
	border-radius: 15rpx;
	margin: 50rpx 30rpx;
	text-align: right;
	float: right;
	z-index: 1099;
}
.bigproscenium .app-empaty{
  position: absolute;
  bottom:7rpx;
  padding: 14rpx 43rpx;
  right:0rpx;
}
.upload-img .groupSliding{
	height: 200rpx;
	background:rgba(26,26,26,1);
}
.appbg{
   position: absolute;
   top: 0;
}
.upload-bottom-list{
  display: flex;
  justify-content: center;
  align-items: center;
  box-sizing: border-box;
  width: 100%;
  overflow: hidden;
}
.upload-bottom{
	position: fixed;
	z-index:99;
	bottom:0;
	left: 0;
	width:40%;
	height:140rpx;
	background:rgba(255,59,48,1);
}
.upload-bottomRight{
  width: 60%;
  position: fixed;
  z-index:99;
  bottom:0;
  right: 0;
  height:140rpx;
  background:#40D3B2
}
.upload-bottomop {
  background:rgba(255,59,48,0.5);
}
.upload-bottom>text{
	font-size:40rpx;
	font-family:HYZhuZiMuTouRenJ;
	color:rgba(255,255,255,1);
	z-index: 99;
	position: fixed;
	left: 20%;
	bottom: 19rpx;
	text-align: center;
	margin: 0 auto;
	display: block;
	transform: translate(-50%,0);
	line-height: 90rpx;
}

.upload-bottomRight>text{
  font-size:40rpx;
  font-family:HYZhuZiMuTouRenJ;
  color:rgba(255,255,255,1);
  z-index: 99;
  position: fixed;
  right:20%;
  bottom: 19rpx;
  text-align: center;
  margin: 0 auto;
  display: block;
  text-align: center;
  transform: translate(0,0);
  line-height: 90rpx;
}
.imgbottom{
	width: 40rpx;
	margin:  0 auto;
	text-align: center;
	display: block;
	z-index: 99;
	left: 50%;
	bottom: 0rpx;
	transform: translate(calc( -50% + 20rpx ),15rpx);
}

/***底部弹窗效果**/
  /** 左上下滚动**/
.commodity_screen {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: #000;
  opacity: 0.5;
  overflow: hidden;
  z-index: 1000;
  color: #fff;
}
.commodity_attr_box {
  height:750rpx;
  width: 100%;
  overflow: hidden;
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 99999;
  background:#141416;
  font-family: 'HYZhuZiMuTouRenJ';  
  box-sizing: border-box;
}
.viewMenus{
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 1rem;
   height: 100rpx;
   border-bottom: 1px solid #333544;
}
.viewMenus .close{
  font-size:36rpx;
  color:rgba(255,59,48,1);
  line-height:45px;
}
.viewMenus .ensure{
  font-size:36rpx;
  color:rgba(1,188,103,1);
  line-height:45px;
}
.viewMenusPicture {
  display: flex;
  /* justify-content: flex-end;
  align-items: flex-end; */
  margin: 0rem 0.2%;
  text-align: center;
  flex-direction: row;
  flex-wrap: wrap;
  position: relative;

}
.viewMenusPicture .photographList{
  flex: 0 0 22.45%;
  margin:0.8rem 0 0rem 0;
  height: 160rpx;
}
.viewMenusPicture:after {
  content: "";
  flex: 0 0 25%;
  display: block; 
  height:0; 
}  

.viewMenusPicture .photographList image{
  width: 144rpx;
  height: 144rpx;
  border-radius: 28rpx;
  position: relative;
  top:0.22rem
}
.scrollViews{
  overflow: hidden;
   height:750rpx;
}
::-webkit-scrollbar {
	width: 0;
	height: 0;
	color: transparent;
	display: none;
  }  
  .active{
	height: 120rpx;
	border-radius:42rpx;
	border:4px solid rgba(1,188,103,1);
	position: relative;
  }
  .activeNo{
	height: 120rpx;
	border-radius:42rpx;
	border:4px solid#141416;
	position: relative;
  }
  /** 左右滚动**/
.scrollView{
	width: 100%; 
	position: absolute;
	white-space: nowrap;
	top: 2rpx;
	background: #232426;
	box-sizing: border-box;
	height: 200rpx;
 }
::-webkit-scrollbar {
  width: 0;
  height: 0;
  color: transparent;
  display: none;
}
 .scrollView .scrollViewSwiper{
  display: inline-block;
  opacity: 0.4;
  position: relative;
 }
 .scrollView .scrollViewActive{
   background: #1A1A1A;
   padding-bottom:12rpx;
   opacity: 1;
   position: relative;
 }
 .swiper{
   height:260rpx;
 }
 .imgList{
   height: 180rpx;
   width: 180rpx;
   margin: 10rpx;
   border-radius:8rpx;

 }
 .hat{ 
	height: 100%;
	width: 100%;
	position: absolute;
	left: 50%;
	top:50%;
	transform: translate(-50%,-50%);

  }
  .partName{
		 color:#a1a1a1;
		 font-size: 30rpx;
		 display: block;
		 text-align: center;
		 margin: 0 auto;
		 position: relative;
		 top:-90rpx
  }
  .partMark{
	color:#a1a1a1;
	font-size: 22rpx;
	display: block;
	text-align: center;
	margin: 0 auto;
	margin-top: 8rpx;
	position: relative;
	top:-94rpx
  }
.scrollViewActive .partName,
.scrollViewActive .partMark{
  color:#fff
}
/**底部弹层*/
.commodity_screen {
	width: 100%;
	height: 100%;
	position: fixed;
	top: 0;
	left: 0;
	background: #000;
	opacity: 0.5;
	overflow: hidden;
	z-index:9999;
	color: #fff;
  }
  .btnContainer{
	position: fixed;
	width:100%;
	overflow: hidden;
	bottom: 0;
	left: 0;
	z-index: 99;
	background:#eee;
  }
  .btnContainer button{
	width:100% !important;
	border-radius: 0;
	height: 80rpx;
	font-weight: normal !important;
	border-bottom: 1px solid #a1a1a1;
  }
  .btnContainer button:after{
	border-radius:0;
  }
  .btnContainer button:last-child{
	margin-top:6px;
  }
  .bg{
	position: absolute;
	z-index:0;
	height: 100% !important;
	width:100%;
   left: 0;
   bottom:0;
  }
  .bgs{
	position: absolute;
	z-index:0;
	height: 100% !important;
	width:100%;
   left: 0;
   bottom:0;
  }
  /**弹窗**/
  .Viewbetween-no{
	position: absolute;
	right:14rpx;
	top: 14rpx;
}
.cropper {
  width: 100%;
  height: 100%;
}
.img {
  display: block;
  position: relative;
  z-index: 999;
  width: 100%;
  background-image: url(.../../1.jpg)
	   background-position:center;
	   background-repeat:no-repeat;
	   background-size:cover;
	   box-sizing: border-box;
	}
	.choose-img {
	  width: 40%;
	  text-align: center;
	  padding: 30rpx;
	  border: 1px solid #fff;
	  margin: 20rpx auto;
	  background: #000;
	  color: #fff;
	}
	.opacity{
		  opacity: 0.5;
	}
  
  第六步进行字符组件的传递
      创建cropper  进行子父组件传递 之前我们在perbig 里面进行父组件的传递
	     cropper.wxml	
		   <view class="container">
 <view  class="img"  style="width:{{ width }}px; height:{{height}}px" catchtouchstart="touchstartCallback"  catchtouchmove="touchmoveCallback" catchtouchend="touchendCallback">
	<image style="transform: translate({{ stv.offsetX }}px, {{ stv.offsetY }}px) scale({{stv.scale}}) rotate({{ stv.rotate }}deg);width:{{ originImg.width }}px; height: {{ originImg.height }}px" src="{{ originImg.url }}">
	</image>
	<image class="appbg" src="{{ appbg }}"  style="width:100%;height:{{ height }}px;z-index:-999;"></image>
	<image class="" src="{{ originImgs }}"  style="width:100%;height:{{ height }}px" ></image> 
</view>
									<view class="bigproscenium" wx:if="{{applique}}">
	<view class="app-next bubbles" bindtap="cropperImgs" wx:if="{{dataNumber>0&&dataNumber}}">
		<text>提交</text>
	</view>
	<view><audio src="{{src}}"  id="myAudio" ></audio></view>
	<view class="app-empaty bubbles" bindtap="appliqueSumbit">
		<text>生成</text>
	</view>
</view>
<canvas class='imgcrop' style="width:{{ width * 2 }}px;height:{{ height *2 }}px;" canvas-id='imgcrop'></canvas>
</view>
	  第七步js中进行处理
	   // component/cropper/cropper.js
   /***
   *  shu_lh@supersoco.com
   * 
   */
//获取应用实例
const app = getApp()
const device = wx.getSystemInfoSync();

var twoPoint = {
  x1: 0,
  y1: 0,
  x2: 0,
  y2: 0
}
Component({
  /**
   * 组件的初始数据
   */
  data: {
	originImgs:'',                                 // 大灯图
	appbg:'',                                      // 背景图
	width: device.windowWidth * 1,                //剪裁框的宽度
	height: device.windowWidth * 1 / (750 / 750), //剪裁框的长度
	originImg: null,                                //存放原图信息
	stv: {
	  offsetX: 0,                                   //剪裁图片左上角坐标x
	  offsetY: 0,                                   //剪裁图片左上角坐标y
	  zoom: false,                                  //是否缩放状态
	  distance: 0,                                  //两指距离
	  scale: 1,                                     //缩放倍数
	  rotate: 0                                     //旋转角度
	},
	windowWidt: wx.getSystemInfoSync().windowWidth,      
	imgwidth: 0,
	base64:'',
	base64data:'',
	imgheight: 0,
	partId:'',   // 商品的id
	originUrlBase:'',   // 原图
	originUrl:null,
	originUrlwidth:'',
	originUrlHeight:'',
	dataImgs:'',
	applique:true,
	cartypeId:'',
	dataNumber:0,
	appliqueIndex:0,
	isPlayingMusic:true,
	src: 'https://wxxcx.supersoco.com/resource/wxChat/video/media.mp3',
	originUrlNumber:''  // 判断从哪个里面进来的
  },
   /**
   * 组件的属性列表
   */
  properties: {
	ratio: {
	  type: Number,
	  observer: function (newVal, oldVal) {
		this.setData({
		  width: device.windowWidth *1, 
		  height: device.windowWidth * 1 / newVal
		})
	  }
	},
	url: {
	  type: String,
	  observer ( newVal, oldVal ) {
		this.initImg( newVal )
	  }
	},
	hatsImgListArrOrder:{
	  type: Object,
	  observer ( newVal, oldVal ) {
		this.initImg( "ObjectnewVal",newVal)
	  }
	},
	showbar:{
	  type:String,
	  observer: function (newVal, oldVal) {
		   var then = this
				wx.getImageInfo({   // 根据头像地址下载头像并存为临时路径
				  src: newVal,
				  success: res => {
					then.setData({
					  originImgs: res.path
					})
				  }
				})
		  }
	 },
	 partId:{
	   type:Number,
		observer: function (newVal, oldVal) {
			  var then = this
				then.setData({
				  partId:newVal
				})
			}
	},
	 appbg:{
		 type:String,
		 observer:function (newVal,oldVal) {
			 var then = this;
			 then.setData({
			  appbg: newVal
			})
		 }
	 },
	 originUrlNumber:{
	   type:Number,
	   observer:function (newVal,oldVal) {
		var then = this;
		then.setData({
			originUrlNumber: newVal
		 })
	 }
	 }
  },
  ready:function(){
	   var that = this
	   console.log("wwww",that.properties);
	  var get1 = that.get1('cartypeId')
	  var get2 = that.get2('carColorId')
	  this.audioCtx = wx.createAudioContext('myAudio')
	  this.audioCtx.pause();
	  that.setData({    
		originImgs : that.properties.originImgs,
		windowWidt: wx.getSystemInfoSync().windowWidth,  //this.setData的方法用于把传递过来的id转化成小程序模板语言
		cartypeId: get1,                     //id是a页面传递过来的名称,a_id是保存在本页面的全局变量   {{b_id}}方法使用
		carColorId: get2,
		originUrl:that.properties.url,
		appbg:that.properties.appbg,  
		dataNumber:app.globalData.locamapListOrder.size,
	  });
	  setTimeout(function(){
		wx.hideLoading();
	  },1000)
  },
  /**
   * 组件的方法列表
   */
  methods: {
	uploadTap() {

  //上传本地图片
  let _this = this
  wx.chooseImage({
    count: 1, // 默认9
    sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
    success(res) {
      _this.initImg(res.tempFilePaths[0]);
      _this.setData({
        applique:false
      });
     
    }
  });
  if(_this.data.base64){
    _this.setData({
      applique:false
    });
  
  }
},
audioPlay() {
  this.audioCtx.play()
  },
  audioPause() {
    this.audioCtx.pause()
  },
   onMusicTap: function(event) {
			var isPlayingMusic = this.data.isPlayingMusic;
			this.audioCtx.play()
			if (isPlayingMusic){
			  this.audioCtx.play()
			  this.setData({
				   isPlayingMusic: true
			  })
			} else {
				   this.audioCtx.play()
				   this.setData({
					isPlayingMusic: true
				})
			}
	},
	showTab:function(e){
	 },
	 get1:function(a){
	  return wx.getStorageSync(a)
	},
	get2:function(a){
	  return wx.getStorageSync(a)
	},
	bindload:function(e){
	  var width = e.detail.width;
	  var height = e.detail.height;
	  this.setData({
		originUrlwidth:width,
		originUrlHeight:height
	  });
	},
	appliqueSumbit:function(e){  
	  wx.showLoading({  // 刷新商品信息
		//显示loading
		title: '上传中',
		mask: true //显示透明蒙层,防止触摸穿透
	  });
	  let _this = this;
	  _this.onMusicTap(); // 生成
	  let ctx = wx.createCanvasContext('imgcrop',this);
	  let cropData = _this.data.stv;
	  ctx.save();
	  // 缩放偏移值
	  let x = (_this.data.originImg.width - _this.data.originImg.width * cropData.scale) / 2;
	  let y = (_this.data.originImg.height - _this.data.originImg.height * cropData.scale) / 2;
	  const windowWidth = wx.getSystemInfoSync().windowWidth;
	  const windowHeight = wx.getSystemInfoSync().windowHeight;
	  //画布中点坐标转移到图片中心
	  ctx.drawImage(_this.data.appbg,0,0,device.windowWidth*2,device.windowWidth*2); // 裁剪背景图片
	  let movex = (cropData.offsetX + x) * 2 + _this.data.originImg.width * cropData.scale;
	  let movey = (cropData.offsetY + y) * 2 + _this.data.originImg.height * cropData.scale;
	  ctx.translate(movex, movey); //translate  对坐标原点进行缩放
	  ctx.rotate(cropData.rotate * Math.PI / 180); //rotate  对坐标轴进行顺时针旋转
	  ctx.translate(-movex, -movey); //translate    对坐标原点进行缩放
	  ctx.drawImage(_this.data.originImg.url, (cropData.offsetX + x) * 2, (cropData.offsetY + y) * 2, _this.data.originImg.width * 2 * cropData.scale, _this.data.originImg.height * 2 * cropData.scale);//绘制图像
	  ctx.restore(); //恢复之前保过的绘图上下文
	  ctx.drawImage(_this.data.originImgs,0,0 ,device.windowWidth*2,device.windowWidth*2);//绘制图像  绘制图像
	  ctx.draw(false, () => { //进行绘图
		setTimeout(() => {
			wx.canvasToTempFilePath({ //把当前画布指定区域的内容导出生成指定大小的图片
			  canvasId: 'imgcrop',
			  success(response) {
				_this.triggerEvent("getCropperImg", { url: response.tempFilePath })
				ctx.drawImage(response.tempFilePath, 0, 0, 0, 0)//绘制图像
				ctx.draw();
				_this.setData({
				  applique:false
				});
				_this.triggerEvent('myevent',{BigpartId:_this.properties.partId,statusBig:true,applique:false}) //myevent自定义名称事件,父组件中使用
			  },
			  fail( e ) {
				wx.hideLoading();
				wx.showToast({
				  title: '生成图片失败',
				  icon: 'none'
				})
			  }
			}, this);
		  }, 500);
     
   });
 
},
cropperImgs:function(){ // 跳转新页面 预览页面
  wx.navigateTo({
    url: '../combine/combine'
});
},
initImg(url) {  //定位图片左上角的坐标
  let _this = this;
  wx.getImageInfo({
    src: url,
    success(resopne) {
      let innerAspectRadio = resopne.width / resopne.height;
      if (innerAspectRadio < _this.data.width / _this.data.height) {
        _this.setData({
          originImg: {
            url: url,
            width: _this.data.width,
            height: _this.data.width / innerAspectRadio
          },
          stv: {
            offsetX: 0,
            offsetY: 0 - Math.abs((_this.data.height - _this.data.width / innerAspectRadio) / 2),
            zoom: false, //是否缩放状态
            distance: 0,  //两指距离
            scale: 1,  //缩放倍数
            rotate: 0
          },
        })
      } else {
        _this.setData({
          originImg: {
            url: url,
            height: _this.data.height,
            width: _this.data.height * innerAspectRadio
          },
          stv: {
            offsetX: 0 - Math.abs((_this.data.width - _this.data.height * innerAspectRadio) / 2),
            offsetY: 0,
            zoom: false, //是否缩放状态
            distance: 0,  //两指距离
            scale: 1,  //缩放倍数
            rotate: 0
          }
        })
      }

    }
  })
},
//事件处理函数
touchstartCallback: function (e) {
  if (e.touches.length === 1) { //一指触控
    let { clientX, clientY } = e.touches[0];
    this.startX = clientX; //手指起始点横坐标
    this.startY = clientY; //手指起始点纵坐标
    this.touchStartEvent = e.touches; 
  } else {  //多指
    let xMove = e.touches[1].clientX - e.touches[0].clientX; //两手指起始点横坐标差
    let yMove = e.touches[1].clientY - e.touches[0].clientY; //两手指起始点纵坐标差
    let distance = Math.sqrt(xMove * xMove + yMove * yMove); //两手指距离
    twoPoint.x1 = e.touches[0].pageX * 2 //第一个手指距离文档左上角的x距离
    twoPoint.y1 = e.touches[0].pageY * 2 //第一个手指距离文档左上角的y距离
    twoPoint.x2 = e.touches[1].pageX * 2 //第二个手指距离文档左上角的x距离
    twoPoint.y2 = e.touches[1].pageY * 2 //第二个手指距离文档左上角的y距离
    this.setData({
      'stv.distance': distance,
      'stv.zoom': true, //缩放状态
    })
  }
},
//图片手势动态缩放
touchmoveCallback: function (e) {
  let _this = this
  fn(_this, e)
},
touchendCallback: function (e) {
  //触摸结束
  if (e.touches.length === 0) { 
    this.setData({
      'stv.zoom': false, //重置缩放状态
    })
  }
}
 },
 })
		/**
		* fn:延时调用函数
		* delay:延迟多长时间
		* mustRun:至少多长时间触发一次
		*/
			var throttle = function (fn, delay, mustRun) {
			  var timer = null,
				previous = null;
			  return function () {
				var now = +new Date(),
				  context = this,
				  args = arguments;
				if (!previous) previous = now;
				var remaining = now - previous;
				if (mustRun && remaining >= mustRun) {
				  fn.apply(context, args);
				  previous = now;
				} else {
				  clearTimeout(timer);
				  timer = setTimeout(function () {
					fn.apply(context, args);
				  }, delay);
				}
			  }
			}
			var touchMove = function (_this, e) {
			  //触摸移动中
			  if (e.touches.length === 1) {
				//单指移动
				if (_this.data.stv.zoom) {
				  //缩放状态,不处理单指
				  return;
				}
				let { clientX, clientY } = e.touches[0];
				let offsetX = clientX - _this.startX; //移动 
				let offsetY = clientY - _this.startY; //移动
				_this.startX = clientX; //更新起始点坐标
				_this.startY = clientY; //更新起始点坐标
				let { stv } = _this.data;
				stv.offsetX += offsetX;
				stv.offsetY += offsetY;
				stv.offsetLeftX = -stv.offsetX;
				stv.offsetLeftY = -stv.offsetLeftY;
				_this.setData({
				  stv: stv
				});
  } else if (e.touches.length === 2) {
	//计算旋转
	let preTwoPoint = JSON.parse(JSON.stringify(twoPoint))
	twoPoint.x1 = e.touches[0].pageX * 2
	twoPoint.y1 = e.touches[0].pageY * 2
	twoPoint.x2 = e.touches[1].pageX * 2
	function vector(x1, y1, x2, y2) {
	  this.x = x2 - x1;
	  this.y = y2 - y1; 
	};
	//计算点乘
	function calculateVM(vector1, vector2) {
	  return (vector1.x * vector2.x + vector1.y * vector2.y) / (Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) * Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y));

	}
//计算叉乘
function calculateVC(vector1, vector2) {
  return (vector1.x * vector2.y - vector2.x * vector1.y) > 0 ? 1 : -1;
}

let vector1 = new vector(preTwoPoint.x1, preTwoPoint.y1, preTwoPoint.x2, preTwoPoint.y2);
let vector2 = new vector(twoPoint.x1, twoPoint.y1, twoPoint.x2, twoPoint.y2);
let cos = calculateVM(vector1, vector2);
let angle = Math.acos(cos) * 180 / Math.PI;

let direction = calculateVC(vector1, vector2);
let _allDeg = direction * angle;

// 双指缩放
let xMove = e.touches[1].clientX - e.touches[0].clientX; //两指x距离
let yMove = e.touches[1].clientY - e.touches[0].clientY; //两指y距离
let distance = Math.sqrt(Math.pow(xMove, 2) + Math.pow(yMove, 2));
let distanceDiff = distance - _this.data.stv.distance; //两指距离变化
let newScale = _this.data.stv.scale + 0.001 * distanceDiff; //得到缩放倍数

if (Math.abs(_allDeg) > 1) {
  _this.setData({
    'stv.rotate': _this.data.stv.rotate + _allDeg
  })
} else {
  //双指缩放
  let xMove = e.touches[1].clientX - e.touches[0].clientX;
  let yMove = e.touches[1].clientY - e.touches[0].clientY;
  let distance = Math.sqrt(xMove * xMove + yMove * yMove);

  let distanceDiff = distance - _this.data.stv.distance;
  let newScale = _this.data.stv.scale + 0.001 * distanceDiff;
  if (newScale < 0.2 || newScale > 2.5) {
    return;
  }
  _this.setData({
    'stv.distance': distance,
    'stv.scale': newScale,
  })
}
  } else {
	return;
  }
}
//为touchMove函数节流
const fn = throttle(touchMove, 10, 10);

第八步 进行 样式的调整
   .container {
  position: relative;
  width: 100%;
  height: 100%;
  background: #000;
}
.modeback{
   position: absolute;
   left: 50%;
   z-index: 999;
   height: 500rpx;
   transform: translateX(-50%);
   overflow: hidden;
}
.appbg{

}
.imgItem{
  position: absolute;
  top: 0%;
  left: 50%;
  z-index: 1;
  transform: translateX(-50%);
  overflow: hidden;
  background: none;
  background-position:center;
  background-repeat:no-repeat;
  background-size:cover;
  box-sizing: border-box;
}
.img {
   position: absolute;
   top: 0%;
   left: 50%;
   z-index: 999;
   transform: translateX(-50%);
   overflow: hidden;
   background-image: url(../../1.jpg)
   background-position:center;
   background-repeat:no-repeat;
   background-size:cover;
   box-sizing: border-box;

}
.img image{
  height:400px;
  position: absolute;
  top:0;
}
.imgcrop {
   position: absolute;
  left: -50000rpx;
  top: -500000rpx; 
}
.footer {
  position: absolute;
  width: 100%;
  height: 110rpx;
  color: #fff;
  background: #000;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-around;
}
.footer view {
  width: 30%;
  text-align: center;
}
.background {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  z-index: -1;
}

.bigproscenium{
  position: relative;
  width: 100%;
}
.proscenium{
position: relative;
box-sizing: border-box;
}
.bigproscenium .app-next{
	position: absolute;
	top: 0rpx;
	padding: 14rpx 43rpx;
	right: 0rpx;
	z-index: 9999;

}
.bigproscenium .app-next,
.bigproscenium .app-empaty{
	color: #fff;
	font-size:40rpx;
	font-family:'HYZhuZiMuTouRenJ';
	color:rgba(255,255,255,1);
	line-height:45rpx;
	padding: 14rpx 36rpx;
	border:6rpx solid #fff;
	display: inline-block;
	border-radius: 15rpx;
	margin: 50rpx 30rpx;
	text-align: right;
	float: right;
	z-index: 9999;
}
.bigproscenium .app-empaty{
	position: absolute;
	padding: 14rpx 43rpx;
	right:0rpx;
	z-index: 9999;
	top: 567rpx;
}
.upload-img .groupSliding{
	height: 200rpx;
	background:rgba(26,26,26,1);
}
.Viewbetween-no::after{
  content: '';
  position: absolute;
  left:6rpx;
  top: -2rpx;
  font-size: 20rpx;
  color: #fff;
}
.ViewbetweenInfo .Viewbetween-no::after{
  content: '✔';
}

第九步就是测试阶段了  
  cropper.json  中不需要用添加什么
         1好了整个过程在微信小程序中进行的,其实这个要注意的就是canvas不能裁剪网络图片,需要进行转化成本地图片  微信小程序自带 转成本地图片
		     wx.getImageInfo({
				src: tempFilePaths,    //请求的网络图片路径
				success: function (res) {
				  //请求成功后将会生成一个本地路径即res.path,然后将该路径缓存到storageKeyUrl关键字中
				  wx.hideLoading();
				  that.setData({
					  originUrl:res.path,
					  originUrlNumber:2
					})
				 }
			  })
       2  第二点就是要注意的就是 在传的过程中很多需要我们自己去思考旋转跟放大为何会两只手一起才可以,需要计算双手指的距离。整理不是很难,相信自己 希望能够帮助大家
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!