微信小程序uniapp开发附源码——图片加水印

开发三十个纯前端微信小程序小工具之图片加水印(30/7)

一个开箱即用的图片加水印工具小程序

页面截图

使用canvas来实现给图片加水印,主要对文字位置的计算需要注意,没有其他难点,单个的时候注意下计算文字与边框的距离问题,多个的话就最主要的就是判断循环结束条件 X轴: for(let x = startX; x < imageWidth + spacingX; x += spacingX) Y轴for(let y = startY; y < imageHeight + spacingY; y += spacingY)

下面直接贴一下核心代码:

xml 复制代码
    <canvas type="2d" id="watermarkCanvas" class="hidden-canvas"></canvas>
javascript 复制代码
      generateWatermark() {
      if (!this.imagePaths.length) {
        uni.showToast({
          title: "请选择图片",
          icon: "none",
        });
        return;
      }
      if (
        (this.watermarkType === "text" || this.watermarkType === "text-tile") &&
        !this.watermarkText
      ) {
        uni.showToast({
          title: "请输入水印文字",
          icon: "none",
        });
        return;
      }
      if (
        (this.watermarkType === "image" ||
          this.watermarkType === "image-tile") &&
        !this.watermarkImagePath
      ) {
        uni.showToast({
          title: "请选择水印图片",
          icon: "none",
        });
        return;
      }
      this.resultPaths = [];
      let processed = 0;
      uni.showLoading({ title: "生成中..." });
      const processOne = (imgPath, idx) => {
        uni.getImageInfo({
          src: imgPath,
          success: (res) => {
            const { width, height } = res;
            const query = uni.createSelectorQuery().in(this);
            query
              .select("#watermarkCanvas")
              .fields({ node: true, size: true })
              .exec((canvasRes) => {
                if (canvasRes[0]) {
                  const canvas = canvasRes[0].node;
                  const ctx = canvas.getContext("2d");
                  const dpr = uni.getSystemInfoSync().pixelRatio;
                  canvas.width = width * dpr;
                  canvas.height = height * dpr;
                  ctx.scale(dpr, dpr);
                  const img = canvas.createImage();
                  img.onload = () => {
                    ctx.clearRect(0, 0, width, height);
                    ctx.drawImage(img, 0, 0, width, height);
                    ctx.globalAlpha = this.opacity / 100;
                    if (
                      this.watermarkType === "text" ||
                      this.watermarkType === "text-tile"
                    ) {
                      ctx.font = `${this.fontSize}px Arial`;
                      ctx.fillStyle = this.textColor;
                      if (this.watermarkType === "text-tile") {
                        this.drawTextTile(ctx, width, height);
                      } else {
                        const position = this.calculatePosition(width, height);
                        const textMetrics = ctx.measureText(this.watermarkText);
                        const textWidth = textMetrics.width;
                        // 如果不是左侧位置,x坐标减去文本宽度
                        if (
                          ![
                            "top-left",
                            "middle-left",
                            "bottom-left",
                            "center",
                          ].includes(this.position)
                        ) {
                          position.x -= textWidth;
                        }
                        ctx.fillText(
                          this.watermarkText,
                          position.x,
                          position.y
                        );
                      }
                      uni.canvasToTempFilePath(
                        {
                          canvas: canvas,
                          success: (res) => {
                            this.$set(this.resultPaths, idx, res.tempFilePath);
                            processed++;
                            if (processed === this.imagePaths.length) {
                              uni.hideLoading();
                              uni.showToast({
                                title: "全部生成成功",
                                icon: "success",
                              });
                            }
                          },
                          fail: () => {
                            processed++;
                            if (processed === this.imagePaths.length) {
                              uni.hideLoading();
                            }
                          },
                        },
                        this
                      );
                    } else {
                      // 固定高度为watermarkFixedHeight,宽度等比缩放
                      const watermarkImg = canvas.createImage();
                      watermarkImg.onload = () => {
                        if (this.watermarkType === "image-tile") {
                          this.drawImageTileFixedHeight(
                            ctx,
                            watermarkImg,
                            width,
                            height
                          );
                        } else {
                          const fixedHeight = this.watermarkFixedHeight;
                          const scale = fixedHeight / watermarkImg.height;
                          const userScale = this.watermarkSize / 100;
                          const watermarkWidth =
                            watermarkImg.width * scale * userScale;
                          const watermarkHeight = fixedHeight * userScale;
                          const position = this.calculatePosition(
                            width,
                            height
                          );
                          // 仅左侧和居中不需要偏移,其他都要减去宽高
                          let drawX = position.x;
                          let drawY = position.y;
                          // 右上、右中、右下:drawX -= watermarkWidth,其它 drawX -= watermarkWidth / 3
                          if (
                            [
                              "top-right",
                              "middle-right",
                              "bottom-right",
                            ].includes(this.position)
                          ) {
                            drawX -= watermarkWidth / 1.5;
                          } else if (
                            ["top-center", "center", "bottom-center"].includes(
                              this.position
                            )
                          ) {
                            drawX -= watermarkWidth / 2;
                          } else {
                            drawX -= watermarkWidth / 3;
                          }
                          if (
                            [
                              "top-right",
                              "top-center",
                              "top-left",
                            ].includes(this.position)
                          ) {
                            drawY -= watermarkHeight;
                          } else if (
                            ["bottom-right", "bottom-left", "bottom-center"].includes(
                              this.position
                            )){
                            drawY -= watermarkHeight / 1.5;
                          }else {
                            drawY -= watermarkHeight / 2;
                          }
                          ctx.drawImage(
                            watermarkImg,
                            drawX,
                            drawY,
                            watermarkWidth,
                            watermarkHeight
                          );
                        }
                        uni.canvasToTempFilePath(
                          {
                            canvas: canvas,
                            success: (res) => {
                              this.$set(
                                this.resultPaths,
                                idx,
                                res.tempFilePath
                              );
                              processed++;
                              if (processed === this.imagePaths.length) {
                                uni.hideLoading();
                                uni.showToast({
                                  title: "全部生成成功",
                                  icon: "success",
                                });
                              }
                            },
                            fail: () => {
                              processed++;
                              if (processed === this.imagePaths.length) {
                                uni.hideLoading();
                              }
                            },
                          },
                          this
                        );
                      };
                      watermarkImg.onerror = () => {
                        processed++;
                        if (processed === this.imagePaths.length) {
                          uni.hideLoading();
                        }
                      };
                      watermarkImg.src = this.watermarkImagePath;
                    }
                  };
                  img.onerror = () => {
                    processed++;
                    if (processed === this.imagePaths.length) {
                      uni.hideLoading();
                    }
                  };
                  img.src = imgPath;
                } else {
                  processed++;
                  if (processed === this.imagePaths.length) {
                    uni.hideLoading();
                  }
                }
              });
          },
          fail: () => {
            processed++;
            if (processed === this.imagePaths.length) {
              uni.hideLoading();
            }
          },
        });
      };
      this.imagePaths.forEach((imgPath, idx) => processOne(imgPath, idx));
    },
    calculatePosition(imageWidth, imageHeight) {
      const margin = 20;
      let x, y;
      switch (this.position) {
        case "top-left":
          x = margin;
          y = margin + this.fontSize;
          break;
        case "top-center":
          x = imageWidth / 2;
          y = margin + this.fontSize;
          break;
        case "top-right":
          x = imageWidth - margin;
          y = margin + this.fontSize;
          break;
        case "middle-left":
          x = margin;
          y = imageHeight / 2;
          break;
        case "center":
          x = imageWidth / 2;
          y = imageHeight / 2;
          break;
        case "middle-right":
          x = imageWidth - margin;
          y = imageHeight / 2;
          break;
        case "bottom-left":
          x = margin;
          y = imageHeight - margin;
          break;
        case "bottom-center":
          x = imageWidth / 2;
          y = imageHeight - margin;
          break;
        case "bottom-right":
          x = imageWidth - margin;
          y = imageHeight - margin;
          break;
      }
      return { x, y };
    },
    drawTextTile(ctx, imageWidth, imageHeight) {
      ctx.save();
      const textMetrics = ctx.measureText(this.watermarkText);
      const textWidth = textMetrics.width;
      const textHeight = this.fontSize;
      const spacingX = this.tileSpacing;
      const spacingY = this.tileSpacing;
      const angle = (this.tileAngle * Math.PI) / 180;
      const startX = -spacingX;
      const startY = -spacingY;
      for (let x = startX; x < imageWidth + spacingX; x += spacingX) {
        for (let y = startY; y < imageHeight + spacingY; y += spacingY) {
          ctx.save();
          ctx.translate(x + textWidth / 2, y + textHeight / 2);
          ctx.rotate(angle);
          ctx.translate(-textWidth / 2, -textHeight / 2);
          ctx.fillText(this.watermarkText, 0, textHeight);
          ctx.restore();
        }
      }
      ctx.restore();
    },
    drawImageTile(ctx, watermarkImg, imageWidth, imageHeight) {
      ctx.save();
      const watermarkWidth = (imageWidth * this.watermarkSize) / 100;
      const watermarkHeight = (imageHeight * this.watermarkSize) / 100;
      const spacingX = this.tileSpacing;
      const spacingY = this.tileSpacing;
      const angle = (this.tileAngle * Math.PI) / 180;
      const startX = -spacingX;
      const startY = -spacingY;
      for (let x = startX; x < imageWidth + spacingX; x += spacingX) {
        for (let y = startY; y < imageHeight + spacingY; y += spacingY) {
          ctx.save();
          ctx.translate(x + watermarkWidth / 2, y + watermarkHeight / 2);
          ctx.rotate(angle);
          ctx.translate(-watermarkWidth / 2, -watermarkHeight / 2);
          ctx.drawImage(watermarkImg, 0, 0, watermarkWidth, watermarkHeight);
          ctx.restore();
        }
      }
      ctx.restore();
    },
    // 新增方法,支持图片平铺水印时固定高度
    drawImageTileFixedHeight(ctx, watermarkImg, imageWidth, imageHeight) {
      ctx.save();
      const fixedHeight = this.watermarkFixedHeight;
      const scale = fixedHeight / watermarkImg.height;
      const userScale = this.watermarkSize / 100;
      const watermarkWidth = watermarkImg.width * scale * userScale;
      const watermarkHeight = fixedHeight * userScale;
      const spacingX = this.tileSpacing;
      const spacingY = this.tileSpacing;
      const angle = (this.tileAngle * Math.PI) / 180;
      const startX = -spacingX;
      const startY = -spacingY;
      for (let x = startX; x < imageWidth + spacingX; x += spacingX) {
        for (let y = startY; y < imageHeight + spacingY; y += spacingY) {
          ctx.save();
          ctx.translate(x + watermarkWidth / 2, y + watermarkHeight / 2);
          ctx.rotate(angle);
          ctx.translate(-watermarkWidth / 2, -watermarkHeight / 2);
          ctx.drawImage(watermarkImg, 0, 0, watermarkWidth, watermarkHeight);
          ctx.restore();
        }
      }
      ctx.restore();
    },  

打代码不易,希望对你有帮助

源码链接:https://gitee.com/chenchongk/tool-box-watermark.git

有兴趣的同学可以在线体验一下小程序【口袋工具包】

相关推荐
毕设源码-郭学长15 小时前
【开题答辩全过程】以 基于uni-app的维修上门服务小程序设计与实现为例,包含答辩的问题和答案
uni-app
xiaohe060115 小时前
📦 Uni ECharts 是如何使用定制 echarts 的?一篇文章轻松掌握!
vue.js·uni-app·echarts
wan10416 小时前
用户隐私协议URL
微信小程序
Z单单17 小时前
微信小程序订单信息录入路径设置
微信小程序·小程序
Front思18 小时前
uniapp实现物流节点
uni-app
码界奇点18 小时前
基于Spring Boot和微信小程序的小程序商城系统设计与实现
spring boot·微信小程序·小程序·毕业设计·源代码管理
计算机毕设指导619 小时前
基于微信小程序的智慧社区娱乐服务管理系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·娱乐
赵庆明老师20 小时前
uniapp 微信小程序页面JS模板
javascript·微信小程序·uni-app
熬耶20 小时前
uniapp 简单实现列表左滑操作
uni-app
小白学过的代码21 小时前
UniApp 引入 Cesium 开发: RenderJS 避坑
uni-app