• 小程序使用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 onLoadImg = 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}
              onLoad={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{
         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;
         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;
           100%;
          background: transparent;
        }
    
        .swiper {
          height: 866px;
           100%;
    
          &__item {
             400px;
            height: 866px;
            display: flex;
            align-items: center;
            justify-content: center;
          }
    
          &__image {
             360px;
            height: 780px;
            box-shadow: 0px 4px 28px 0px rgba(0, 0, 0, 0.15);
            border-radius: 16px;
            transition: all 200ms;
    
            &.current {
               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;
          }
        }
      }
    }
    
    
  • 相关阅读:
    C#获取类以及类下的方法(用于Asp.Net MVC)
    ES6学习笔记
    在nuget上发布自己的程序集教程
    C#创建IIS站点及相应的应用程序池,支持IIS6.0+Windows Server 2003. 使用Builder设计模式
    ASP.Net Mvc实现自定义User Identity用户身份识别系统(2)
    ASP.Net Mvc实现自定义User Identity用户身份识别系统(1)
    C#实现.ini文件读写操作
    C#实现注册表 LocalMachine 目录下CURD工具类
    博客园打赏功能(未申请下js权限使用二维码打赏功能)
    WebServeice 动态代理类
  • 原文地址:https://www.cnblogs.com/wangwenhui/p/16071095.html
Copyright © 2020-2023  润新知