用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 查看内容

如何实现微信小程序图像剪切?代码拿去用,不谢!

Rolan 2019-11-4 00:41

前情回顾 我在早先发布的文章《如何实现微信小程序换头像?三步帮你搞定!》中,提到实现微信小程序换头像需要三步: 获取用户头像 图片模板 图片合成 前文已经就获取用户头像和图片模板两个步骤进行了讲解,本文 ...

前情回顾

我在早先发布的文章《如何实现微信小程序换头像?三步帮你搞定!》中,提到实现微信小程序换头像需要三步:

  1. 获取用户头像
  2. 图片模板
  3. 图片合成

前文已经就获取用户头像和图片模板两个步骤进行了讲解,本文就来详细说说如何合成图片。

图片合成的过程中非常重要的一块功能对图片进行剪切。该功能点很固定,大都是对图片进行拖拽、缩放后,在一定区域内剪切出一个固定长宽的图片。这类功能在app端和H5中都有很多成熟的插件供使用,接下来就来看看我在海豚趣图小程序中的头像剪切插件是如何实现的,欢迎大家提意见。

为了更好地理解接下来的代码,建议大家先扫描体验一下图片裁剪效果。

实现效果

界面实现

在H5中要实现图片的拖拽和缩放需要一大坨代码,具体实现网上有很多。小程序实现就简单的多了,通过 <movable-area><movable-view> 就可以实现上述功能

<view class="clip-view">
  <!--
    clipHeight、clipWidth 分别为剪切框的高和宽
    imgHeight、imgWidth 分别对应图片的初始高和宽
    imgUrl 为剪切图片的url地址
  -->
  <movable-area class="moveare" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
    <movable-view scale="true" scale-min="{{1}}" damping="1000" style="height: {{imgHeight}}px; width: {{imgWidth}}px; " direction="all" x="{{x}}" y="{{y}}" bindchange="_onChange" bindscale="_onScale">
      <image class="clip-img" src="{{imgUrl}}" />
    </movable-view>
  </movable-area>
  <!--剪切框 装饰用-->
  <view class="clip-box" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
    <!--剪切框四个角-->
    <view class="clip-border clip-border-lt"></view>
    <view class="clip-border clip-border-rt"></view>
    <view class="clip-border clip-border-lb"></view>
    <view class="clip-border clip-border-rb"></view>
  </view>
  <!--剪切图片用的canvas-->
  <canvas class="clip-canvas" id="img_clip_canvas" canvas-id="img_clip_canvas" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; "></canvas>
</view>
复制代码

组件属性

组件的入参只需要原始图片的地址和图片剪切框的宽高

/**
 * 组件的属性列表
 */
properties: {
  // 原始图片路径(要剪切的图片)
  imgUrl: {
    type: String,
    value: ''
  },
  // 剪切的宽度 (rpx)
  clipWidth: {
    type: Number,
    value: 500
  },
  // 截切的高度 (rpx)
  clipHeight: {
    type: Number,
    value: 500
  }
}
复制代码

组件data

组件的data就会多一些记录图片拖拽缩放的数据,需要注意的是,图片初始位置和图片拖拽位置没有使用同一变量存储,是为了防止在拖拽过程中产生抖动。

/**
 * 组件的初始数据
 */
data: {
  baseScale: 1,
  imgPath: '',
  imgWidth: 0, // 图片宽
  imgHeight: 0, // 图片高
  x: 0, // 图片初始时x轴位置
  y: 0, // 图片初始时y轴位置,之所以不和top共用一个变量,是因为如果频繁改变y值,图片会闪烁,x同理
  left: 0, // 图片拖拽后的x轴位置
  top: 0, // 图片拖拽后的y轴位置
  scale: 1 // 拖拽后的缩放比例
}
复制代码

组件初始化

组件的初始化方法用于把图片缩放到合适的大小,并使剪切框位于图片中央

/**
 * 初始化方法
 * 获取图片宽高后,把图片缩放至剪切框大小,
 * 并使剪切框位于图片中央
 **/
_init() {
  if (!this.data.imgUrl) return
  // 获取屏幕宽度
  let {
    screenWidth
  } = wx.getSystemInfoSync()
  // 计算屏幕rpx
  const rpx = screenWidth / 750
  // 获取图片宽高,然后缩放至剪切框大小
  wx.getImageInfo({
    src: this.data.imgUrl,
    success: ({
      width,
      height,
      path
    }) => {
      const cw = this.data.clipWidth * rpx
      const ch = this.data.clipHeight * rpx
      let scale = Math.max(cw / width, ch / height)
      const imgWidth = width * scale
      const imgHeight = height * scale
      this.setData({
        imgPath: path,
        imgWidth,
        imgHeight,
        baseScale: scale,
        x: (cw - imgWidth) / 2,
        y: (ch - imgHeight) / 2
      })
    }
  })
}
复制代码

图片拖拽缩放处理

完成初始化后就可以监听用户的拖拽和缩放操作了,主要是记录拖拽的位置和缩放的比例,具体到代码实现就是监听<movable-view> 的拖拽(bindchange)和缩放(bindscale)事件

  // 拖拽事件响应函数
  _onChange: function(e) {
    this.setData({
      x: e.detail.x,
      y: e.detail.y
    })
  }

  // 缩放事件响应函数
  _onScale: function(e) {
    this.setData({
      x: e.detail.x,
      y: e.detail.y,
      scale: e.detail.scale
    })
  }
复制代码

图片剪切实现

拖拽缩放完成后就是剪切了,剪切是利用了<canvas>重新绘制图片的剪切区域,保存到微信临时目录里,并返回保存路径。需要注意的是拖拽和缩放后记录的图片剪切位置并不是原图的位置,利用canvasdrawImage 进行绘制的时候需要转换成原图位置,或者先把canvas的坐标系进行缩放。注意:在自定义组件下调用 wx.createCanvasContext(string canvasId, Object this)方法时,第二个参数this不能省略,否则canvas绘制无响应

/**
  * 图片剪切入口方法
  */
clip() {
  const scale = this.data._scale * this.data._baseScale
  const canvasId = 'img_clip_canvas'
  imageClip(canvasId, this.data.imgPath, {
    x: this.data._left * -1,
    y: this.data._top * -1,
    scale,
    width: this.data.clipWidth,
    height: this.data.clipHeight
  }, this)
}

/**
 * 图片剪切
 *
 * @param canvas canvas组件id,用于绘制剪切图片
 * @param img 要剪切的图片
 * @param option 剪切的位置宽高等信息
 * @param option.left 剪切图片左边距
 * @param option.top 剪切图片上边距
 * @param option.width 剪切图片宽度
 * @param option.height 剪切图片高度
 * @param context 组件实例对象
 * 
 * @return new Promise(resolve=>ctx)
 */
imageClip(canvas, img, option, context) {
  return new Promise((resolve, reject) => {
    option = Object.assign({
      left: 0,
      top: 0,
      scale: 1,
      width: 0,
      height: 0
    }, option)
    let x = option.left / option.scale
    let y = option.top / option.scale
    let clipW = option.width / option.scale
    let clipH = option.height / option.scale
    const ctx = wx.createCanvasContext(canvas, context)
    ctx.drawImage(img,
      x,
      y,
      clipW,
      clipH,
      0,
      0,
      option.width,
      option.height)

    ctx.draw(false, (e) => {
      console.log(e)
      resolve()
    })

  })
},

/**
 * canvas 保存为临时文件
 */
function saveCanvasToTemp(canvas, option) {
  return new Promise((resolve, reject)=>{
    wx.canvasToTempFilePath({
      ...option,
      canvasId: canvas,
      success:resolve,
      fail:reject
    }, this)
  })
}
复制代码

写在最后

这就是小程序版图片剪切的主要代码实现,里面还有一些小点儿需要注意

  • <movable-view> 的damping的值要设置大写,否则可能会出现拖出界外的情况
  • 在自定义组件下调用 wx.createCanvasContext(string canvasId, Object this)方法时,第二个参数this不能省略,否则canvas绘制无响应
  • 切图时要记住转换成实际图片的大小


鲜花
鲜花
鸡蛋
鸡蛋
分享至 : QQ空间
收藏
原作者: 快狗打车前端团队 来自: 掘金