微信小程序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

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

相关推荐
2501_916008897 小时前
全面介绍Fiddler、Wireshark、HttpWatch、SmartSniff和firebug抓包工具功能与使用
android·ios·小程序·https·uni-app·iphone·webview
webYin7 小时前
解决 Uni-App 运行到微信小程序时 “Socket合法域名校验出错” 问题
微信小程序·小程序·uni-app
蓝丶曦月15 小时前
MacM系列芯片安装 最新版本Notepad--(平替Windows系统的Notepad++)详细教程
编辑器·notepad++·mac
程序媛徐师姐19 小时前
Java基于微信小程序的模拟考试系统,附源码+文档说明
java·微信小程序·java模拟考试系统小程序·模拟考试微信小程序·模拟考试系统小程序·模拟考试小程序·java模拟考试小程序
大尚来也19 小时前
微信小程序开发费用全解析:从SaaS到定制的多元选择
微信小程序
奔跑的web.19 小时前
UniApp 路由导航守
前端·javascript·uni-app
如果你好19 小时前
UniApp 路由导航守卫
前端·微信小程序
特立独行的猫a19 小时前
主要跨端开发框架对比:Flutter、RN、KMP、Uniapp、Cordova,谁是未来主流?
flutter·uni-app·uniapp·rn·kmp·kuikly
大尚来也21 小时前
小程序怎么开发自己的小程序
微信小程序
码云数智-园园21 小时前
小程序开发平台有哪些?小程序第三方开发平台评测对比
微信小程序