美文网首页
微信小程序:将图片裁剪成九宫格图片(九张图片发朋友圈)

微信小程序:将图片裁剪成九宫格图片(九张图片发朋友圈)

作者: 我的小小笔尖 | 来源:发表于2022-04-24 14:14 被阅读0次

    1.先看小程序界面效果

    91.jpg 92.jpg 93.jpg

    2.用户故事

    1)用户选择本地照片或拍照上传
    2)用户拖动九宫格,选择截取的区域,九宫格可缩放
    3)用户点击裁剪按钮,按九宫格裁剪照片
    4)小程序按九宫格形式显示裁剪出的九张图片
    5)用户满意则可以保存九张图片到本地相册,不满意可以返回上一步
    6)用户朋友圈发九宫格图片

    朋友圈效果

    94.jpg

    3.小程序实现的主要技术说明

    page.wxml

    页面主要使用了movable-area、image、movable-view、canvas标签
    1)movable-area标签内包含了image和movable-view
    2)image显示用户上传的照片,movable-area大小和image照片大小一致
    3)movable-view设计成九宫格,可在movable-area区域内移动和缩放
    4)canvas用于画用户上传的照片,支持从canvas中截取一块区域保存为图片,canvas隐藏对用户不可见

    page.js

    js代码中主要使用了
    1)wx.chooseMedia 选择照片或拍照
    2)wx.getImageInfo 获取照片的宽度和高度
    3)wx.createSelectorQuery 获取和操作canvas
    4)wx.getSystemInfoSync().pixelRatio 获取设备像素比
    5)wx.getSystemInfoSync().windowWidth 获取屏幕宽度,单位px
    6)canvas.createImage() 创建图片对象
    7)ctx.drawImage 将图片对象写入canvas
    8)wx.canvasToTempFilePath 将canvas指定区域的内容导出为图片
    9)wx.previewImage 图片预览
    10)wx.saveImageToPhotosAlbum 将图片保存到相册

    4.完整代码

    page.wxml

    <view class="page">
    
      <view class="top"></view>
    
      <view class="body" wx:if="{{step!=3}}">
        <movable-area style="width:{{imageScreenWidth}}rpx;height:{{imageScreenHeight}}rpx;">
          <image src="{{sourceImageUrl}}" style="width:{{imageScreenWidth}}rpx;height:{{imageScreenHeight}}rpx;" wx:if="{{sourceImageUrl}}"></image>
          <movable-view 
            direction="all" 
            bindchange="onChange" 
            scale scale-min="0.2" scale-max="1" bindscale="onScale" 
            style="width:{{gridWidth}}rpx;height:{{gridHeight}}rpx;">
              <block wx:for="{{9}}" wx:for-item="cell" wx:for-index="cellIdx" wx:key="cellIdx">
                <view class="cell light bg-green" style="width:{{gridWidth/3-4}}rpx;height:{{gridHeight/3-3}}rpx;">{{cellIdx+1}}</view>      
              </block>
          </movable-view>
        </movable-area>
        <view class="btnLarge bg-green" wx:if="{{step==1}}" bindtap="onChooseImage">上传照片/拍照</view>
        <view class="btnWrap" wx:if="{{step==2}}">
          <view class="btnSmall bg-green" bindtap="onChooseImage">更换照片</view>
          <view class="btnLarge bg-blue" bindtap="onCutImage">按网格裁剪图片</view>
        </view>
        <view class="tips text-red" wx:if="{{step==2}}">图片上的网格可移动、可缩放</view>
      </view>
      <view class="body" wx:if="{{step==3}}">
        <view class="imageWrap">
          <block wx:for="{{cutImageList}}" wx:for-item="image" wx:for-index="imageIdx" wx:key="imageIdx">
            <image 
              src="{{image}}" 
              style="width:{{imageMaxWidth/3-4}}rpx;height:{{imageMaxWidth/3-3}}rpx;" 
              bindtap="onPreviewImage" 
              data-idx="{{imageIdx}}"></image>
          </block>
        </view>
        <view class="btnWrap" wx:if="{{step==3}}">
          <view class="btnSmall bg-blue" bindtap="onBackStep2">返回上一步</view>
          <view class="btnLarge bg-orange" bindtap="onSaveImages">保存所有图片</view>
        </view>
      </view>
    
      <view class="bottom"></view>
    
    </view>
    
    <!--通过css(position:fixed; left:100%;)隐藏canvas-->
    <!--A4是2480*3508象素 210*297毫米-->
    <canvas
      type="2d"
      id="canvas"
      canvas-id="canvas"
      style="width:{{imageScreenWidth2Px}}px; height:{{imageScreenHeight2Px}}px; position:fixed; left:100%;"
    ></canvas>
    

    page.js

    // pages/tools/cut9.js
    Page({
    
      /**
       * 页面的初始数据
       */
      data: {
        step: 1,
    
        gridX: 0,
        gridY: 0,
        gridScale: 1,
    
        imageMaxWidth: 690,
        sourceImageWidth: 690,
        sourceImageHeight: 690,
        imageScreenWidth: 690,
        imageScreenHeight: 690,
        imageScreenWidth2Px: Math.floor(690/750*wx.getSystemInfoSync().windowWidth),
        imageScreenHeight2Px: Math.floor(690/750*wx.getSystemInfoSync().windowWidth),
        gridWidth: 690,
        gridHeight: 690,
    
        cutImageList: [],
      },
    
      // 选择照片
      onChooseImage: function (e) {
        var that = this
        wx.chooseMedia({
          camera: 'back',
          sourceType: ['album', 'camera'],
          mediaType: ['image'],
          count: 1,
          success(res) {
            // console.log('### choose media success', res)
            // 0: {tempFilePath: "http://tmp/puQIgiWCT7TAcd9be358091eb7161d1af7d57c191eb4.jpg", size: 263247, fileType: "image"}
            let sourceImageUrl = res.tempFiles[0].tempFilePath
            that.setData({
              sourceImageUrl: sourceImageUrl
            })
            that.getSourceImageInfo()
          },
          fail(err) {
            console.error('### choose media failure', err)
          }
        })
      },
    
      // 获取图片宽高
      getSourceImageInfo() {
        var that = this
        let sourceImageUrl = this.data.sourceImageUrl
        wx.getImageInfo({
          src: sourceImageUrl,
          success (res) {
            // console.log('### get image info success', res)
    
            let sourceImageWidth = res.width
            let sourceImageHeight = res.height
            console.log('### source image width & height', sourceImageWidth, sourceImageHeight)
          
            let imageMaxWidth = that.data.imageMaxWidth
            let imageScreenWidth = sourceImageWidth
            let imageScreenHeight = sourceImageHeight        
            imageScreenWidth = imageMaxWidth
            imageScreenHeight = Math.floor((sourceImageHeight * imageMaxWidth) / sourceImageWidth)
            console.log('### image screen width & height', imageScreenWidth, imageScreenHeight)
    
            let imageScreenWidth2Px = Math.floor(imageScreenWidth/750*wx.getSystemInfoSync().windowWidth)
            let imageScreenHeight2Px = Math.floor(imageScreenHeight/750*wx.getSystemInfoSync().windowWidth)
            console.log('### image screen width(px) & height(px)', imageScreenWidth2Px, imageScreenHeight2Px)
    
            let gridWidth = imageScreenWidth<=imageScreenHeight ? imageScreenWidth : imageScreenHeight
            let gridHeight = gridWidth
            console.log('### grid width & height', gridWidth, gridHeight)
    
            that.setData({
              sourceImageWidth: sourceImageWidth,
              sourceImageHeight: sourceImageHeight,
              imageScreenWidth: imageScreenWidth,
              imageScreenHeight: imageScreenHeight,
              imageScreenWidth2Px: imageScreenWidth2Px,
              imageScreenHeight2Px: imageScreenHeight2Px,
              gridWidth: gridWidth,
              gridHeight: gridHeight,
              step: 2,
            })
          },
          fail(err) {
            console.error('### get image info failure', err)
          }
        })
      },
    
      // 移动网格
      onChange: function (e) {
        // console.log(e)
        let x = e.detail.x
        let y = e.detail.y
        // console.log('### onChange() x, y', x, y)
        this.setData({
          gridX: x,
          gridY: y,
        })
      },
    
      // 缩放网格
      onScale: function (e) {
        // console.log(e)
        let x = e.detail.x
        let y = e.detail.y
        let scale = e.detail.scale
        // console.log('### onScale() x, y, scale', x, y, scale)
        this.setData({
          gridX: x,
          gridY: y,
          gridScale: scale,
        })
      },
    
      // 按网格切图
      onCutImage: function (e) {
        // 显示提示
        wx.showLoading({
          title: '正在裁剪...',
          mask: true,
        })
        this.onCanvas()
      },
      
      // 调用 canvas
      onCanvas() {
        // 通过 SelectorQuery 获取 Canvas 节点
        console.log('### get canvas node.')
        wx.createSelectorQuery()
          .select('#canvas')
          .fields({
            node: true,
            size: true,
          })
          .exec((res)=>{
            this.initCanvas(res)
          })
      },
      // 初始化 canvas
      initCanvas(res) {
        var that = this
        
        const width = res[0].width
        const height = res[0].height
        console.log('### initial width & height', width, height)
    
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')
    
        try {
          const dpr = wx.getSystemInfoSync().pixelRatio
          console.log('### dpr', dpr)
    
          canvas.width = width * dpr
          canvas.height = height * dpr
          console.log('### by dpr set canvas width & height', canvas.width, canvas.height)
    
          that.setData({
            canvasWidth: canvas.width,
            canvasHeight: canvas.height,
            dpr: dpr,
          })
          ctx.scale(dpr, dpr)
          // 添加图片到 canvas
          this.addImage2Canvas(canvas, ctx)
        } catch (e) {
          console.log('### init canvas catch error', e)
        }
      },
      // 添加图片到 canvas
      async addImage2Canvas(canvas, ctx) {
        let imageUrl = this.data.sourceImageUrl
        // 创建图片对象
        console.log('### create image object.', imageUrl)
        const image = canvas.createImage()
        image.src = imageUrl
        // 绑定图片 onload 事件
        let imageObject = await new Promise((resolve, reject) => {
          image.onload = () => {
            console.log('### image onload success.')
            resolve(image)
          }
          image.onerror = (e) => {
            console.log('### image onload error.')
            reject(e)
          }
        })    
        // 添加图片
        // let imageWidth = this.data.sourceImageWidth
        // let imageHeight = this.data.sourceImageHeight
        let imageWidth = this.data.imageScreenWidth2Px
        let imageHeight = this.data.imageScreenHeight2Px
        console.log('### imageWidth, imageHeight', imageWidth, imageHeight)
        ctx.drawImage(imageObject, 0, 0, imageWidth, imageHeight)
        ctx.save()
        // 裁剪
        this.canvasCut(canvas)
      },
    
      // 裁剪
      canvasCut(canvas) {
        // 裁剪出第1张图片
        wx.showLoading({
          title: '正在裁剪...',
        })
        this.cutOneImage(0, canvas)
      },
    
      // 裁剪1张图片
      cutOneImage(idx, canvas) {
        var that = this
        let gridX = this.data.gridX
        let gridY = this.data.gridY
        console.log('### gridX, gridY', gridX, gridY)
    
        let baseX = Math.floor(this.data.gridX) //
        let baseY = Math.floor(this.data.gridY) //
        // let baseX = Math.floor(gridX*(this.data.imageScreenWidth/this.data.sourceImageWidth)) //
        // let baseY = Math.floor(gridY*(this.data.imageScreenWidth/this.data.sourceImageWidth)) //
        console.log('### baseX, baseY', baseX, baseY)
        
        let imageScreenWidth2Px = this.data.imageScreenWidth2Px
        let imageScreenHeight2Px = this.data.imageScreenHeight2Px
        let gridScale = this.data.gridScale
        // 纵向图片使用宽,横向图片使用高
        let cellWidth = Math.floor((imageScreenWidth2Px<imageScreenHeight2Px?imageScreenWidth2Px:imageScreenHeight2Px)/3*gridScale)
        let cellHeight = cellWidth
        let cutImageList = this.data.cutImageList
    
        let x = baseX+((idx%3)*cellWidth)
        let y = baseY+(Math.floor(idx/3)*cellHeight)
        console.log('### x, y, cellWidth, cellHeight', x, y, cellWidth, cellHeight)
        wx.canvasToTempFilePath({
          x: x,
          y: y,
          width: cellWidth,
          height: cellHeight,
          // destWidth: 100,
          // destHeight: 100,
          canvas: canvas,
          success(res) {
            console.log('### Canvas To Temp File Path Success.', idx, res.tempFilePath)
            cutImageList.push(res.tempFilePath)
          },
          fail(err) {
            console.log('### Canvas To Temp File Path ERROR.', err)
          },
          complete() {
            if(idx+1<9) {
              // 继续裁剪
              wx.showLoading({
                title: '正在裁剪第'+(idx+1)+'张',
              })
              that.cutOneImage(idx+1, canvas)
            }else {
              that.setData({
                cutImageList: cutImageList,
                step: 3,
              })
              // 隐藏提示
              wx.hideLoading({
                success: (res) => {},
              })
            }
          }
        })
      },
    
      // 预览图片
      onPreviewImage: function (e) {
        console.log('### preview image.')
        let idx = e.currentTarget.dataset.idx
        let cutImageList = this.data.cutImageList
        wx.previewImage({
          current: cutImageList[idx],
          urls: cutImageList,
          success(res) {
            console.log('### preview image success.', res)
          },
          fail(err) {
            console.log('### preview image failure.', err)
          }
        })
      },
    
      // 返回第二步
      onBackStep2: function (e) {
        this.setData({
          cutImageList: [],
          step: 2,
        })
      },
    
      // 保存所有图片
      onSaveImages: function (e) {
        wx.showLoading({
          title: '开始保存...',
        })
        // 保存第1张
        this.saveImage(0)
      },
    
      // 保存单张图片到相册
      saveImage(idx) {
        var that = this
        let cutImageList = this.data.cutImageList
        let filePath = cutImageList[idx]
        wx.saveImageToPhotosAlbum({
          filePath: filePath,
          success(res) {
            console.log('### Save Image To Photos Album SUCCESS.', idx)
            wx.showLoading({
              title: '已保存第'+(idx+1)+'张  ',
            })
          },
          fail(err) {
            console.log('### Save Image To Photos Album FAILURE.', err)
          },
          complete() {
            if((idx+1)<9) {
              that.saveImage(idx+1)
            }else {
              wx.hideLoading({
                success: (res) => {
                  wx.showToast({
                    title: '已保存所有图片',
                  })
                },
              })
            }
          }
        })
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      onLoad: function (options) {
    
      },
    
      /**
       * 生命周期函数--监听页面初次渲染完成
       */
      onReady: function () {
    
      },
    
      /**
       * 生命周期函数--监听页面显示
       */
      onShow: function () {
    
      },
    
      /**
       * 生命周期函数--监听页面隐藏
       */
      onHide: function () {
    
      },
    
      /**
       * 生命周期函数--监听页面卸载
       */
      onUnload: function () {
    
      },
    
      /**
       * 页面相关事件处理函数--监听用户下拉动作
       */
      onPullDownRefresh: function () {
    
      },
    
      /**
       * 页面上拉触底事件的处理函数
       */
      onReachBottom: function () {
    
      },
    
      /**
       * 用户点击右上角分享
       */
      onShareAppMessage: function () {
    
      }
    })
    

    page.wxss

    .page {  
      display: flex;
      flex-direction: column;
      justify-content: start;
      align-items: center;
    }
    
    
    
    .body {
      width: 100%;
    
      display: flex;
      flex-direction: column;
      justify-content: start;
      align-items: center;
    }
    movable-area image {
      border: 1rpx solid #cccccc;
    }
    movable-view {
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
      flex-wrap: wrap;  
    }
    .cell {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      opacity: 0.8;
      margin: 1rpx;
    }
    
    
    .imageWrap {
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
      flex-wrap: wrap;  
    }
    .imageWrap image {
      margin: 1rpx;
    }
    
    
    .btnWrap {
      width: 100%;
    
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
    }
    .btnSmall {
      width: 280rpx;
    }
    .btnLarge {
      width: 380rpx;
    }
    .btnLarge, .btnSmall {
      padding: 25rpx 0;
      border-radius: 15rpx;
      margin: 25rpx 10rpx;
      text-align: center;
    }
    .tips {
      font-size: small;
    }
    

    相关文章

      网友评论

          本文标题:微信小程序:将图片裁剪成九宫格图片(九张图片发朋友圈)

          本文链接:https://www.haomeiwen.com/subject/wvetyrtx.html