博客
关于我
微信小程序可移动缩放图片裁剪框
阅读量:301 次
发布时间:2019-03-01

本文共 7576 字,大约阅读时间需要 25 分钟。

微信小程序图片裁剪框开发实践指南

效果预览

本文将详细介绍一个基于微信小程序canvas组件实现的可移动缩放图片裁剪框的开发方法,结合实际项目经验分享技术细节和实现方案。

前言

传统的图片裁剪框应用在移动端应用中虽然功能完善,但在灵活性和精准度方面仍存在一定局限性。通过使用微信小程序canvas组件,可以实现一个支持自由移动和缩放的图片裁剪框解决方案。本文将从技术分析、实现细节等多个方面展开讨论。

技术要点分析

使用canvas组件实现图片裁剪框开发涉及多个关键技术点,包括:

  • 图片绘制与裁剪框绘制:通过canvasAPI实现图片绘制和裁剪框边框绘制。
  • 裁剪框缩放点计算:计算四个缩放点的位置,确保裁剪框的灵活性和精准性。
  • 裁剪框移动与缩放:实现基于触摸事件的裁剪框操作,支持自由移动和缩放。
  • 边界防超出控制:确保裁剪框不会超出屏幕边界,提供更好的用户体验。
  • 重绘机制优化:在裁剪框发生变化时,及时重绘canvas,避免重叠或其他显示问题。
  • 裁剪结果生成:将裁剪后的图片区域转换为临时文件,方便后续使用。
  • 具体实现

    1. 页面数据结构

    page组件的数据结构设计如下:

    data: {  open_camera: boolean, // 是否打开相机拍照  cut_image_canvas: {}, //裁剪画布上下文  mycanvas: {}, // canvas 实例  image_obj: {}, //绘制的图片对象  window_width: number, //屏幕宽度  window_height: number, //屏幕高度  cut_img_canvas_w: number, //画布宽度  cut_img_canvas_h: number, //画布高度  cut_area: { //裁剪区域配置    x: number, //左上角顶点x坐标    y: number, //左上角顶点y坐标    cut_width: number, //裁剪宽度    cut_height: number, //裁剪高度    cut_area_color: string, //裁剪框边框颜色  },  pixelRatio: number, //像素比  last_touches_x: number, //上一个触摸点x坐标  last_touches_y: number, //上一个触摸点y坐标  cut_area_change_status: string, //裁剪框变换类型  cut_image: string, //裁剪后的图片临时文件链接}

    2. WXML、WXSS和页面数据初始化

    page组件中,初始化屏幕参数和canvas布局:

    裁剪
    拍照
    .take_photo {  width: 240rpx;  height: 96rpx;  background: white;  z-index: 1000;  display: flex;  flex-direction: row;  align-content: center;  align-items: center;  justify-content: space-around;  justify-items: center;  border-radius: 10rpx;  background: rgba(0, 0, 0, 0.4);  color: white;  position: fixed;  bottom: 8%;  left: 35%;  text-align: center;  font-size: 30rpx;}.take_photo_hover {  transform: scale(0.9);}

    3. 创建canvas绘制图片和裁剪框

    page组件中,初始化canvas并绘制图片和裁剪框:

    createCutImageCanvas(imageUrl) {  const that = this;  const query = that.createSelectorQuery();  query.select('#cut_image_canvas').fields({    node: true,    size: true  }).exec((res) => {    const canvas = res[0].node;    that.data.mycanvas = canvas;    that.data.cut_image_canvas = canvas.getContext('2d');    wx.getImageInfo({      src: imageUrl,      success: (res) => {        const w = that.data.window_width;        const h = that.data.window_height;        const imgWidth = res.width;        const imgHeight = res.height;        canvas.width = w;        canvas.height = h;        that.data.cut_area.cut_width = 0.8 * w;        that.data.cut_area.x = (w - that.data.cut_area.cut_width) / 2;        that.data.cut_area.y = (h - that.data.cut_area.cut_height) / 2;        that.setData({          cut_img_canvas_h: h,          cut_img_canvas_w: w        });        that.data.image_obj = canvas.createImage();        that.data.image_obj.src = imageUrl;        that.data.image_obj.onload = () => {          that.data.cut_image_canvas.drawImage(that.data.image_obj, 0, 0, w, h);          that.createCutArea();        };      }    });  });}

    4. 绘制裁剪框

    page组件中,绘制裁剪框:

    createCutArea() {  this.data.cut_image_canvas.strokeStyle = this.data.cut_area.cut_area_color;  this.data.cut_image_canvas.strokeRect(    this.data.cut_area.x,    this.data.cut_area.y,    this.data.cut_area.cut_width,    this.data.cut_area.cut_height  );  // 绘制四个缩放点  this.data.cut_image_canvas.beginPath();  this.data.cut_image_canvas.arc(    this.data.cut_area.x,    this.data.cut_area.y,    5 * this.data.pixelRatio,    0,    2 * Math.PI  );  this.data.cut_image_canvas.fillStyle = 'white';  this.data.cut_image_canvas.fill();  this.data.cut_image_canvas.beginPath();  this.data.cut_image_canvas.arc(    this.data.cut_area.x,    this.data.cut_area.y,    2.5 * this.data.pixelRatio,    0,    2 * Math.PI  );  this.data.cut_image_canvas.fillStyle = '#1296db';  this.data.cut_image_canvas.fill();  // 其他三个缩放点类似绘制...}

    5. 辨别裁剪框移动和缩放手势

    page组件中,处理触摸事件:

    getCutAreaChangeStatus(e) {  const that = this;  const x = e.touches[0].x;  const y = e.touches[0].y;  that.data.cut_area_change_status = '';  const scalePointLocation = that.getScalePointLocation();  const scaleResponseRadius = 30;  // 判断是否在移动区域  if ((scalePointLocation.left_up_point_x + scaleResponseRadius < x &&        x < scalePointLocation.right_down_point_x - scaleResponseRadius &&       scalePointLocation.left_up_point_y < y &&       y < scalePointLocation.right_down_point_y) {    that.data.cut_area_change_status = 'move';  }  // 判断是否在缩放区域  if (Math.sqrt(    Math.pow(x - scalePointLocation.left_up_point_x, 2) +     Math.pow(y - scalePointLocation.left_up_point_y, 2)  ) < scaleResponseRadius) {    that.data.cut_area_change_status = 'left_up_scale';  }  // 类似处理其他三个缩放点...}

    6. canvas重绘

    page组件中,实现canvas重绘:

    redraw(mode = 0) {  this.data.cut_image_canvas.clearRect(0, 0, this.data.cut_img_canvas_w, this.data.cut_img_canvas_h);  this.data.cut_image_canvas.drawImage(this.data.image_obj, 0, 0,     this.data.cut_img_canvas_w, this.data.cut_img_canvas_h);  if (mode === 0) {    this.createCutArea();  }}

    7. 防止裁剪框超出屏幕

    page组件中,实现裁剪框边界控制:

    watchCutAreaOverflow() {  const that = this;  const margin = 5 * that.data.pixelRatio;  const scalePointLocation = that.getScalePointLocation();  if (scalePointLocation.left_up_point_x < margin) return 1;  if (scalePointLocation.right_down_point_x > (that.data.window_width - margin)) return 1;  if (scalePointLocation.left_up_point_y < margin) return 1;  if (scalePointLocation.right_down_point_y > (that.data.window_height - margin)) return 1;  return 0;}

    8. 裁剪框移动和缩放

    page组件中,处理触摸移动事件:

    cutAreaMoveAndScale(e) {  const that = this;  const touch_x = e.touches[0].x;  const touch_y = e.touches[0].y;  const dx = touch_x - that.data.last_touches_x;  const dy = touch_y - that.data.last_touches_y;  if (that.data.cut_area_change_status === 'left_up_scale' ||       that.data.cut_area_change_status === 'left_down_scale') {    that.data.cut_area.cut_height += -dy;    that.data.cut_area.cut_width += -dx;    that.data.cut_area.x += dx;    that.data.cut_area.y += dy;    if (that.watchCutAreaOverflow()) {      that.data.cut_area.cut_height -= -dy;      that.data.cut_area.cut_width -= -dx;      that.data.cut_area.x -= dx;      that.data.cut_area.y -= dy;      return;    }    that.redraw();  }  if (that.data.cut_area_change_status === 'right_up_scale' ||       that.data.cut_area_change_status === 'right_down_scale') {    that.data.cut_area.cut_height += dy;    that.data.cut_area.cut_width += dx;    if (that.watchCutAreaOverflow()) {      that.data.cut_area.cut_width -= dx;      that.data.cut_area.cut_height -= dy;      return;    }    that.redraw();  }  if (that.data.cut_area_change_status === 'move') {    that.data.cut_area.x += dx;    that.data.cut_area.y += dy;    if (that.watchCutAreaOverflow()) {      that.data.cut_area.x -= dx;      that.data.cut_area.y -= dy;      return;    }    that.redraw();  }  that.data.last_touches_x = touch_x;  that.data.last_touches_y = touch_y;}

    9. 生成裁剪后的图片及预览

    page组件中,处理裁剪图片:

    cutImage() {  this.redraw(1); // 1表示不绘制裁剪框  wx.canvasToTempFilePath({    x: this.data.cut_area.x / this.data.pixelRatio,    y: this.data.cut_area.y / this.data.pixelRatio,    width: Math.round(this.data.cut_area.cut_width / this.data.pixelRatio),    height: Math.round(this.data.cut_area.cut_height / this.data.pixelRatio),    canvas: this.data.mycanvas,    filetype: 'png',    success: (res) => {      console.log('裁剪的图片', res.tempFilePath);      this.setData({        cut_image: res.tempFilePath      });    },    fail: (err) => {      console.log('裁剪图片失败', err);      wx.showModal({        title: '错误',        content: '剪切图片失败'      });    }  });}

    集成到项目

    以上代码已经封装成一个自定义组件,方便集成到项目中。如果需要直接在page中使用,可以直接复制以上代码到相应的page中。自定义组件下载地址:链接 提取码:wbau

    转载地址:http://uhet.baihongyu.com/

    你可能感兴趣的文章
    npm 安装依赖过程中报错:Error: Can‘t find Python executable “python“, you can set the PYTHON env variable
    查看>>
    npm.taobao.org 淘宝 npm 镜像证书过期?这样解决!
    查看>>
    npm—小记
    查看>>
    npm上传自己的项目
    查看>>
    npm介绍以及常用命令
    查看>>
    NPM使用前设置和升级
    查看>>
    npm入门,这篇就够了
    查看>>
    npm切换到淘宝源
    查看>>
    npm切换源淘宝源的两种方法
    查看>>
    npm前端包管理工具简介---npm工作笔记001
    查看>>
    npm包管理深度探索:从基础到进阶全面教程!
    查看>>
    npm升级以及使用淘宝npm镜像
    查看>>
    npm发布包--所遇到的问题
    查看>>
    npm发布自己的组件UI包(详细步骤,图文并茂)
    查看>>
    npm和package.json那些不为常人所知的小秘密
    查看>>
    npm和yarn清理缓存命令
    查看>>
    npm和yarn的使用对比
    查看>>
    npm如何清空缓存并重新打包?
    查看>>
    npm学习(十一)之package-lock.json
    查看>>
    npm安装 出现 npm ERR! code ETIMEDOUT npm ERR! syscall connect npm ERR! errno ETIMEDOUT npm ERR! 解决方法
    查看>>