微信小程序——实现图片拖拽缩放并截图

cropper组件
cropper.js

// component/cropper/cropper.js
const device = wx.getSystemInfoSync();
var twoPoint = {
  x1: 0,
  y1: 0,
  x2: 0,
  y2: 0
}

Component({
  /**
   * 组件的属性列表
   */
  properties: {
    ratio: {
      type: Number,
      observer: function (newVal, oldVal) {
        this.setData({
          width: device.windowWidth * 0.8, 
          height: device.windowWidth * 0.8 / newVal
        })
      }
    },
    url: {
      type: String,
      observer ( newVal, oldVal ) {
        this.initImg( newVal )
      }
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    width: device.windowWidth * 0.8,                //剪裁框的宽度
    height: device.windowWidth * 0.8 / (598 / 790), //剪裁框的长度
    originImg: null,                                //存放原图信息
    stv: {
      offsetX: 0,                                   //剪裁图片左上角坐标x
      offsetY: 0,                                   //剪裁图片左上角坐标y
      zoom: false,                                  //是否缩放状态
      distance: 0,                                  //两指距离
      scale: 1,                                     //缩放倍数
      rotate: 0                                     //旋转角度
    },
  },

  /**
   * 组件的方法列表
   */
  methods: {
    uploadTap() {
      //上传本地图片
      let _this = this
      wx.chooseImage({
        count: 1, // 默认9
        sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
        sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
        success(res) {
          _this.initImg( res.tempFilePaths[0]);
        }
      })
    },
    rotate() {
      let _this = this;
      _this.setData({
        'stv.rotate': _this.data.stv.rotate % 90 == 0 ? _this.data.stv.rotate = _this.data.stv.rotate + 90 : _this.data.stv.rotate = 0
      })
    },

    cropperImg() {
      // canvas剪裁图片并导出
      wx.showLoading({
        //显示loading
        title: 'loading',
        mask: true //显示透明蒙层,防止触摸穿透
      })
      let _this = this;
      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;

      //画布中点坐标转移到图片中心
      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.draw(false, () => { //进行绘图
        wx.canvasToTempFilePath({ //把当前画布指定区域的内容导出生成指定大小的图片
          canvasId: 'imgcrop',
          success(response) {
            console.log(response.tempFilePath);
            _this.triggerEvent("getCropperImg", { url: response.tempFilePath })
            wx.hideLoading();
          },
          fail( e ) {
            console.log( e );
            wx.hideLoading();
            wx.showToast({
              title: '生成图片失败',
              icon: 'none'
            })
          }
        }, this)
      });
    },

    initImg(url) {  //定位图片左上角的坐标
      let _this = this;
      wx.getImageInfo({
        src: url,
        success(resopne) {
          console.log(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(xMove * xMove + yMove * yMove); //两指距离

    let distanceDiff = distance - _this.data.stv.distance; //两指距离变化
    let newScale = _this.data.stv.scale + 0.005 * 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.005 * 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);

cropper.json

{
  "component": true,
  "usingComponents": {}
}

cropper.wxml


  
  
    
  
  
      选择图片 
      旋转
      打印
  

  
  

cropper.wxcc

.container {
  position: relative;
  width: 100%;
  height: 100%;
  background: #000;
}
.img {
   position: absolute;
   top: 5%;
   left: 50%;
   transform: translateX(-50%);
   overflow: hidden;
   background: #eee;
}
.img image {
  height:400px;
}
.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;
}

index.js

//index.js
//获取应用实例
const app = getApp()

Page({
  data: {
    ratio: 598 / 790, //剪裁比例
    originUrl: '', //原始图片url
    cropperResult: '', //变化后结果
    base64: '' //base64
  },

  uploadTap() {
    //首次上传本地图片
    let _this = this
    wx.chooseImage({
      count: 1, // 默认9
      sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
      success(res) {
        _this.setData({
          originUrl: res.tempFilePaths[0],//图片的本地路径
          cropperResult: ''
        })
      }
    })
  },
  getCropperImg(e) {
    //将原图片url置空,表示已经完成剪裁,剪裁后图片地址储存
    this.setData({
      originUrl: '',
      cropperResult: e.detail.url,
      base64: 'data:image/png;base64,' + wx.getFileSystemManager().readFileSync(e.detail.url, "base64")
    })
  }
})

index.json

{
  "usingComponents": {
    "cropper": "../../component/cropper/cropper"
  }
}

index.wxml


  
  
    
  
  choose Img 
  

index.wxss

/**index.wxss**/
page {
  width: 100%;
  height: 100%;
}
.container {
  width: 100%;
  height: 100%;
  background: #eee;
  overflow: hidden;
}
.cropper {
  width: 100%;
  height: 100%;
}
.img {
  margin: 20rpx auto;
  display: block;
  background: #fff;
}
.choose-img {
  width: 40%;
  text-align: center;
  padding: 30rpx;
  border: 1px solid #fff;
  margin: 20rpx auto;
  background: #000;
  color: #fff;
}

log.js

//logs.js
const util = require('../../utils/util.js')

Page({
  data: {
    logs: []
  },
  onLoad: function () {
    this.setData({
      logs: (wx.getStorageSync('logs') || []).map(log => {
        return util.formatTime(new Date(log))
      })
    })
  }
})

log.json

{
  "navigationBarTitleText": "查看启动日志"
}

log.wxml



  
    {{index + 1}}. {{log}}
  

log.wxss

.log-list {
  display: flex;
  flex-direction: column;
  padding: 40rpx;
}
.log-item {
  margin: 10rpx;
}

app.json

{
  "pages": [
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle": "black"
  },
  "sitemapLocation": "sitemap.json"
}

你可能感兴趣的:(微信小程序——实现图片拖拽缩放并截图)