小程式-生成海報儲存本地相簿

一諾淺淺發表於2019-05-10

需求:

小程式實現生成海報功能,海報上面加頭像和姓名以及二維碼

實現:Taro 小程式(js + redux + sass)

import { ComponentClass } from 'react'
import Taro, { Component } from '@tarojs/taro'
import { View, Image, Text, Video, Button } from '@tarojs/components'
import { connect } from '@tarojs/redux'
import {
  getUserInfoApi,
  getLittleAppQRCodeApi
} from '../../service/api.service';
import './collagePoster.scss'

@connect(({ globalData, authData, payInfoData }) => ({
  globalData,
  authData,
  payInfoData
}))
class collagePoster extends Component {
  config = {
      navigationBarTitleText: '生成海報',
      pages: [
          'pages/collagePoster/collagePoster'
      ]
  };
  constructor(props) {
      super(props);
      this.state = {
        headPhoto: '', // 頭像
        avatorSrc: '',
        studentName: '', // 姓名
        bgImg: 'https://appd.knowbox.cn/ss/miniapp/shareImg/collagePosterBg.png', // 背景圖
        bgSrc: '',
        codeImg: '', // 二維碼
        codeSrc: '', 
        shareText: '給我家孩子報了程式設計課,在家就能上課,大廠課程品質有保障,有一起參團的不?5節課超划算,手慢則無!',
        timeStart: 0,
        timeEnd: 0,
        isCanToAlbum: false, // 是否授權儲存圖片到相簿
        showModal: false // 是否展示彈窗
      };
  }

  // onLoad
  async componentWillMount() {
    Taro.showLoading({
      title: '海報生成中...',
      mask: true,
    })
    const {token} = this.props.authData;
    await this.getUserInfoFn(token);
  }
  // onReady
  componentDidMount() { }

  // onShow
  componentDidShow() { }

  // onHide
  componentDidHide() { }

  // onUnload
  componentWillUnmount() { }
 
  // 獲取頭像和姓名以及二維碼等資訊
  async getUserInfoFn(data) {
    const resp = await getUserInfoApi(data);
    const {Collageid} =this.props.payInfoData.data;
    let path = `pages/payCollage/payCollage?collageId=${Collageid}`
    const codeResp = await getLittleAppQRCodeApi(path);
    const codeImg = codeResp.data;
    const {headPhoto, studentName} = resp.data;
    this.setState({
      headPhoto,
      studentName,
      codeImg
    });
    const BGIMG = await this.downloadHttpImg(this.state.bgImg);
    const headPhotoImg = await this.downloadHttpImg(headPhoto);
    const codeImgSrc = await this.downloadHttpImg(codeImg);
    this.setState({
      bgSrc: BGIMG,
      avatorSrc: headPhotoImg,
      codeSrc: codeImgSrc
    }, () => {
      this.sharePosteCanvas()
    })
  }
  // 封裝的下載圖片函式
  // httpImg: string 圖片地址 要是https的哦!
  downloadHttpImg (httpImg) {
    return new Promise(((resolve, reject) => {
      Taro.downloadFile({
        url: httpImg,
        success: (res) => {
          if (res.statusCode === 200) {
            resolve(res.tempFilePath)
          } else {
            Taro.showToast({
              title: '圖片下載失敗!',
              icon:'none',
              duration: 2000
            })
          }
        },
        fail: (res) => {
          Taro.showModal({
            title: '提示圖片下載失敗!',
            content: `原因:${JSON.stringify(res)}`,
            success(res) {
              if (res.confirm) {
                console.log('使用者點選確定')
              } else if (res.cancel) {
                console.log('使用者點選取消')
              }
            }
          })
        }
      })
    }))
  }
  // 畫圖
  sharePosteCanvas () {
    let studentName = this.state.studentName;
    let avaterSrc = this.state.avatorSrc;
    let codeSrc = this.state.codeSrc;
    let bgImg = this.state.bgSrc;
    const ctx = Taro.createCanvasContext('posterCanvas');
    Taro.createSelectorQuery().select('#collagePosterId').boundingClientRect((rect) => {
      let height = rect.height,
      width = rect.width,
      studentNameTop = height * 0.27,
      avatorWidth = width * 0.17, // 頭像大小
      avatorTop = height * 0.1394,
      avatorleft = (width - avatorWidth) / 2,
      codeWidth = width * 0.2, // 二維碼大小
      codeHeight = width * 0.2 * 1.16, // 二維碼大小
      codeTop = height * 0.76,
      codeLeft = width * 0.67,
      circle = {
        x : avatorleft + avatorWidth / 2,
        y : avatorTop + avatorWidth / 2,
        r : avatorWidth / 2
      },
      textLeft = width / 2;
      ctx.setFillStyle('#fff');
      ctx.fillRect(0, 0, width, height);
      // 背景
      if (bgImg) {
        ctx.drawImage(bgImg, 0, 0, rect.width, height);
      }
      // 頭像
      if (avaterSrc) {
        ctx.save();
        ctx.beginPath();
        ctx.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2, false);
        ctx.setFillStyle('#EEEEEE')
        ctx.fill()
        ctx.clip(); //剪下路徑
        ctx.drawImage(avaterSrc, avatorleft, avatorTop, avatorWidth, avatorWidth);
        ctx.restore();
      }
      // 使用者資訊 - 姓名
      if (studentName) {
        ctx.setFontSize(16);
        ctx.setFillStyle('#fff');
        ctx.setTextAlign('center');
        ctx.fillText(studentName, textLeft, studentNameTop, 300);
      }

      //  繪製二維碼
      if (codeSrc) {
        ctx.drawImage(codeSrc, codeLeft , codeTop,  codeWidth, codeHeight)
      }

    }).exec()

    setTimeout(() => {
      ctx.draw();  // 這裡有個需要注意就是,這個方法是在繪製完成之後在呼叫,不然容易其它被覆蓋。
      wx.hideLoading();
    }, 1000)
    setTimeout(() => {
      this.handleSaveImg()
    }, 1000)
  }
  handleTimeStart () {
    this.setState({
      timeStart: new Date().getTime()
    })
  }
  // 儲存圖片
  handleSaveImg () {
    let that = this;
    Taro.getSetting({
      success: (res) => {
        Taro.authorize({
          scope: 'scope.writePhotosAlbum',
          success: (res) => {
            this.downloadImgToAlbum()
          },
          fail: (res) => {
            // 授權失敗 不能儲存圖片
            that.setState({
              showModal: true,
              isCanToAlbum: false
            })
          } 
        })
      }
    })
  }
  // 儲存圖片到相簿
  downloadImgToAlbum () {
    let that = this;
    Taro.canvasToTempFilePath({
      canvasId: 'posterCanvas',
      success: (res) => {
        let imgUrl = res.tempFilePath;
        Taro.saveImageToPhotosAlbum({
          filePath: imgUrl,
          success (res) {
            that.setState({
              showModal: true,
              isCanToAlbum: true
            })
          },
          fail (res) {
            that.setState({
              showModal: true,
              isCanToAlbum: false
            })
          } 
        })
      }
    });
  }
  // 關閉彈窗
  handleCloseModal () {
    this.setState({
      showModal: false
    })
  }
  // 複製文字
  copyText () {
    Taro.setClipboardData({data: this.state.shareText}).then(() => {
      Taro.showToast({
        title: '複製成功'
      })
    })
  }
  render() {
      const {
        headPhoto,
        studentName,
        showModal,
        isCanToAlbum,
        bgImg,
        codeImg,
        shareText
      } = this.state;
      return (
          <View>
            <View
              className='collagePoster'
              ref='collagePoster'
              id='collagePosterId'
              // onTouchStart={this.handleTimeStart}
              onLongPress={this.handleSaveImg}
            >
              <View className='main'>
                <View className='userInfoWrapper'>
                  {headPhoto && <Image src={headPhoto} alt='頭像' className='avator'></Image>}
                  <Text>{studentName}</Text>
                </View>
                {codeImg && <Image src={codeImg} alt='二維碼' className='codeImg'></Image>}
                <Image src={bgImg} alt='背景圖' className='bg_img'></Image>
              </View>
              <View className='space'></View>
              <canvas canvas-id="posterCanvas" style="width:750rpx;height:1334rpx;" /> 
            </View>
            {
              showModal ? 
              <View className='model-poster'>
                <View className='model-body'>
                    <View className="modal-btn-close" onClick={this.handleCloseModal}></View>
                    <View className='model-title'>{isCanToAlbum ? '- 海報已儲存至相簿 -' : '- 海報已生成,請截圖儲存 -'}</View>
                    <View className='model-tip'>複製文字一起分享,成功率翻3倍</View>
                    <View className='model-content'>{shareText}</View>
                    <Button onClick={this.copyText}>一鍵複製文字</Button>
                </View>
              </View> : null
            }
          </View>
      );
  }
}
複製程式碼

  1. 由介面拿到的圖片地址是https的要經過wx.downloadFile轉換一下。若不轉換則在實際的手機上繪製的圖是空白的無法正常實現繪製。
  2. 需要配置小程式域名資訊中的downloadFile 合法域名。若不配置則在部分手機上開啟除錯工具的時候能正常執行,不開啟除錯工具的話不能正常繪圖。

相關文章