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

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

相关推荐
菜鸟学Python8 分钟前
零基础用AI编程开发微信小程序-开始篇
微信小程序·小程序·ai编程
q_19132846951 小时前
基于Springboot2+Vue2+uniapp的单商家在线点餐外卖小程序
vue.js·spring boot·mysql·小程序·uni-app·计算机毕业设计
2501_915918411 小时前
iOS CPU 使用率深度分析,多工具协同定位高占用瓶颈的工程化方法
android·ios·小程序·https·uni-app·iphone·webview
2501_915106322 小时前
如何防止资源文件被替换?一套针对 iOS App 的多层资源安全方案
android·安全·ios·小程序·uni-app·iphone·webview
烟囱土著2 小时前
捣鼓30天,我写了一个数学加减练习小程序
学习·算法·微信小程序·小程序
你真的可爱呀7 小时前
uniapp+vue3项目中的常见报错情况以及解决方法
前端·vue.js·uni-app
郑州光合科技余经理10 小时前
同城系统海外版:一站式多语种O2O系统源码
java·开发语言·git·mysql·uni-app·go·phpstorm
博客zhu虎康11 小时前
uniApp 开发
arcgis·uni-app
qq_124987075314 小时前
基于微信小程序的线下点餐系统的设计与实现(源码+论文+部署+安装)
spring boot·微信小程序·小程序·毕业设计
郑州光合科技余经理14 小时前
基于PHP:海外版同城O2O系统多语言源码解决方案
java·开发语言·git·spring cloud·uni-app·php·uniapp