首页前端组件库Canvas 添加水印向下兼容版

Canvas 添加水印向下兼容版

分类前端组件库时间2025-04-25 12:54:15发布RustStream浏览104
摘要:watermarks脚本文件 // watermarks.ts interface WatermarkOptions { rabbitStudent_innerText?: string | Array<String>; rabbitStudent_canvasWidth?: number; rabbitStudent_canvasHeight?: number; rabbitStudent_canvasX?: number; rabbitStudent_canvasY?: number; rabbitStudent_canvasAngle?: number; rabbitStudent_canvasFont?: string; rabbitStudent_canvasFillStyle?: string; rabbitStudent_canvasTextAlign?: string; rabbitStudent_divTop?: number; rabbitStudent_divLeft?: number; rabbitStudent_zIndex?: number; } // 换行显示 function wrapTextWithComma(context, text, maxWidth = 500 { // 增强分割逻辑(处理连续逗号) const segments = text.split(/(,+ /g // 匹配连续逗号 .filter(seg =</String><!--autointro-->...

watermarks脚本文件

// watermarks.ts
interface WatermarkOptions {
    rabbitStudent_innerText?: string | Array<String>;
    rabbitStudent_canvasWidth?: number;
    rabbitStudent_canvasHeight?: number;
    rabbitStudent_canvasX?: number;
    rabbitStudent_canvasY?: number;
    rabbitStudent_canvasAngle?: number;
    rabbitStudent_canvasFont?: string;
    rabbitStudent_canvasFillStyle?: string;
    rabbitStudent_canvasTextAlign?: string;
    rabbitStudent_divTop?: number;
    rabbitStudent_divLeft?: number;
    rabbitStudent_zIndex?: number;
}

// 换行显示
function wrapTextWithComma(context, text, maxWidth = 500) {
    // 增强分割逻辑(处理连续逗号)
    const segments = text.split(/(,+)/g) // 匹配连续逗号
      .filter(seg => seg !== '')
      .reduce((arr, seg) => {
        if (/^,+$/.test(seg)) {
          // 将连续逗号视为单个换行标记
          arr.push({ type: 'comma' })
        } else {
          // 合并文本段
          if (arr.length > 0 && arr[arr.length-1].type === 'text') {
            arr[arr.length-1].value += seg
          } else {
            arr.push({ type: 'text', value: seg })
          }
        }
        return arr
      }, [])
  
    const lines = []
    let currentLine = ''
  
    // 处理每个语义段
    segments.forEach((seg, index) => {
      if (seg.type === 'comma') {
        // 强制换行并保留至少一个逗号
        lines.push(currentLine + (seg.value?.[0] || ','))
        currentLine = ''
      } else {
        // 逐字符处理保证完整性
        let tempLine = currentLine
        for (const char of seg.value) {
          const testLine = tempLine + char
          if (context.measureText(testLine).width > maxWidth) {
            lines.push(tempLine)
            tempLine = char // 新行开始
          } else {
            tempLine = testLine
          }
        }
        currentLine = tempLine
      }
    })
  
    // 处理最后剩余内容
    if (currentLine !== '') lines.push(currentLine)
    return lines.map(v => v?.replace(',', '')).filter(line => line.trim() !== '') // 过滤空行
  }
  
  // 增强绘制方法(自动处理超出画布内容)
  function drawText(context, lines, x, y, lineHeight) {
    const canvas = context.canvas
    const maxLines = Math.floor((canvas.height - y) / lineHeight)
    
    lines.slice(0, maxLines).forEach((line, index) => {
      context.fillText(line, x, y + index * lineHeight)
    })
    
    // 添加省略号提示
    if (lines.length > maxLines) {
      const lastLine = lines[maxLines-1]
      const ellipsis = '...'
      let truncated = lastLine
      
      while (context.measureText(truncated + ellipsis).width > canvas.width - x) {
        truncated = truncated.slice(0, -1)
      }
      
      context.fillText(truncated + ellipsis, x, y + (maxLines-1) * lineHeight)
    }
  }
/**
*	@description 设置水印
*	@description 使用方法  import { getmark } from '@/utils/watermarks';
*	@description const {watermark}=getmark()  watermark(传参)
* @author rabbitStudent 小菟同学
* @param {Object} obj 水印参数
* @param {String | Array} obj.rabbitStudent_innerText 参数:水印文字--必填
* @param {Number} obj.rabbitStudent_canvasWidth 参数:canvas宽度
* @param {Number} obj.rabbitStudent_canvasHeight 参数:canvas高度
* @param {Number} obj.rabbitStudent_canvasX 参数:canvasX轴
* @param {Number} obj.rabbitStudent_canvasY 参数:canvasY轴
* @param {Number} obj.rabbitStudent_canvasAngle 参数:canvas旋转角度
* @param {String} obj.rabbitStudent_canvasFont 参数:canvas字体
* @param {String} obj.rabbitStudent_canvasFillStyle 参数:canvas字体颜色
* @param {String} obj.rabbitStudent_canvasTextAlign 参数:canvas对齐方式
* @param {Number} obj.rabbitStudent_divTop 参数:div距离顶部距离
* @param {Number} obj.rabbitStudent_divLeft 参数:div距离左侧距离
* @param {Number} obj.rabbitStudent_zIndex 参数:div层级
* @date 2024-4-25 15:27
* @returns 
*/
export const getmark = () => {
    const setWatermark = (row:WatermarkOptions) => {
        const {
            rabbitStudent_innerText,
            rabbitStudent_canvasWidth,//canvas宽度
            rabbitStudent_canvasHeight,//canvas高度
            rabbitStudent_canvasX,
            rabbitStudent_canvasY,
            rabbitStudent_canvasAngle,//canvas旋转角度
            rabbitStudent_canvasFont,//canvas字体
            rabbitStudent_canvasFillStyle,
            rabbitStudent_canvasTextAlign,//canvas对齐方式
            rabbitStudent_divTop,
            rabbitStudent_divLeft,
            rabbitStudent_zIndex,
        }=row
        const id = "1.23452384164.123412416";
        if (document.getElementById(id) !== null) {
            document.body.removeChild(document.getElementById(id)!);
        }
        //创建一个画布
        const can = document.createElement("canvas");
        //设置画布的长宽
        can.width = rabbitStudent_canvasWidth;
        can.height = rabbitStudent_canvasHeight;
        
        const cans = can.getContext("2d")!;
        // const { width } = cans.measureText(rabbitStudent_innerText);
          // const canvasSize = Math.max(200, width) * devicePixelRatio;
        // console.log(canvasSize)
          // can.width = canvasSize;
          // can.height = canvasSize;
        //旋转角度
        cans.rotate((rabbitStudent_canvasAngle * Math.PI) / 180);
        cans.font = rabbitStudent_canvasFont;
        //设置填充绘画的颜色、渐变或者模式
        cans.fillStyle = rabbitStudent_canvasFillStyle;
        //设置文本内容的当前对齐方式
        cans.textAlign = rabbitStudent_canvasTextAlign;
        //设置在绘制文本时使用的当前文本基线
        // cans.textBaseline = "Middle";
        //在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
        if(rabbitStudent_innerText?.indexOf(',') != '-1'){
            // wrapText(cans, rabbitStudent_innerText)
            const wrappedLines = wrapTextWithComma(cans, rabbitStudent_innerText, 200)

            // 绘制文本
            drawText(cans, wrappedLines, 10, 100, 20) // x=10, y=100, 行高=20
        }else {
            cans.fillText(rabbitStudent_innerText, can.width / rabbitStudent_canvasX, can.height / rabbitStudent_canvasY);
        }		
        const div = document.createElement("div");
        div.id = id;
        div.style.pointerEvents = "none";
        div.style.top = rabbitStudent_divTop+'px';
        div.style.left =rabbitStudent_divLeft+"px";
        div.style.position = "fixed";
        div.style.zIndex = rabbitStudent_zIndex;
        div.style.width = document.documentElement.clientWidth + "px";
        div.style.height = document.documentElement.clientHeight + "px";
        div.style.background =
            "url(" + can.toDataURL("image/png") + ") left top repeat";
        document.body.appendChild(div);
        return id;
    };
    const watermark  = (obj: WatermarkOptions) => {
            //默认设置
        const defaultSetting:WatermarkOptions={
            rabbitStudent_innerText:'',
            rabbitStudent_canvasWidth:400,//canvas宽度
            rabbitStudent_canvasHeight:200,//canvas高度
            rabbitStudent_canvasX:8,
            rabbitStudent_canvasY:2,
            rabbitStudent_canvasAngle:-22,//canvas旋转角度
            rabbitStudent_canvasFont:'18px Vedana',//canvas字体
            rabbitStudent_canvasFillStyle:'rgba(0, 0, 0, 0.3)',
            rabbitStudent_canvasTextAlign:'middle',//canvas对齐方式
            rabbitStudent_divTop:0,
            rabbitStudent_divLeft:0,
            rabbitStudent_zIndex:1,
        }
        let mergeObj=merge({},defaultSetting,obj)
        let id = setWatermark(mergeObj);
        // setInterval(() => {
        // 	//防止用户进行删除元素
        // 	if (document.getElementById(id) === null) {
        // 		id = setWatermark(mergeObj);
        // 	}
        // 	//防止用户进行隐藏元素
        // 	if(hasDeveloperToolClasses(document.getElementById(id))){
        // 		document.body.removeChild(document.getElementById(id)!);
        // 		id=setWatermark(mergeObj)
        // 	}
        // 	//防止用户使用display:none
        // 	if(document.getElementById(id).style.display=='none'){
        // 		document.getElementById(id).style.display='block'
        // 	}
        // }, 1000);
        window.onresize = () => {
            setWatermark(mergeObj);
        };
    };
    return { watermark };
};
// 检查水印元素是否包含开发者工具添加的特殊类
function hasDeveloperToolClasses(element) {
    return element.classList.contains("__web-inspector-hide-shortcut__") ||
                 element.classList.contains("__web-inspector-hide-shortcut__") ||
                 element.classList.contains("__web-inspector-hidebefore-shortcut__") ||
                 element.classList.contains("__web-inspector-hideafter-shortcut__") ||
                 hasDeveloperToolClassesInChildren(element);
}

// 检查水印元素的子元素是否包含开发者工具添加的特殊类
function hasDeveloperToolClassesInChildren(element) {
    var children = element.querySelectorAll("*");
    for (var i = 0; i < children.length; i++) {
            if (hasDeveloperToolClasses(children[i])) {
                    return true;
            }
    }
    return false;
}


function merge(target, ...sources) {
    // 遍历所有的源对象
    for (const source of sources) {
            // 遍历源对象的所有属性
            for (const key in source) {
                    // 如果属性是对象,则递归调用 merge
                    if (typeof source[key] === 'object') {
                            // 如果目标对象不存在该属性,则创建一个空对象
                            if (!target[key]) {
                                    target[key] = {};
                            }
                            // 递归调用 merge
                            merge(target[key], source[key]);
                    } else {
                            // 否则直接赋值给目标对象的属性
                            target[key] = source[key];
                    }
            }
    }
    // 返回合并后的目标对象
    return target;
}

使用

import { getmark } from '@/utils/watermarks.ts'

// 辅助函数用于创建水印

let obj = { rabbitStudent_innerText: `禁止截图外传` };

const { idCard, username } = userInfo;
let obj = { rabbitStudent_innerText: `${username},${idCard ?? ''},${parseTime(new Date())}` };

// 如果需要换行的话需要使用下面的方式,中间用 , 分隔传参。第一种传参不支持换行

const addWatermark = (obj) => {
    const {
        watermark
    } = getmark()
    watermark(obj)
};

本文链接:https://blog.smallhao.fun/?id=26 转载需授权!

分享到:

Chen’Blog版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

JavaScript
uni-app实现仿美团商品左右菜单联动效果 uni-app 实现自定义tabBar显示

游客 回复需填写必要信息
召唤伊斯特瓦尔