美文网首页微信小程序开发小程序微信小程序
微信小程序之图片选择、预览与上传

微信小程序之图片选择、预览与上传

作者: 一斤代码 | 来源:发表于2018-03-06 23:56 被阅读1283次

    所谓:一图胜千言。这话说明了图片描述事物的能力是非常强大的(怪不得我们可以用表情包聊一整天),尤其现在的手机拍照功能那么方便,用户对使用拍照和相册的需求日益上升。因此,在我们的移动应用中,可能经常会碰到这样的功能需求,需要为用户提供在相册中选择照片或者拍照片并上传的功能。

    例如下图所示的应用界面,这是一个比较典型的创建帖子或问答等内容的表单,用户可以填写标题和正文,并从自己的手机相册中选择3张图片(或直接通过摄像头拍摄),且当点击缩略图时,可以全屏预览查看这些图片:

    像这样一个带图片上传和预览功能的表单,在移动app中是比较常见的。那么在微信小程序中该如何来实现呢?且看我们一步步来构建这样的功能。

    标题和正文输入框

    对于这个表单,我们首先来创建上部的2个输入区域:标题和正文输入区。我们使用了一个单行输入框组件<input>来接收标题的输入,而使用一个多行输入组件<textarea>来接收正文的输入,并且为它们分别设置了maxlength属性来作最大输入字符数的限制。然后,为了更加直观,我们还为这2个输入区域分别放置了一个展示当前已输入字符数统计状态的标签。

    界面的WXML代码大致如下所示:

    <view class="question-input-area">
    
      <!-- 问题标题区域  -->
      <view class="question-title-wrap">
        <!-- 标题输入框 -->
        <input class="question-title" placeholder="请输入标题" maxlength="40" placeholder-style="color:#b3b3b3;font-size:18px;" bindinput="handleTitleInput"></input>
        <!-- 标题输入字数统计 -->
        <view class="title-input-counter">{{titleCount}}/40</view>
      </view>
    
      <!-- 问题正文区域  -->
      <view class="weui-cells weui-cells_after-title">
        <view class="weui-cell">
          <view class="weui-cell__bd">
            <!-- 多行输入框 -->
            <textarea class="weui-textarea" placeholder="请输入问题的正文内容。" maxlength="500" placeholder-style="color:#b3b3b3;font-size:14px;" style="height: 12rem" bindinput="handleContentInput" />
            <!-- 正文输入字数统计 -->
            <view class="weui-textarea-counter">{{contentCount}}/500</view>
          </view>
        </view>
      </view>
    
    </view>
    

    而与之对应的Page代码如下:

    import { $init, $digest } from '../../utils/common.util'
    
    Page({
    
      data: {
        titleCount: 0, //标题字数
        contentCount: 0, //正文字数
        title: '', //标题内容
        content: '' //正文内容
      },
    
      onLoad(options) {
        $init(this)
      },
    
      handleTitleInput(e) {
        const value = e.detail.value
        this.data.title = value
        this.data.titleCount = value.length  //计算已输入的标题字数
        $digest(this)
      },
    
      handleContentInput(e) {
        const value = e.detail.value
        this.data.content = value
        this.data.contentCount = value.length  //计算已输入的正文字数
        $digest(this)
      }
    }
    

    【注意】有人可能会对这里的一些代码觉得奇怪,这段JavaScript代码中出现的$init和$digest是什么?其实它是一个通过对象深层比较,将Page的data对象中的数据进行批量、按需更新到视图层WXML中的一个功能。对初学者来说,你暂且可以认为是在每个调用$digest(this)的地方调用了一次this.setData()的操作吧,方便理解。

    通过上面的两段代码,我们就已经把表单的输入框部分创建出来了。下面,我们要进入本文的关键功能部分。

    选择和预览图片、以及上传图片

    微信小程序提供的众多API中,wx.chooseImage函数就是用来访问手机相册或摄像头的。调用该函数后,界面下方会呼出一个菜单,可以分别选择进入相册挑选已有照片或是打开摄像头进行拍照:

    二话不说继续上代码!我们往WXML里新添一个按钮,点击该按钮就会触发wx.chooseImage的调用:

    <button
        type="default" size="mini" bindtap="chooseImage" 
        wx:if="{{images.length < 3}}"
    >添加图片</button>
    
    import { $init, $digest } from '../../utils/common.util'
    
    Page({
    
      data: {
        images: []
      },
    
      onLoad(options) {
        $init(this)
      },
    
      chooseImage(e) {
        wx.chooseImage({
          sizeType: ['original', 'compressed'],  //可选择原图或压缩后的图片
          sourceType: ['album', 'camera'], //可选择性开放访问相册、相机
          success: res => {
            const images = this.data.images.concat(res.tempFilePaths)
            // 限制最多只能留下3张照片
            this.data.images = images.length <= 3 ? images : images.slice(0, 3) 
            $digest(this)
          }
        })
      }
    }
    

    通过以上代码,我们就可以开始把玩起手机相册和摄像头了。但是目前选择了照片或拍了照之后,在表单界面上并不能看到。下面我们就要继续做选择图片后的展示工作。

    我们通过wx:for语法,将我们之前存在images数组中的照片展示到界面上来:

    <view class="question-images">
      <block wx:for="{{images}}" wx:key="*this">
        <view class="q-image-wrap">
          <!-- 图片缩略图  -->
          <image class="q-image" src="{{item}}" mode="aspectFill" data-idx="{{index}}" bindtap="handleImagePreview"></image>
          <!-- 移除图片的按钮  -->
          <view class="q-image-remover" data-idx="{{index}}" bindtap="removeImage">删除</view>
        </view>
      </block>
    </view>
    

    我们在每个缩略图元素上绑定了一个点击事件,当点击缩略图的时候,会调用微信小程序提供的预览图片的方法wx.previewImage进行全屏预览,用户可以左右滑动查看选中图片列表中的大图。另外,在每个缩略图的下方,还有一个删除按钮,用于移除所选的图片,方便重新选图。下面是对应的JS代码:

    import { $init, $digest } from '../../utils/common.util'
    
    Page({
    
      data: {
        images: []
      },
    
      onLoad(options) {
        $init(this)
      },
    
      removeImage(e) {
        const idx = e.target.dataset.idx
        this.data.images.splice(idx, 1)
        $digest(this)
      },
    
      handleImagePreview(e) {
        const idx = e.target.dataset.idx
        const images = this.data.images
        wx.previewImage({
          current: images[idx],  //当前预览的图片
          urls: images,  //所有要预览的图片
        })
      }
    }
    

    终于,只剩下最后一件事,就是提交表单数据及上传图片到后端,将的这些数据组成一个完整的问题,保存进数据库。

    对于我们的WXML,还缺最后这个提交按钮呢!立马补上吧:

    <!-- 提交表单按钮  -->
    <button class="weui-btn" type="primary" bindtap="submitForm">提交</button>
    

    然后就是这Page中的集大成者(大杂烩吧,哈哈)submitForm函数:

    import { $init, $digest } from '../../utils/common.util'
    
    Page({
    
      data: {
        images: []
      },
    
      onLoad(options) {
        $init(this)
      },
    
      submitForm(e) {
        const title = this.data.title
        const content = this.data.content
    
        if (title && content) {
          const arr = []
    
          //将选择的图片组成一个Promise数组,准备进行并行上传
          for (let path of this.data.images) {
            arr.push(wxUploadFile({
              url: config.urls.question + '/image/upload',
              filePath: path,
              name: 'qimg',
            }))
          }
    
          wx.showLoading({
            title: '正在创建...',
            mask: true
          })
    
          // 开始并行上传图片
          Promise.all(arr).then(res => {
            // 上传成功,获取这些图片在服务器上的地址,组成一个数组
            return res.map(item => JSON.parse(item.data).url)
          }).catch(err => {
            console.log(">>>> upload images error:", err)
          }).then(urls => {
            // 调用保存问题的后端接口
            return createQuestion({
              title: title,
              content: content,
              images: urls
            })
          }).then(res => {
            // 保存问题成功,返回上一页(通常是一个问题列表页)
            const pages = getCurrentPages();
            const currPage = pages[pages.length - 1];
            const prevPage = pages[pages.length - 2];
    
            // 将新创建的问题,添加到前一页(问题列表页)第一行
            prevPage.data.questions.unshift(res)
            $digest(prevPage)
    
            wx.navigateBack()
          }).catch(err => {
            console.log(">>>> create question error:", err)
          }).then(() => {
            wx.hideLoading()
          })
        }
      }
    }
    

    这个提交保存函数的主要流程是:

    1. 将图片分别通过文件上传APIwx.uploadFile进行上传,并返回上传后的图片地址备用;
    2. 接着将标题、正文、以及刚才的图片地址一并通过调用后端创建问题的API,保存到数据库中。
    3. 保存完毕,返回问题列表页

    在我的这个实现代码中,是将上传文件和创建问题分别通过2个后端API来进行的,其实wx.uploadFile除了上传文件,同时也可以携带其他表单数据,这样一来,就可以用单一API来实现。具体选择哪一种方式,主要看你们实际的后端API的设计了。

    最后,附上比较完整的源代码供大家参考吧。

    相关文章

      网友评论

      • 工程设计:小程序不允许个人添加内容,即不允许个人发布内容,请问您是怎么解决的?
        一斤代码:这个比较难判断腾讯的这条规则。比如,我做的文章类小程序,不允许个人发布文章,只允许小程序运营者发布文章;但是,个人用户可以发布文章下的评论。
      • 工程设计:真厉害,三下两下就实现这个非常必要的功能。
      • c822694aefe5:这个工具函数怎么用
      • Eugene1024:学习了 有空试试
        c822694aefe5:@z不羁的风 这个工具函数怎么用

      本文标题:微信小程序之图片选择、预览与上传

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