编程语言
首页 > 编程语言> > 小程序使用taro将两张图合在一起,并保存到相册

小程序使用taro将两张图合在一起,并保存到相册

作者:互联网

// index.tsx

import React, { useState, useEffect, useCallback, FC } from 'react'
import {
  useShareAppMessage,
  getImageInfo,
  createCanvasContext,
  canvasToTempFilePath,
  getStorageSync,
  showToast,
  showModal,
  hideLoading,
  showLoading,
} from '@tarojs/taro'
import { AtActionSheet } from 'taro-ui'
import { View, Image, Button, Canvas } from '@tarojs/components'
import MainLayout from '@/layout/MainLayout'
import LoginButton from '@/components/LoginButton'
import ShareImageModal from './ShareImageModal'
import { fetchQRCode } from '@/apis/detail'
import shareImage from './share-image-icon.png'
import shareWx from './share-wx-icon.png'
import sharePaper from './sharePaper.png'
import activeDetail from './activeDetail@3x.png'
import './index.scss'

const Spread: FC = () => {
  // const userInfo = useUserInfo<IRecommenderUserInfo>()
  const [isOpen, setIsOpen] = useState(false)
  const [shareImageModal, setShareImageModal] = useState(false)
  const [isLoading, setIsLoading] = useState(false)
  const [qrCodeUrl, setQrCodeUrl] = useState<string>('')
  const [tpmPath, setTpmPath] = useState('')
  const [loadFlag, setLoadFlag] = useState(false)
  const [imgHeight, setImgHeight] = useState<number>(1)
  const isAccredit = getStorageSync('accredit') || false

  const onShareClick = val => {
    if (val) {
      // setIsOpen(true)
      setShareImageModal(true)
    } else {
      showToast({ title: '请授权手机号!', icon: 'none' })
    }
  }

  useShareAppMessage(() => ({
    title: '1234',
    path: ``,
    imageUrl: '',
  }))

  const onShareWX = () => {
    setTimeout(() => setIsOpen(false), 250)
  }
  const onShareImg = () => {
    setShareImageModal(true)
    setTimeout(() => setIsOpen(false), 250)
  }

  useEffect(() => {
    setIsLoading(true)
    fetchQRCode()
      .then(res => setQrCodeUrl(res.qrCodeUrl))
      .finally(() => setIsLoading(false))
  }, [])

  const imgToCanvas = useCallback(async () => {
    if (qrCodeUrl) {
      const canvas = createCanvasContext('canvasId')
      // showLoading({
      // 	title: '正在保存',
      // 	mask: true
      // })
      getImageInfo({
        src: sharePaper,
      }).then((res: any) => {
        canvas.drawImage(sharePaper, 0, 0, res.width / 3, res.height / 3)
        getImageInfo({
          src: qrCodeUrl,
        })
          .then(qrRes => {
            canvas.drawImage(
              qrRes.path,  // 将网络图片路径转成本地路径,因为drawImage的第一个参数是图片的本地路径;(所要绘制的图片资源(网络图片要通过 getImageInfo / downloadFile 先下载))
              30,
              res.height / 3 - 115,
              qrRes.width / 5,
              qrRes.height / 5
            )
            canvas.draw(true, () => {
              canvasToTempFilePath({
                canvasId: 'canvasId',
                fileType: 'png',
              }).then(res => setTpmPath(res.tempFilePath))
            })
          })
          .catch(res => {
            // hideLoading()
            showModal({
              title: '温馨提示',
              content: '小程序码图片合成失败,请重试',
              showCancel: false,
            })
          })
      })
    }
  }, [qrCodeUrl])

  useEffect(() => {
    imgToCanvas()
  }, [imgToCanvas])

  const onl oadImg = res => {
    setLoadFlag(true)
    setImgHeight(res.detail.height / 2)
  }

  const renderMenu = () => {
    return (
      <AtActionSheet isOpened={isOpen} cancelText="取消" onCancel={() => setIsOpen(false)}>
        <Button hoverClass="hover" onClick={onShareWX} openType="share">
          <Image src={shareWx} className="img" />
          <View className="txt">分享微信</View>
        </Button>
        <View onClick={onShareImg}>
          <Image src={shareImage} className="img" />
          <View className="txt">分享图片</View>
        </View>
      </AtActionSheet>
    )
  }
  return (
    <MainLayout className="spread">
      <View>
        <Image
          className="wp100"
          style={{ height: loadFlag ? imgHeight : 1 }}
          src={activeDetail}
          onl oad={onLoadImg}
          // mode="widthFix"
        />
        {/* <Image className="wp100" src={activeDetail} mode="widthFix" /> */}
        {/* {renderMenu()} */}
        <Canvas canvasId="canvasId" className="canvas"></Canvas>
        <View className="botm">
          {isAccredit ? (
            <Button className="btn" onClick={onShareClick}>
              分享返利
            </Button>
          ) : (
            <LoginButton className="btn" onLoginSuccess={onShareClick}>
              分享返利
            </LoginButton>
          )}
        </View>
        <ShareImageModal
          isOpen={shareImageModal}
          onClose={() => setShareImageModal(false)}
          tpmPath={tpmPath}
        />
      </View>
    </MainLayout>
  )
}
export default Spread

其中canvas的样式,为了不在当当前页面展示
 .canvas{
    width: 375PX;
    height: 812PX;
    visibility: hidden;
    position: fixed;
    top: 99999px;
    left: 99999px;
  }

// ShareImageModal.tsx

import React, { useEffect, useState } from 'react'
import c from 'classnames'
import { noop } from 'lodash'
import {
  showLoading,
  hideLoading,
  showToast,
  authorize,
  saveImageToPhotosAlbum,
  // getFileSystemManager
} from '@tarojs/taro'
import { View, Image, Swiper, SwiperItem } from '@tarojs/components'
import { useDebounce } from 'ahooks'
import './shareimage.scss'

export interface IShareImageModalProps extends IProps, IModalProps {
  title?: string
  onClose?(): void
  sharePath?: string
  tpmPath: string
}

const ShareImageModal: React.FC<IShareImageModalProps> = props => {
  const { isOpen, onClose = noop, sharePath = '', tpmPath} = props
  const debouncedOpen = useDebounce(isOpen, { wait: 250 })
  const delayOpen = useDebounce(isOpen, { wait: 10 }) && isOpen
  const [current, setCurrent] = useState(1)
  const [images, setImages] = useState<Array<{ fileUrl: string; enabled: boolean; id: number }>>([])

  useEffect(() => {
    if (isOpen) {
      Promise.resolve([{fileUrl: tpmPath, enabled:false,  id:1 }])
        .then(res => {
          setImages(res)
          setCurrent(Math.max(0, Math.floor((res.length - 1) / 2)))
        })
    }
  }, [sharePath, isOpen, images.length])

  // 点击保存图片
  const saveImageClickHandler = () => {
    showLoading({ title: '生成分享图...', mask: true })
    authorize({ scope: 'scope.writePhotosAlbum' })
      .then(() => {
        saveImageToPhotosAlbum({
          filePath: tpmPath,
          success (res: TaroGeneral.CallbackResult) {
            showToast({ title: '分享图已成功保存到相册', icon: 'none' })
          },
          fail (res: TaroGeneral.CallbackResult) {
            showToast({ title: '生成分享图失败,请重试', icon: 'none' })
          },
          complete (res: TaroGeneral.CallbackResult) {
            hideLoading()
          }
        })
      })
      .catch(() => void showToast({ title: '请授权保存图片权限以保存分享图', icon: 'none' }))
  }

  // 图片选择器切换
  const swiperChangeHandler = e => {
    const newIndex = e.detail.current
    setCurrent(newIndex)
  }

  // 此时弹窗处于关闭状态
  if (!isOpen && !debouncedOpen) {
    return null
  }

  return (
    <View className="share-image-modal" catchMove>
      <View
        className={c('share-image-modal__body', {
          'share-image-modal__body--actived': delayOpen,
        })}
      >
        <View className="menu-content">
          <Swiper
            current={current}
            onChange={swiperChangeHandler}
            previousMargin="140rpx"
            nextMargin="140rpx"
            duration={200}
            className="swiper"
          >
            {images.map((item, index) => (
              <SwiperItem
                onClick={() => void setCurrent(index)}
                className="swiper__item"
                key={item.id}
              >
                <Image
                  className={c('swiper__image', { current: index === current })}
                  src={item.fileUrl}
                  mode="aspectFit"
                />
              </SwiperItem>
            ))}
          </Swiper>
        </View>

        <View onClick={onClose} className="close at-icon at-icon-close"></View>
        <View onClick={saveImageClickHandler} className="confirm">
          保存图片
        </View>
      </View>
      <View
        className={c('share-image-modal__mask', {
          'share-image-modal__mask--actived': delayOpen,
        })}
      ></View>
    </View>
  )
}

export default ShareImageModal

// shareimage.scss

.share-image-modal {
  z-index: 5;

  &__mask {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(0, 0, 0, 0.6);
    transition: opacity 250ms;
    opacity: 0;

    &--actived {
      opacity: 1;
    }
  }

  &__body {
    z-index: 8;
    position: fixed;
    width: 100%;
    overflow: hidden;
    transition: all 250ms;
    bottom: calc(-1100px - #{$pageBottom});

    &--actived {
      bottom: 0;
    }

    .menu-content {
      background: #F5F6FA;
      border-radius: 24px 24px 0px 0px;
      height: 1000px;
      padding: 70px 0 60px;
    }

    .swiper-skeleton {
      height: 866px;
      width: 100%;
      background: transparent;
    }

    .swiper {
      height: 866px;
      width: 100%;

      &__item {
        width: 400px;
        height: 866px;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      &__image {
        width: 360px;
        height: 780px;
        box-shadow: 0px 4px 28px 0px rgba(0, 0, 0, 0.15);
        border-radius: 16px;
        transition: all 200ms;

        &.current {
          width: 400px;
          height: 866px;
        }
      }
    }

    .confirm {
      background: #FFFFFF;
      bottom: 0;
      height: calc(100px + #{$pageBottom});
      padding-bottom: $pageBottom;
      line-height: 80px;
      text-align: center;
      font-size: 32px;
      font-weight: 500;
      color: #0F1E3E;
      line-height: 100px;
    }

    .close {
      position: absolute;
      top: 28px;
      right: 28px;
      font-size: 30px;
      color: #C2C2C2;

      &::after {
        content: ' ';
        z-index: 15;
        position: absolute;
        top: -30px;
        bottom: -30px;
        right: -30px;
        left: -30px;
      }
    }
  }
}

标签:合在一起,const,相册,res,height,useState,import,taro,false
来源: https://www.cnblogs.com/wangwenhui/p/16071095.html