使用JS代码实现页面添加水印的方法详解

 更新时间:2024年05月01日 07:54:10   作者:日升  
这篇文章主要介绍了使用80 行 JS 代码实现页面添加水印:文字水印、多行文字水印、图片水印、文字&图片水印,文中通过代码示例给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下

一、水印概括

1. 添加水印的好处

  • 信息标识: 水印可以用于标识文档的所有者、保密级别、状态或其他相关信息,帮助用户更好地理解文档内容的属性。
  • 版权保护: 在文档中添加水印可以帮助保护内容的版权,防止他人未经授权地复制、转载或篡改内容。
  • 安全保护: 对于敏感信息或机密文档,添加水印可以帮助防止信息泄露,提高文档的安全性。
  • 提升专业性: 在一些场景下,如商业报告、合同文件等,添加水印可以增加文档的专业性和正式性。
  • 防止截屏或拷贝: 在网页中添加水印可以防止用户通过截屏或复制粘贴等方式非法获取文档内容。

2. 添加水印的坏处

  • 视觉干扰: 过多或过显眼的水印可能会影响页面的美观度和可读性,甚至干扰用户对页面内容的浏览和理解。
  • 性能影响: 在页面中添加水印可能会增加页面加载时间和资源消耗,特别是对于大型或复杂的水印图案或动态水印。
  • 用户体验: 一些用户可能会认为水印对页面内容的查看和操作产生不必要的干扰,从而降低他们的使用体验。
  • 不适用于移动端: 在移动设备上,屏幕空间有限,添加水印可能会占用宝贵的屏幕空间,影响用户的阅读和操作。

二、技术方案

1. watermark 第三方库

优势:已有 github 库,可直接使用 劣势:

  • 库比较老,多年未更新
  • 只支持图片水印,不支持文案水印
  • 不支持多行水印
  • 包比较重,增加 SDK 负担

2. JS 简单实现水印功能

优势:

  • 可支持图片和文案水印
  • 封装函数即可,轻量 劣势:需要自己封装

三、水印功能实现

1. 水印功能需求

  • 需要支持文案和图片
  • 可设置文案颜色字体等样式
  • 可支持多行文字水印
  • 可动态控制水印间隔
  • 能铺满全屏
  • 可旋转
  • 不影响页面其他模块背景展示

2. 功能实现

/**
 * 水印功能代码实现
 */
interface IWarterMarkProps{
  text?: string | string[]  // 水印文案
  imgUrl?: string           // 水印图片
  imageOpacity?: number     // 图片透明度
  fontStyle?: string        // 字体样式
  fontVariant?: string      // 字体变体
  fontWeight?: number       // 字体粗细
  fontFamily?: string       // 字体
  fontSize?: number         // 字体大小
  lineHeight?: number       // 字体行高
  fontColor?: string        // 字体颜色
  rotate?: number           // 水印旋转角度 deg
  canvasRotate?: number     // canvas 旋转角度 deg
  imageWidth?: number       // 图片宽度
  imageHeight?: number      // 图片高度
  xOffset?: number          // 水印 x 偏移
  yOffset?: number          // 水印 y 偏移
  width?: number            // 宽度
  height?: number           // 高度
  zIndex: number            // 层级
}
interface ISetDomProps{
  url: string               // 图片 URL
  rotate?: number           // 水印旋转角度
  zIndex?: number           // 层级
  width?: number            // 宽度
}
/**
 * 设置 dom CSS 属性
 * @param {ISetDomProps} props dom 数据类型
 */
const setDomCSSAttr = (props: ISetDomProps) => {
  const {
    url, rotate = 0, zIndex = 10, width = 32
  } = props
  let waterMarker = document.getElementById('water-marker')
  let isInitDom = true
  if (!waterMarker) {
    isInitDom = false
    waterMarker = document.createElement('div')
    waterMarker.id = 'water-marker'
  }
  Object.assign(waterMarker.style, {
    transform: rotate ? `rotate(${rotate}deg)` : '',
    backgroundSize: `${width}px`,
    backgroundImage: `url(${url}), url(${url})`,
    backgroundRepeat: 'repeat',
    position: 'fixed',
    left: 0,
    top: 0,
    zIndex,
    width: '100vw',
    height: '100vh',
    pointerEvents: 'none',
    mixBlendMode: 'multiply'
  });
  !isInitDom && document.body.appendChild(waterMarker)
}

/**
 * 获取 canvas 像素比
 * @param {CanvasRenderingContext2D} context 
 * @returns 像素比
 */
const getRatio = (context: any) => {
  if (!context) {
    return 1;
  }
  const backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1
  return (window.devicePixelRatio || 1) / backingStore
}

/**
 * 设置水印
 * @param {IWarterMarkProps} props 水印各种属性
 */
const setWarterMarker = (props: IWarterMarkProps) => {
  const {
    imgUrl = '', imageOpacity = 1, imageWidth = 0, imageHeight = 0, fontColor = 'rgba(128, 128, 128, .2)',
    fontVariant = 'normal', fontWeight = 400, fontSize = 14, lineHeight = 14, fontStyle = 'normal', fontFamily = 'arial', rotate = 0, canvasRotate = 0,
    yOffset = 0, xOffset = 0, width = 200, height = 200, zIndex = 10,
  } = props
  const text = typeof props.text === 'object' ? props.text : props.text ? [props.text] : []
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
  const ratio = getRatio(ctx);
  const canvasWidth = width * ratio
  const canvasHeight = height * ratio
  const canvasOffsetLeft = xOffset * ratio
  const canvasOffsetTop = yOffset * ratio
  canvas.width = canvasWidth
  canvas.height = canvasHeight
  ctx.rotate(canvasRotate * (Math.PI / 180));
  let url = ''
  if (text.length) {
    ctx.font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize * ratio}px/${lineHeight * ratio}px ${fontFamily || getComputedStyle(document.body).fontFamily}`
    ctx.fillStyle = fontColor
    text.forEach((content, index) => {
      ctx.fillText(content, canvasOffsetLeft, index * lineHeight + canvasOffsetTop + lineHeight * ratio)
    })
    url = canvas.toDataURL()
  }
  if (imgUrl) {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.referrerPolicy = 'no-referrer';
    img.src = imgUrl;
    img.onload = () => {
      ctx.globalAlpha = imageOpacity
      ctx.drawImage(img, canvasOffsetLeft, canvasOffsetTop, (imageWidth || (imageHeight ? img.width * imageHeight / img.height : img.width)) * ratio, (imageHeight || (imageWidth ? img.height * imageWidth / img.width : img.height)) * ratio)
      setDomCSSAttr({
        url: canvas.toDataURL(),
        rotate,
        zIndex,
        width
      })
      return
    };
    return
  }
  setDomCSSAttr({
    url,
    rotate,
    zIndex,
    width
  })
}

3. 步骤解析

3.1. setWarterMarker 方法

setWarterMarker 是使用水印的方法,参数是一个对象,包含以下内容

interface IWarterMarkProps{
  text?: string | string[]  // 水印文案
  imgUrl?: string           // 水印图片
  imageOpacity?: number     // 图片透明度
  fontStyle?: string        // 字体样式
  fontVariant?: string      // 字体变体
  fontWeight?: number       // 字体粗细
  fontFamily?: string       // 字体
  fontSize?: number         // 字体大小
  lineHeight?: number       // 字体行高
  fontColor?: string        // 字体颜色
  rotate?: number           // 水印旋转角度 deg
  canvasRotate?: number     // canvas 旋转角度 deg
  imageWidth?: number       // 图片宽度
  imageHeight?: number      // 图片高度
  xOffset?: number          // 水印 x 偏移
  yOffset?: number          // 水印 y 偏移
  width?: number            // 宽度
  height?: number           // 高度
  zIndex: number            // 层级
}
  • 获取各个参数值
  • 创建 canvas,获取 canvas 的像素比,设置 canvas 的各种参数
  • 如果存在 text 属性,则进行遍历绘制
  • 如果有图片,则创建 image,再进行绘制
  • 生成 url,通过 setDomCSSAttr 方法进行 dom 渲染

3.2. getRatio 方法

获取 canvas 的像素比,更清晰的绘制 canvas

3.3. setDomCSSAttr 方法

接收一个对象,包含以下内容

interface ISetDomProps{
  url: string               // 图片 URL
  rotate?: number           // 水印旋转角度
  zIndex?: number           // 层级
  width?: number            // 宽度
}
  • 查找/创建 dom 元素
  • 设置 style 属性
    • transform: 旋转
    • backgroundSize:背景大小
    • backgroundImage:背景图片
    • backgroundRepeat:背景重复
    • position:定位
    • zIndex:层级
    • pointerEvents:不能被选中
    • mixBlendMode:控制元素的混合模式

3.4. CSS mix-blend-mode 属性

mix-blend-modeCSS 中的一个属性,用于控制元素的混合模式。它可以让元素的背景色和内容色以一种特定的方式混合在一起,从而产生各种视觉效果。 以下是一些常用的 mix-blend-mode 值:

  • normal:默认值,内容色完全覆盖背景色。
  • multiply:将内容色与背景色相乘。通常用于创建暗色效果。
  • screen:将内容色与背景色相除,并取反。通常用于创建亮色效果。
  • overlay:结合了 multiplyscreen 模式,根据背景色的亮度来决定内容色的混合模式。
  • darken:内容色和背景色中的每个像素值,取对应像素值的较小者。
  • lighten:内容色和背景色中的每个像素值,取对应像素值的较大者。
  • color-dodge:颜色减淡,通过减少背景色的值来增加内容色的亮度。
  • color-burn:颜色加深,通过增加背景色的值来降低内容色的亮度。
  • difference:计算内容色和背景色之间的差异,产生颜色反转的效果。
  • exclusion:将内容色和背景色相减,并添加其结果的两倍。
  • hue:保留内容色的色相,并使用背景色的饱和度和亮度。
  • saturation:保留内容色的饱和度,并使用背景色的色相和亮度。
  • color:保留内容色的色相和饱和度,并使用背景色的亮度。
  • luminosity:保留内容色的亮度,并使用背景色的色相和饱和度。

使用 mix-blend-mode 属性,可以轻松地创建各种视觉效果,例如颜色混合、遮罩效果等。

三、水印使用

1. 文字

setWarterMarker({
  text: '这是水印, 这是水印, 这是水印',
  width: 200,
  height: 200,
  fontColor: 'red',
  fontSize: 8,
  lineHeight: 10,
})

2. 图片

setWarterMarker({
  imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
  imageHeight: 50,
  imageWidth: 50,
  imageOpacity: 0.1,
})

3. 图片和文字结合

setWarterMarker({
  text: '这是水印, 这是水印, 这是水印',
  width: 200,
  height: 200,
  fontColor: 'red',
  fontSize: 8,
  lineHeight: 10,
  imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
  imageHeight: 50,
  imageWidth: 50,
  imageOpacity: 0.1,
  canvasRotate: 15
})

4. 多行水印和图片

setWarterMarker({
  text: ['这是水印, 这是水印, 这是水印', '第二行水印', '第三行水印', '第四行水印'],
  width: 200,
  height: 200,
  fontColor: 'red',
  fontSize: 8,
  lineHeight: 40,
  imgUrl: 'https://sponsors.vuejs.org/images/chrome_frameworks_fund.png',
  imageHeight: 50,
  imageWidth: 50,
  imageOpacity: 0.1,
})

四、总结

  • 水印技术调研显示,水印在保护版权、信息标识和安全保护方面具有重要作用。
  • 通过CSS、JavaScript或第三方库,可以实现页面水印功能。
  • 过多或显眼的水印可能影响用户体验,而性能和兼容性也是考虑的因素。
  • 综合评估水印的利弊,可结合实际需求谨慎选择适合的实现方式。
  • 如果需求有更复杂的水印,可以按步骤去改即可。

以上就是使用JS代码实现页面添加水印的方法详解的详细内容,更多关于JS页面添加水印的资料请关注脚本之家其它相关文章!

相关文章

  • promise封装wx.request的方法

    promise封装wx.request的方法

    这篇文章主要介绍了promise封装wx.request的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-05-05
  • 关于小程序优化的一些建议(小结)

    关于小程序优化的一些建议(小结)

    这篇文章主要介绍了关于小程序优化的一些建议(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • JavaScript实现图片缩放功能

    JavaScript实现图片缩放功能

    本文,我们来讲讲如何使用 JavaScript 实现图片的缩放,当然,我们可以类比到其他的元素,比如视频的缩放,文中有详细的代码示例,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-07-07
  • js实现弹窗效果

    js实现弹窗效果

    这篇文章主要为大家详细介绍了js实现弹窗效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • 如何基于JS截获动态代码

    如何基于JS截获动态代码

    这篇文章主要介绍了JS注入eval, Function系统函数并截获动态代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 使用javascript实现ListBox左右全选,单选,多选,全请

    使用javascript实现ListBox左右全选,单选,多选,全请

    使用javascript实现ListBox左右全选,单选,多选,全请。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-11-11
  • 利用JS提交表单的几种方法和验证(必看篇)

    利用JS提交表单的几种方法和验证(必看篇)

    下面小编就为大家带来一篇利用JS提交表单的几种方法和验证(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • javascript 中事件冒泡和事件捕获机制的详解

    javascript 中事件冒泡和事件捕获机制的详解

    这篇文章主要介绍了javascript 中事件冒泡和事件捕获机制的详解的相关资料,网上的相关资料有很多,但是讲的不是多清楚,通过本文希望能让大家理解掌握,需要的朋友可以参考下
    2017-09-09
  • uniapp获取手机通知权限实现demo

    uniapp获取手机通知权限实现demo

    这篇文章主要为大家介绍了uniapp获取手机通知权限实现demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • webpack中CommonsChunkPlugin详细教程(小结)

    webpack中CommonsChunkPlugin详细教程(小结)

    本篇文章主要介绍了webpack中CommonsChunkPlugin详细教程(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11

最新评论