小程序-生成海报保存本地相册

需求:

小程序实现生成海报功能,海报上面加头像和姓名以及二维码

实现: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 合法域名。若不配置则在部分手机上打开调试工具的时候能正常运行,不打开调试工具的话不能正常绘图。

转载于:https://juejin.im/post/5cd50f49e51d456e6154b50a

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值