外围代码

import EXIF from "exif-js";// 入口const waterMarkToPanel = async (panelFile, waterMarkFile) => {  const orientation = await getOrientation(panelFile);  const panelBase64Url = await getBase64(panelFile);  const waterMarkBase64Url = await getBase64(waterMarkFile);  // 获取panel图片的原生宽度、高度、图片元素  const panelNatureData = await getNatureData(panelBase64Url);  // 获取水印图片的原生宽度、高度、图片元素  const waterMarkNatureData = await getNatureData(waterMarkBase64Url);  // 解决画布旋转/加水印  return startDraw(orientation, panelNatureData, waterMarkNatureData);};// 开始解决画布并加水印const startDraw = (orientation, panelNatureData, waterMarkNatureData) => {  const canvas = document.createElement("canvas");  const ctx: any = canvas.getContext("2d");  // panel图片数据  const { naturalWidth, naturalHeight, element: panelEle} =  panelNatureData;  // waterMark图片数据  const { naturalWidth: wNatureWidth, naturalHeight: wNatureHeight, element: waterMarkEle} =  waterMarkNatureData;  // 解决画布旋转, 挪动端存在一些兼容性,ios之类  switch (Number(orientation)) {    case 6:      console.log("照片顺时针旋转了270度");      canvas.width = naturalHeight; // 留神这里,canvas宽度理论取的是图片高度,因为图片的方位是旋转过270度的,以下类试      canvas.height = naturalWidth;      ctx.rotate((90 * Math.PI) / 180); // 既然照片方位顺时针转了270度,那再转90度,相当于一共转了360度,相当于没转,这是方位就正确了      ctx.translate(0, -canvas.width); // 转了90度,是不是地位也变了,想想图片在第一象限,绕原点顺时针转了90度,那就转到第二象限了,那就须要超y方向移上去      ctx.drawImage(panelEle, 0, 0, canvas.height, canvas.width); // 画图      // 复原坐标 画完图,须要将canvas复原原位,不解释了,有点绕      ctx.translate(0, canvas.width);      ctx.rotate((-90 * Math.PI) / 180);      break;    case 8:      console.log("照片顺时针旋转90度");      canvas.width = naturalHeight;      canvas.height = naturalWidth;      ctx.rotate((-90 * Math.PI) / 180);      ctx.translate(-canvas.height, 0);      ctx.drawImage(panelEle, 0, 0, canvas.height, canvas.width);      // 复原坐标      ctx.translate(canvas.height, 0);      ctx.rotate((90 * Math.PI) / 180);      break;    case 3:      console.log("照片顺时针旋转180度");      canvas.width = naturalWidth;      canvas.height = naturalHeight;      ctx.rotate((180 * Math.PI) / 180);      ctx.translate(-canvas.width, -canvas.height);      ctx.drawImage(panelEle, 0, 0, canvas.width, canvas.height);      // 复原坐标      ctx.translate(canvas.width, canvas.height);      ctx.rotate((-180 * Math.PI) / 180);      break;    default:      canvas.width = naturalWidth;      canvas.height = naturalHeight;      ctx.drawImage(panelEle, 0, 0, canvas.width, canvas.height);      break;  }  // 在画布上加水印 canvas.drawImage https://www.runoob.com/try/try.php?filename=tryhtml5_canvas_drawimage  ctx.drawImage(    waterMarkEle,    (40 / 750) * canvas.width,    canvas.height - ((40+ wNatureHeight) / 750) * canvas.width,    (wNatureWidth / 750) * canvas.width,    (wNatureHeight / 750) * canvas.width  );  const url = canvas.toDataURL("image/jpeg"); // 最终图片url  const file = dataURItoBlob(url); // 最终图片file  return {    url,    file  }};

工具函数

// 获取照片方向const getOrientation = (file) => {  return new Promise((resolve, reject) => {    const currentVersion = getVersion(); // ios 版本号    const support = testVersion(currentVersion, "13.4.1"); // ios版本号大于13.4.1, 无需非凡解决    if (browserInfo.versions.ios && support) {      resolve(1);    } else {      EXIF.getData(file, () => {        resolve(EXIF.getTag(file, "Orientation"));      });    }  });};// 获取base64const getBase64 = (file) => {  return new Promise((resolve, reject) => {    const reader = new FileReader();    reader.addEventListener("load", () => resolve(reader.result));    reader.readAsDataURL(file);  });};// 获取图片原生尺寸const getNatureData = (imgUrl) => {  return new Promise((resolve, reject) => {    const img = new Image();    img.setAttribute("crossOrigin", "Anonymous");    img.onload = (e: any) => {      resolve({        element: img        naturalWidth: e.target.naturalWidth,        naturalHeight: e.target.naturalHeight,      });    };    img.src = imgUrl;  });};// 判断ios版本 preVersion是否大于lastVersionexport const testVersion = (  preVersion: string = "",  lastVersion: string = "") => {  const sources = preVersion.split(".");  const dests = lastVersion.split(".");  const maxL = Math.max(sources.length, dests.length);  let result = true;  for (let i = 0; i < maxL; i++) {    let preValue = sources.length > i ? sources[i] : "0";    let preNum = isNaN(Number(preValue))      ? preValue.charCodeAt(0)      : Number(preValue);    let lastValue = dests.length > i ? dests[i] : "0";    let lastNum = isNaN(Number(lastValue))      ? lastValue.charCodeAt(0)      : Number(lastValue);    if (preNum < lastNum) {      result = false;      break;    } else if (preNum > lastNum) {      result = true;      break;    }  }  return result;};export const getVersion = () => {  const str = navigator.userAgent.toLowerCase();  const version = str.match(/cpu iphone os (.*?) like mac os/);  let currentVersion = "0.0.0"; // ios 版本号  if (version && version[1]) {    currentVersion = version[1].replace(/_/g, ".");  }  return currentVersion;};// 转换为 blob 间接传输 临时无用export const dataURItoBlob = (dataURI: string) => {  // convert base64 to raw binary data held in a string  // doesn't handle URLEncoded DataURIs  var byteString = atob(dataURI.split(",")[1]);  // separate out the mime component  var mimeString = dataURI    .split(",")[0]    .split(":")[1]    .split(";")[0];  // write the bytes of the string to an ArrayBuffer  var ab = new ArrayBuffer(byteString.length);  var ia = new Uint8Array(ab);  for (var i = 0; i < byteString.length; i++) {    ia[i] = byteString.charCodeAt(i);  }  try {    // 新版本浏览器    return new window.Blob([ia], { type: mimeString });  } catch (e) {    // TypeError old chrome and FF    // Android 中该形式有效    window.BlobBuilder =      window.BlobBuilder ||      window.WebKitBlobBuilder ||      window.MozBlobBuilder ||      window.MSBlobBuilder;    if (e.name === "TypeError" && window.BlobBuilder) {      var bb = new window.BlobBuilder();      bb.append(ab);      return bb.getBlob(mimeString);    } else {      return null;    }  }};

demo尝试

https://codesandbox.io/s/canv...