JavaScript和小程序写水印的方法示例

主要JavaScript方法:Canvas

文档地址:

绘制文本 - Web API | MDN在前一个章节中看过如何应用样式和颜色之后,我们现在来看一下如何在画布(canvas)中绘制文本。https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text

说一下目前主流的两种实现方式:

1.使用for循环将文字依次填充到不同的坐标位置

html代码示例:

复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Canvas Text Water Mask Example</title>
</head>

<body>
  <canvas id="myCanvas"></canvas>
  <script>
    drawWaterMask()

    function getDPR() {
      const dpr = devicePixelRatio || 1;
      return dpr;
    }

    function drawWaterMask() {
      const dpr = getDPR()
      const canvas = document.getElementById('myCanvas');
      const pageWidth = document.documentElement.clientWidth
      const pageHeight = document.documentElement.clientHeight
      const ctx = canvas.getContext('2d');
      // 提升水印清晰度,消除锯齿---start----
      canvas.width = pageWidth * dpr;
      canvas.height = pageHeight * dpr;
      canvas.style.width = pageWidth * dpr + 'px';
      canvas.style.height = pageHeight * dpr + 'px';
      ctx.scale(dpr, dpr); // 缩放绘图上下文,提升水印清晰度
      // 提升水印清晰度,消除锯齿---end----
      const FOR_NUM = 10
      const MAX_TEXT_Y = 200
      const OPACITY_NUM = 0.5 // 水印文字透明度
      ctx.rotate(45 * Math.PI / 180);//设置文字的旋转角度;

      const text = ('张三 2025-07-29 00:00:00'); // 在(x, y)位置写文字

      //对斜对角线以左部分进行文字的填充(两个for循环的配合,使得文字充满斜对角线的左下部分)
      for (let j = 1; j < FOR_NUM; j++) { //用for循环达到重复输出文字的效果,这个for循环代表纵向循环
        ctx.beginPath();
        ctx.font = `bold 12px Arial`;
        ctx.fillStyle = `rgba(169,169,169,${OPACITY_NUM})`;

        ctx.fillText(text, 0, MAX_TEXT_Y / 2 * j);
        for (let i = 1; i < FOR_NUM; i++) {//这个for循环代表横向循环,
          ctx.beginPath();
          ctx.font = `bold 12px Arial`;
          ctx.fillStyle = `rgba(169,169,169,${OPACITY_NUM})`;
          ctx.fillText(text, MAX_TEXT_Y * i, MAX_TEXT_Y / 2 * j);
        }
      }

      //对斜对角线以右部分进行文字的填充逻辑同上
      for (let j = 0; j < FOR_NUM; j++) {
        ctx.beginPath();
        ctx.font = `bold 12px Arial`;
        ctx.fillStyle = (`rgba(169,169,169,${OPACITY_NUM})`);

        ctx.fillText(text, 0, -MAX_TEXT_Y / 2 * j);
        for (let i = 1; i < FOR_NUM; i++) {
          ctx.beginPath();
          ctx.font = `bold 12px Arial`;
          ctx.fillStyle = `rgba(169,169,169,${OPACITY_NUM})`;
          ctx.fillText(text, MAX_TEXT_Y * i, -MAX_TEXT_Y / 2 * j);
        }
      }
    }
  </script>
</body>

</html>

小程序代码示例:

在wxml文件写下

复制代码
<canvas canvas-id='myCanvas1' style='width:100%;height:100vh;'></canvas>

具体方法如下:

复制代码
drawWaterMask (text) {
    if(!text) {
      return
    }
    var ctx = wx.createCanvasContext("myCanvas1");
    ctx.rotate(45 * Math.PI / 180);//设置文字的旋转角度;
    const FONT_SIZE = 12
    const FOR_NUM = 10
    const MAX_TEXT_Y = 200
    const OPACITY_NUM = 0.5
    //对斜对角线以左部分进行文字的填充
    for (let j = 1; j < FOR_NUM; j++) { 
      ctx.beginPath();
      ctx.setFontSize(FONT_SIZE);
      ctx.setFillStyle(`rgba(169,169,169,${OPACITY_NUM})`);
 
      ctx.fillText(text, 0, MAX_TEXT_Y/2 * j);
      for (let i = 1; i < FOR_NUM; i++) {
        ctx.beginPath();
        ctx.setFontSize(FONT_SIZE);
        ctx.setFillStyle(`rgba(169,169,169,${OPACITY_NUM})`);
        ctx.fillText(text, MAX_TEXT_Y * i, MAX_TEXT_Y/2 * j);
      }
    }
 
    for (let j = 0; j < FOR_NUM; j++) {
      ctx.beginPath();
      ctx.setFontSize(FONT_SIZE);
      ctx.setFillStyle(`rgba(169,169,169,${OPACITY_NUM})`);
 
      ctx.fillText(text, 0, -MAX_TEXT_Y/2 * j);
      for (let i = 1; i < FOR_NUM; i++) {
        ctx.beginPath();
        ctx.setFontSize(FONT_SIZE);
        ctx.setFillStyle(`rgba(169,169,169,${OPACITY_NUM})`);
        ctx.fillText(text, MAX_TEXT_Y * i, -MAX_TEXT_Y/2 * j);
      }
    }
    ctx.draw();
  }

然后在onLoad和onPullDownRefresh生命周期下使用

复制代码
this.drawWaterMask('具体文案')

注意:把按钮等的层级写高一点,以免被水印遮挡,点击不了

2.先填充一个文字,输出图片,作为页面的background,再利用background的repeat属性

html代码示例:

复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Canvas Text Water Mask Example</title>
</head>

<body>
  <canvas id="myCanvas" width="200" height="200" style="width:200px;height: 200px;display: none;"></canvas>
  <div id="outputImage" style="width:100%;height: 100vh;"></div>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    const FONT_SIZE = 16
    const MAX_TEXT_X = 10
    const MAX_TEXT_Y = 10
    const OPACITY_NUM = 0.5
    ctx.rotate(45 * Math.PI / 180);//设置文字的旋转角度;

    const text = ('张三 2025-07-29 00:00:00');
    ctx.fontSize = `${FONT_SIZE}px`;
    ctx.fillStyle = (`rgba(169,169,169,${OPACITY_NUM})`);
    // 在(X, Y)位置写文字
    ctx.fillText(text, MAX_TEXT_X, MAX_TEXT_Y);
    // 当背景图片图片填充,巧用repeat属性就不用循环多次写水印文字了
    const imageUrl = canvas.toDataURL('image/png'); // 可以选择'image/jpeg'等其他格式
    document.getElementById('outputImage').style.background = `url(${imageUrl}) repeat`;
    document.getElementById('outputImage').style.backgroundSize = '40%';
    document.getElementById('outputImage').style.backgroundPosition = 'left top';
  </script>
</body>

</html>
相关推荐
Nil20829 分钟前
leetcode 160相交链表
算法·leetcode·链表
IT_陈寒1 小时前
Vue的双向绑定把我坑惨了,原来这个场景不能用
前端·人工智能·后端
hunterandroid1 小时前
[Android 从零到一] Compose LazyColumn 性能优化:key、稳定性与重组治理
android·前端
迷途之人不知返1 小时前
算法系列2:滑动窗口
算法
lichenyang4532 小时前
从一次团队邀请开始:用 React、NestJS 与 Socket.IO 做可靠的实时通知
前端
Herbert_hwt2 小时前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
vtian2 小时前
一篇文章吃透 Monorepo:pnpm + Turborepo + Changesets 全流程实战(含 8 个踩坑)
前端
默_笙2 小时前
❗ 点击计数按钮,为什么"峨眉队"也跟着重新渲染?React.memo 说:我记住了
前端·javascript
31535669132 小时前
DeepSeek Harness 发布后,我没急着跑 Demo,先把 `.agents/` 翻了一遍
前端·后端·github
名字还没想好☜2 小时前
Next.js 用 Server Components 直连数据库:去掉 API 层的边界,和三条别踩的安全红线
前端·javascript·数据库·安全·react·next.js