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

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

相关推荐
一个假的前端男5 小时前
uniapp 3端轮播
前端·javascript·uni-app
Memory沙漏5 小时前
IOS如何免费申请开发者证书(uniapp开发)
ios·uni-app
集成显卡8 小时前
AI取名大师 | uni-app + Wot UI 跟随设备自动切换明暗主题
人工智能·ui·uni-app·外观配色
zhangyao9403308 小时前
uni-app scroll-view特定情况下运用
前端·javascript·uni-app
vx_bscxy3229 小时前
告别毕设焦虑!Python 爬虫 + Java 系统 + 数据大屏,含详细开发文档 基于微信小程序的民宿预约系统22398 (上万套实战教程,赠送源码)
java·spring boot·mysql·微信小程序·课程设计
LXA080910 小时前
在 UniApp 中为小程序实现视频播放记录功能
小程序·uni-app·音视频
你真的可爱呀10 小时前
【uniapp实践】主题样式配置浅色深色以及自定义
uni-app·sass
小胖学前端12 小时前
解决 uniapp H5 与原生应用通信的坑:一个经过实战验证的解决方案
前端·uni-app
千寻技术帮14 小时前
50015_基于微信小程序的红色旅游系统
微信小程序·小程序·源码·ppt·项目文档