从零到一教你在鸿蒙中实现微信分享--全流程

前言

大家好,我是青蓝逐码组织的君莫笑。

随着鸿蒙生态发展越来越好,微信各种功能也能随之接入了,今天教大家如何接入微信分享的所有功能,主要包括微信分享小程序,微信分享h5。此次分享只分享关键代码,最后需要大家自己学习然后合并成一个工具类哦

首先接入微信sdk

在终端输入 ohpm i @tencent/wechat_open_sdk 即可依赖微信的sdk

微信平台中创建鸿蒙应用(如已创建请跳过此处)

使用微信sdk需要应用在微信开发平台创建了鸿蒙应用,创建流程可查看链接,此处不做教学

实现微信分享前的准备工作

在module.json5中加入字段

创建WX实例(越早创建越好)

arduino 复制代码
// WXApi 是第三方app和微信通信的openApi接口,其实例通过WXAPIFactory获取,需要传入应用申请到的AppID
export const WXApi = wxOpenSdk.WXAPIFactory.createWXAPI(APP_ID)

说明

  • appid和微信平台安卓应用的appid相同,创建鸿蒙应用后直接复用即可

现在开始实现微信分享的工具类(搭建底子,最后实现的完整工具类自行合并)

typescript 复制代码
/**
 * 微信分享
 */
export class WxShareViewModel {
  // 此处换成真实appid
  WXApi = wxOpenSdk.WXAPIFactory.createWXAPI('')
  static instance: WxShareViewModel | null = null
  isWXApp: boolean = this.WXApi.isWXAppInstalled()

  private constructor() {

  }

  static getInstance() {
    if (!WxShareViewModel.instance) {
      WxShareViewModel.instance = new WxShareViewModel()
    }
    return WxShareViewModel.instance
  }
}

说明

  • 我推荐使用单例去实现工具类,这样比较简洁并且封装不易被污染

实现分享文本

typescript 复制代码
/**
 * 分享文本
 * @param text 文本内容
 */
textShare(text: string) {
  this.isWXAPPCallback(() => {
    let textObject = new wxOpenSdk.WXTextObject
    textObject.text = text
    let mediaMessage = new wxOpenSdk.WXMediaMessage()
    mediaMessage.mediaObject = textObject
    let req = new wxOpenSdk.SendMessageToWXReq()
    req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
    req.message = mediaMessage
    this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
  })
}

实现分享h5

1.其中需要关注的是,分享h5以及小程序携带图片,要进行下载图片buffer以及压缩到64kb以下才能进行分享,不然就会报错无反应

2.现在先实现压缩buffer至64kb以下

压缩算法我比较推荐华为官网的例子

说明

  • 在此不演示其他压缩方式,只压缩buffer格式,因为下载图片返回的就是buffer,我们直接通过压缩 buffer至64k以下传给sdk即可,不进行拓展
  • 我对华为的官网例子进行了改造,使得我们可以直接使用,并且压缩buffer格式
ini 复制代码
import { image } from '@kit.ImageKit';

/**
 * 图片压缩,保存
 * @param sourcePixelMap:原始待压缩图片的PixelMap对象
 * @param maxCompressedImageSize:指定图片的压缩目标大小,单位kb
 * @returns compressedImageInfo:返回最终压缩后的图片信息
 */
export async function compressedImage(sourcePixelMap: image.PixelMap,
  maxCompressedImageSize: number): Promise<ArrayBuffer> {
  // 创建图像编码ImagePacker对象
  const imagePackerApi = image.createImagePacker();
  const IMAGE_QUALITY = 0;
  const packOpts: image.PackingOption = { format: "image/jpeg", quality: IMAGE_QUALITY };
  // 通过PixelMap进行编码。compressedImageData为打包获取到的图片文件流。
  let compressedImageData: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
  // 压缩目标图像字节长度
  const maxCompressedImageByte = maxCompressedImageSize * 1024;
  // 图片压缩。先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。如果满足,则使用packing方式二分查找最接近指定图片压缩目标大小的quality来压缩图片。如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。
  if (maxCompressedImageByte > compressedImageData.byteLength) {
    // 使用packing二分压缩获取图片文件流
    compressedImageData =
      await packingImage(compressedImageData, sourcePixelMap, IMAGE_QUALITY, maxCompressedImageByte);
  } else {
    // 使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据
    let imageScale = 1;
    const REDUCE_SCALE = 0.4;
    // 判断压缩后的图片大小是否大于指定图片的压缩目标大小,如果大于,继续降低缩放倍数压缩。
    while (compressedImageData.byteLength > maxCompressedImageByte) {
      if (imageScale > 0) {
        // 性能知识点: 由于scale会直接修改图片PixelMap数据,所以不适用二分查找scale缩放倍数。这里采用循环递减0.4倍缩放图片,来查找确定最适合的缩放倍数。如果对图片压缩质量要求不高,建议调高每次递减的缩放倍数reduceScale,减少循环,提升scale压缩性能。
        imageScale = imageScale - REDUCE_SCALE;
        await sourcePixelMap.scale(imageScale, imageScale);
        compressedImageData = await packing(sourcePixelMap, IMAGE_QUALITY);
      } else {
        // imageScale缩放小于等于0时,没有意义,结束压缩。这里不考虑图片缩放倍数小于reduceScale的情况。
        break;
      }
    }
  }
  return compressedImageData;
}


/**
 * packing压缩
 * @param sourcePixelMap:原始待压缩图片的PixelMap
 * @param imageQuality:图片质量参数
 * @returns data:返回压缩后的图片数据
 */
async function packing(sourcePixelMap: image.PixelMap, imageQuality: number): Promise<ArrayBuffer> {
  const imagePackerApi = image.createImagePacker();
  const packOpts: image.PackingOption = { format: "image/jpeg", quality: imageQuality };
  const data: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
  return data;
}

/**
 * packing二分方式循环压缩
 * @param compressedImageData:图片压缩的ArrayBuffer
 * @param sourcePixelMap:原始待压缩图片的PixelMap
 * @param imageQuality:图片质量参数
 * @param maxCompressedImageByte:压缩目标图像字节长度
 * @returns compressedImageData:返回二分packing压缩后的图片数据
 */
async function packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: image.PixelMap, imageQuality: number,
  maxCompressedImageByte: number): Promise<ArrayBuffer> {
  // 图片质量参数范围为0-100,这里以10为最小二分单位创建用于packing二分图片质量参数的数组。
  const packingArray: number[] = [];
  const DICHOTOMY_ACCURACY = 10;
  // 性能知识点: 如果对图片压缩质量要求不高,建议调高最小二分单位dichotomyAccuracy,减少循环,提升packing压缩性能。
  for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) {
    packingArray.push(i);
  }
  let left = 0;
  let right = packingArray.length - 1;
  // 二分压缩图片
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    imageQuality = packingArray[mid];
    // 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。
    compressedImageData = await packing(sourcePixelMap, imageQuality);
    // 判断查找一个尽可能接近但不超过压缩目标的压缩大小
    if (compressedImageData.byteLength <= maxCompressedImageByte) {
      left = mid + 1;
      if (mid === packingArray.length - 1) {
        break;
      }
      // 获取下一次二分的图片质量参数(mid+1)压缩的图片文件流数据
      compressedImageData = await packing(sourcePixelMap, packingArray[mid + 1]);
      // 判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。如果大于,说明当前图片质量参数(mid)压缩出来的图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。
      if (compressedImageData.byteLength > maxCompressedImageByte) {
        compressedImageData = await packing(sourcePixelMap, packingArray[mid]);
        break;
      }
    } else {
      // 目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。
      right = mid - 1;
    }
  }
  return compressedImageData;
}

3.实现下载图片

javascript 复制代码
export function loadImageUrl(url: string, successCallBack: (ImgArrayBuffer: ArrayBuffer) => void) {
  http.createHttp()
    .request(url,
      {
        method: http.RequestMethod.GET,
      },
      async (error: BusinessError, data: http.HttpResponse) => {
        if (http.ResponseCode.OK === data.responseCode) {
          let imageBuffer: ArrayBuffer = data.result as ArrayBuffer;
          const imageSource = image.createImageSource(imageBuffer)
          imageSource.createPixelMap()
            .then((pixelMap: image.PixelMap) => {
              compressedImage(pixelMap, 64)
                .then((arrayBuffer) => {
                  successCallBack(arrayBuffer)
                })
            })
        }
      })
}

说明

  • 通过createHttp下载图片,url传入实际的网络地址
  • 第二个successCallBack为成功回调函数,网络请求成功后进行回调函数处理并且传入压缩后的arrayBuffer
  • 因为华为官网例子无法直接传入arrayBuffer进行压缩,而需要pixelMap格式,因此我们需要将arrayBuffer转换为pixelMap格式

4.实现是否安装微信回调函数

javascript 复制代码
isWXAPPCallback(callback: Function) {
  if (!this.isWXApp) {
    AlertDialog.show({ message: JSON.stringify("请先安装微信") })
  } else {
    callback()
  }
}

5.核心准备工作已经完成,我们开始实现分享h5

typescript 复制代码
/**
 * 分享h5
 * @param Url 网页地址
 * @param title 标题
 * @param description 描述
 * @param imgUrl 图片网络地址
 */
h5Share(Url: string, title: string, description: string, imgUrl: string) {
  this.isWXAPPCallback(async () => {
    const webpageObject = new wxOpenSdk.WXWebpageObject()
    webpageObject.webpageUrl = Url
    const mediaMessage = new wxOpenSdk.WXMediaMessage()
    mediaMessage.mediaObject = webpageObject
    mediaMessage.title = title
    mediaMessage.description = description
    loadImageUrl(imgUrl, async (buffer) => {
      mediaMessage.thumbData = new Uint8Array(buffer)
      const req = new wxOpenSdk.SendMessageToWXReq()
      req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
      req.message = mediaMessage
      this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
    })
  })
}

说明

  • 通过是否安装微信回调函数包裹,再进行核心代码
  • 最后进行下载图片的buffer函数,传入成功回调后的函数,并将压缩后的buffer传给sdk进行分享

实现微信分享小程序

typescript 复制代码
/**
 * 分享小程序
 * @param userName 小程序的原始 id(gh_xxxx形式的id)
 * @param path 小程序的 path
 * @param miniprogramType 小程序的类型, 默认正式版
 * @param title 标题
 * @param description 描述
 * @param imgUrl 网络图片地址
 */
miniProgramShare(userName: string, path: string, miniprogramType: number, title: string, description: string,
  imgUrl: string) {
  this.isWXAPPCallback(async () => {
    const miniProgramObject = new wxOpenSdk.WXMiniProgramObject()
    miniProgramObject.userName = userName
    miniProgramObject.path = path
    miniProgramObject.miniprogramType = miniprogramType
    const mediaMessage = new wxOpenSdk.WXMediaMessage()
    mediaMessage.mediaObject = miniProgramObject
    mediaMessage.title = title
    mediaMessage.description = description
    loadImageUrl(imgUrl, async (buffer) => {
      mediaMessage.thumbData = new Uint8Array(buffer)
      const req = new wxOpenSdk.SendMessageToWXReq()
      req.scene = wxOpenSdk.SendMessageToWXReq.WXSceneSession
      req.message = mediaMessage
      this.WXApi.sendReq(getContext(this) as common.UIAbilityContext, req)
    })
  })
}

说明

  • 压缩图片函数如分享h5一样
  • 大多数参数都是为接口下发

最后

以上就是本次分享给大家的一些教学了,如果这个文章对你有帮助,恳请三连。

青蓝逐码组织官网的服务器申请和域名在申请中,有结果了会第一时间分享我们的组织,后续我会持续分享一些实际项目遇到的难题解决方案,希望大家多多关注哦!

相关推荐
万事胜意5072 分钟前
前端切换Tab数据缓存实践
前端
渣渣宇a3 分钟前
Three_3D_Map 中国多个省份的组合边界绘制,填充背景
前端·javascript·three.js
点正6 分钟前
ResizeObserver 和nextTick 的用途
前端
zayyo8 分钟前
Web 应用轻量化实战
前端·javascript·面试
kovli12 分钟前
红宝书第十七讲:通俗详解JavaScript的Promise与链式调用
前端·javascript
lilye6612 分钟前
精益数据分析(19/126):走出数据误区,拥抱创业愿景
前端·人工智能·数据分析
别说我什么都不会17 分钟前
【仓颉三方库】算法类—— flexSearch4cj
harmonyos
李是啥也不会18 分钟前
Vue中Axios实战指南:高效网络请求的艺术
前端·javascript·vue.js
xiaoliang22 分钟前
《DNS优化真经》
前端
一只小海獭25 分钟前
了解uno.config.ts文件的配置项---转化器
前端