接口获取数据 画canvas

71 阅读3分钟
  viewCertDetail(data).then((res) => {
    if (res.result === 200) {
      const template = res.data;
      const { templateinfo } = template;

      // 初始化Canvas
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      canvas.width = 1089;
      canvas.height = 768;

      // 加载所有资源
      const loadResources = async () => {
        // 1. 加载背景图
        const backgroundImg = new Image();
        backgroundImg.crossOrigin = 'anonymous';
        backgroundImg.src = templateinfo.img;
        await new Promise((resolve, reject) => {
          backgroundImg.onload = resolve;
          backgroundImg.onerror = () => {
            ELMessage.error('背景图加载失败');
            console.error('背景图加载失败:', templateinfo.img);
            reject(new Error('背景图加载失败'));
          };
        });

        // 2. 加载印章图片
        const signImgs = await Promise.all(
          templateinfo.sign.map((item) => {
            return new Promise((resolve, reject) => {
              const img = new Image();
              img.crossOrigin = 'anonymous';
              img.src = item.url;
              img.onload = () => resolve(img);
              img.onerror = () => {
                ELMessage.error('印章图片加载失败');
                console.error('印章图片加载失败:', item.url);
                reject(new Error('印章图片加载失败'));
              };
            });
          })
        );

        // 3. 加载二维码图片
        const qrImgs = await Promise.all(
          templateinfo.qrcode.map(() => {
            return new Promise((resolve, reject) => {
              const img = new Image();
              img.crossOrigin = 'anonymous';
              img.src = qrImg;
              img.onload = () => resolve(img);
              img.onerror = () => {
                ELMessage.error('二维码图片加载失败');
                console.error(
                  '二维码图片加载失败:',
                  'https://static.xinxin.pro/images/qrcode.png'
                );
                reject(new Error('二维码图片加载失败'));
              };
            });
          })
        );

        // 4. 加载图片组件
        const imgComponents = await Promise.all(
          templateinfo.picture.map((item) => {
            return new Promise((resolve, reject) => {
              const img = new Image();
              img.crossOrigin = 'anonymous';
              img.src = Img;
              img.onload = () => resolve(img);
              img.onerror = () => {
                ELMessage.error('图片组件加载失败');
                console.error('图片组件加载失败:', item.url);
                reject(new Error('图片组件加载失败'));
              };
            });
          })
        );

        return {
          backgroundImg,
          signImgs,
          qrImgs,
          imgComponents,
        };
      };

      // 绘制所有元素
      const drawAll = async () => {
        try {
          const resources = await loadResources();
          const { backgroundImg, signImgs, qrImgs, imgComponents } = resources;

          const ratio = canvas.height / backgroundImg.height;
          const drawWidth = backgroundImg.width * ratio;
          const drawHeight = canvas.height;

          // 计算居中位置
          const drawX = (canvas.width - drawWidth) / 2;
          const drawY = 0;

          // 1. 绘制背景(高度等于画布高度,宽度按比例拉长,居中绘制)
          ctx.drawImage(backgroundImg, drawX, drawY, drawWidth, drawHeight);

          // 2. 绘制文本组件
          templateinfo.para.forEach((item, index) => {
            console.log(item.font.size, item.font.family, 8888888888);

            ctx.font = `${item.font.size}px ${item.font.family || ''}`;
            ctx.fillStyle = item.font.color;
            ctx.textBaseline = 'top';

            // 获取文本的宽度
            const textWidth = ctx.measureText(item.size).width;
            const textHeight = ctx.measureText(item.size).height;
            console.log(item);
            let drawX;
            // 根据对齐方式计算绘制的 X 坐标
            if (item.font.textalign === 'left') {
              drawX = item.position.left;
            } else if (item.font.textalign == 'center') {
              drawX = item.position.left + textWidth / 2 - item.font.size * 1.6;
            } else if (item.font.textalign == 'right') {
              // drawx = 1089 - item.position.left - textWidth;
              drawX =
                item.position.left + item.size.width - item.text.length * 1;
            } else {
              // 默认居左
              drawX =
                item.position.left + item.size.width / 2 - item.text.length * 1;
            }

            // 绘制文本
            ctx.fillText(item.text, drawX, item.position.top);

            // 绘制变量
            item.variable.forEach((varItem, varIndex) => {
              ctx.font = `${varItem.bold} ${varItem.size}px ${item.font.family}`;
              ctx.fillStyle = varItem.color;
              ctx.fillText(
                varItem.name,
                item.position.left + varItem.index * 10,
                item.position.top + varIndex * 20
              );
            });
          });

          // 3. 绘制印章
          templateinfo.sign.forEach((item, index) => {
            ctx.drawImage(
              signImgs[index],
              item.position.left,
              item.position.top,
              item.size.width,
              item.size.height
            );
          });

          // 4. 绘制二维码
          templateinfo.qrcode.forEach((item, index) => {
            ctx.drawImage(
              qrImgs[index],
              item.position.left,
              item.position.top,
              item.size.width,
              item.size.height
            );
          });

          // 5. 绘制图片组件
          templateinfo.picture.forEach((item, index) => {
            ctx.drawImage(
              imgComponents[index],
              item.position.left,
              item.position.top,
              item.size.width,
              item.size.height
            );
          });

          // 6. 绘制水印
          if (templateinfo.watermark.text) {
            ctx.save();
            ctx.font = `${templateinfo.watermark.fontSize}px sans-serif`;
            ctx.fillStyle = templateinfo.watermark.color;
            ctx.globalAlpha = templateinfo.watermark.alpha;

            const text = templateinfo.watermark.text;
            const textWidth = ctx.measureText(text).width;
            const textHeight =
              parseInt(templateinfo.watermark.fontSize, 10) * 1.2; // 估算文本高度

            const horizontalSpacing = templateinfo.watermark.xSpace || 0;
            const verticalSpacing = templateinfo.watermark.ySpace || 0;
            const offsetX = templateinfo.watermark.left || 0;
            const offsetY = templateinfo.watermark.top || 0;
            const rotation = (templateinfo.watermark.rotate * Math.PI) / 180;

            // 将画布原点移动到画布中心
            ctx.translate(canvas.width / 2, canvas.height / 2);
            // 旋转画布
            ctx.rotate(rotation);

            // 计算在旋转后的坐标系下的绘制范围
            const rotatedWidth =
              Math.abs(canvas.width * Math.cos(rotation)) +
              Math.abs(canvas.height * Math.sin(rotation));
            const rotatedHeight =
              Math.abs(canvas.width * Math.sin(rotation)) +
              Math.abs(canvas.height * Math.cos(rotation));

            // 计算绘制的起始位置,考虑偏移
            let startX = -rotatedWidth / 2 + offsetX;
            let startY = -rotatedHeight / 2 + offsetY;

            // 循环绘制水印
            for (
              let y = startY;
              y < rotatedHeight;
              y += textHeight + verticalSpacing
            ) {
              for (
                let x = startX;
                x < rotatedWidth;
                x += textWidth + horizontalSpacing
              ) {
                ctx.fillText(text, x, y);
              }
            }

            ctx.restore();
          } else if (templateinfo.watermark.imgUrl) {
            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.src = templateinfo.watermark.imgUrl;
            await new Promise((resolve, reject) => {
              img.onload = resolve;
              img.onerror = () => {
                console.error(
                  '水印图片加载失败:',
                  templateinfo.watermark.imgUrl
                );
                reject(new Error('水印图片加载失败'));
              };
            });

            ctx.save();
            ctx.globalAlpha = templateinfo.watermark.alpha;

            const horizontalSpacing = templateinfo.watermark.xSpace || 0;
            const verticalSpacing = templateinfo.watermark.ySpace || 0;
            const offsetX = templateinfo.watermark.left || 0;
            const offsetY = templateinfo.watermark.top || 0;
            const rotation = (templateinfo.watermark.rotate * Math.PI) / 180;

            // 将画布原点移动到画布中心
            ctx.translate(canvas.width / 2, canvas.height / 2);
            // 旋转画布
            ctx.rotate(rotation);

            // 计算在旋转后的坐标系下的绘制范围
            const rotatedWidth =
              Math.abs(canvas.width * Math.cos(rotation)) +
              Math.abs(canvas.height * Math.sin(rotation));
            const rotatedHeight =
              Math.abs(canvas.width * Math.sin(rotation)) +
              Math.abs(canvas.height * Math.cos(rotation));

            // 计算绘制的起始位置,考虑偏移
            let startX = -rotatedWidth / 2 + offsetX;
            let startY = -rotatedHeight / 2 + offsetY;

            const imgWidth = templateinfo.watermark.width || img.width;
            const imgHeight = templateinfo.watermark.height || img.height;

            // 循环绘制图片水印
            for (
              let y = startY;
              y < rotatedHeight;
              y += imgHeight + verticalSpacing
            ) {
              for (
                let x = startX;
                x < rotatedWidth;
                x += imgWidth + horizontalSpacing
              ) {
                ctx.drawImage(img, x, y, imgWidth, imgHeight);
              }
            }

            ctx.restore();
          }
          // 生成图片的Data URL
          const imgDataUrl = canvas.toDataURL('image/png');
          const newWindow = window.open('', '_blank');
          if (newWindow) {
            newWindow.document.write(`
    <!DOCTYPE html>
    <html>
      <head>
        <style>
          body {
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #f0f0f0;
          }
          img {
            max-width: 100%;
            max-height: 100vh;
            object-fit: contain;
          }
        </style>
      </head>
      <body>
        <img src="${imgDataUrl}" alt="Preview">
      </body>
    </html>
  `);
          } else {
            console.error('无法打开新窗口,请检查浏览器设置。');
          }

          // 创建一个img元素并设置其src属性为Data URL
          // const img = document.createElement('img');
          // img.src = imgDataUrl;

          // // 获取页面上用于展示图片的容器元素
          // const imageContainer = document.querySelector('.tbody');
          // if (imageContainer) {
          //   // 清空容器内的内容
          //   imageContainer.innerHTML = '';
          //   // 将生成的图片添加到容器中
          //   imageContainer.appendChild(img);
          // }
        } catch (error) {
          console.error('图片加载或绘制过程中出现错误:', error);
          // 可以在这里添加错误提示给用户
        }
      };

      drawAll();
    } else {
      ElMessage.error(res.message);
    }
  });