Vue3 实现背景水印功能

2023-11-06

在 web 的世界里,对于图片文档等增加水印处理是十分有必要的。水印的添加根据环境可以分为两大类,前端浏览器环境添加和后端服务环境添加。 通过 canvas 创建一张含有水印信息的背景图片,通过 hooks 函数插入到页面中。

对外暴露方法

  • 设置水印 setWatermark
  • 清除水印 clear

核心功能

  • 创建水印 createWatermark
  • 更新水印 updateWatermark
  • 根据文字创建 canvas 背景图 createBase64
export function useWatermark(
  appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>,
) {
  // 清除水印
  const clear = () => {};
  // 创建 canvas 背景图
  function createBase64(str: string) {}

  // 更新水印
  function updateWatermark() {}
  // 创建水印
  const createWatermark = (str: string) => {};
  // 设置水印
  function setWatermark(str: string) {}

  return { setWatermark, clear };
}

创建 canvas 背景图

function createBase64 (str: string) {
  const can = document.createElement('canvas')
  const width = 300
  const height = 240
  Object.assign(can, { width, height })

  const cans = can.getContext('2d')
  if (cans) {
    cans.rotate((-20 * Math.PI) / 120);
    cans.font = '15px Vedana';
    cans.fillStyle = 'rgba(0, 0, 0, 0.15)';
    cans.textAlign = 'left';
    cans.textBaseline = 'middle';
    cans.fillText(str, width / 20, height);
  }
  return can.toDataURL('image/png')
}

创建水印

动态创建一个 div 标签,设置绝对定位,整个浏览器窗口铺满,若已存在水印层,则直接调用 updateWatermark 方法更新水印。

const id = domSymbol.toString()
const watermarkEl = shallowRef<HTMLElement>()
  
const createWatermark = (str: string) => {
  if (unref(watermarkEl)) {
    updateWatermark({ str })
    return id
  }

  const div = document.createElement('div')
  watermarkEl.value = div
  div.id = id
  div.style.pointerEvents = 'none'
  div.style.top = '0px';
  div.style.left = '0px';
  div.style.position = 'absolute';
  div.style.zIndex = '100000';

  const el = unref(appendEl)
  if (!el) return id

  const { clientHeight: height, clientWidth: width } = el
  updateWatermark({ str, width, height })
  el.appendChild(div)
  return id
}

更新水印

使用 createBase64 方法创建 Base64 格式的图片来铺满整个窗口。

function updateWatermark(
  options: {
    width?: number;
    height?: number;
    str?: string;
  } = {},
) {
  const el = unref(watermarkEl);
  if (!el) return;
  if (isDef(options.width)) {
    el.style.width = `${options.width}px`;
  }
  if (isDef(options.height)) {
    el.style.height = `${options.height}px`;
  }
  if (isDef(options.str)) {
    el.style.background = `url(${createBase64(options.str)}) left top repeat`;
  }
}

设置水印

在设置水印时,不仅需要创建水印,还需要设置 resize 监听来更新水印的位置,以及 Vue 生命周期中,卸载页面时清除水印等操作。

function setWatermark(str: string) {
  createWatermark(str);
  addResizeListener(document.documentElement, func);
  const instance = getCurrentInstance();
  if (instance) {
    onBeforeUnmount(() => {
      clear();
    });
  }
}

const func = useRafThrottle(function () {
  const el = unref(appendEl);
  if (!el) return;
  const { clientHeight: height, clientWidth: width } = el;
  updateWatermark({ height, width });
});

清除水印

清除水印时,需要移出窗口的监听函数。

const clear = () => {
  const domId = unref(watermarkEl);
  watermarkEl.value = undefined;
  const el = unref(appendEl);
  if (!el) return;
  domId && el.removeChild(domId);
  removeResizeListener(el, func);
};

组件使用

import { defineComponent, ref } from 'vue'
import { useWatermark } from '/@/hooks/web/useWatermark'

export default defineComponent({
  setup () {
    const { setWatermark, clear } = useWatermark();
    return {
      setWatermark,
      clear,
    }
  }
})

利用 ResizeObserver Polyfill 库实现DOM调整大小监听和移除监听

window.resize 事件能帮我们监听窗口大小的变化。但是 resize 事件会在一秒内触发将近60次,所以很容易在改变窗口大小时导致性能问题。换句话说,window.resize 事件通常是浪费的,因为它会监听每个元素的大小变化(只有window对象才有resize事件),而不是具体到某个元素的变化。

ResizeObserver API 使用了观察者模式,即常说的发布-订阅模式, 来监听一个DOM节点的变化,这种变化包括但不仅限于:某个节点的出现和隐藏、某个节点的大小变化

  1. 安装依赖
npm install resize-observer-polyfill --save-dev
  1. 封装方法
// src/utils/event/index.ts

import ResizeObserver from 'resize-observer-polyfill';

const isServer = typeof window === 'undefined';

/* istanbul ignore next */
function resizeHandler(entries: any[]) {
  for (const entry of entries) {
    // 通过 entry.target 访问监听的 DOM 对象 上的 __resizeListeners__ 属性, 遍历存储的监听回调
    const listeners = entry.target.__resizeListeners__ || [];
    if (listeners.length) {
      listeners.forEach((fn: () => any) => {
        fn();
      });
    }
  }
}

/* istanbul ignore next */
// 接受DOM元素和方法
export function addResizeListener(element: any, fn: () => any) {
  if (isServer) return;
  if (!element.__resizeListeners__) {
    // 在 DOM 元素对象上,设置 __resizeListeners__ 属性,存储监听器(回调函数)。
    element.__resizeListeners__ = [];
    // 监听DOM元素的变化
    element.__ro__ = new ResizeObserver(resizeHandler);
    // 监听指定的 Element 或 SVGElement
    element.__ro__.observe(element);
  }
  element.__resizeListeners__.push(fn);
}

/* istanbul ignore next */
export function removeResizeListener(element: any, fn: () => any) {
  if (!element || !element.__resizeListeners__) return;
  element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
  if (!element.__resizeListeners__.length) {
    // 取消 element.__ro__ 观察者身上所有的元素的观察
    element.__ro__.disconnect();
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue3 实现背景水印功能 的相关文章

随机推荐

  • uniapp微信小程序订阅消息发送服务通知

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net qq 44718932 article details 126130702 uniap
  • 爬虫发送带headers报错内容为ValueError: too many values to unpack (expected 2)

    这个是我报错的代码 author li lee import requests 变量url接收访问地址 url http www baidu com 加headers 将浏览器的用户代理 写到headers中 用于重置User Agent
  • 《C++ Primer Plus 第六版》编程练习参考答案(第四章)

    第四章 第一题 include
  • NativeWindow 妙用,截取windows消息

    namespace System Windows Forms using System using System ComponentModel using System Drawing using System Drawing Design
  • linux设置VT模式,android – 如何在Linux上的BIOS和KVM模块中启用VT-x?

    KVM Installation I referred the instructions from Ubuntu community documentation page to get KVM installed To see if you
  • C#使用FFmpeg的总结

    上篇文章提到FFmpeg解决项目中视频和语音问题 说道C 和FFmpeg不得不提的2个类库 1 Xabe FFmpeg 简单查看了下源码和demo 发现基于ffmpeg exe的命令行参数进行处理 2 FFmpeg AutoGen 把C语言
  • sqlserver日期格式转换yyyymmdd_8个案例,玩转时间合并转换运算和提取,3个函数就够了...

    大家好 上一篇文章分享了关于日期时间的提取函数和判定 讲解了如何对日期是否是周末进行判定 今天继续分享关于日期时间方面的合并 提取方面的剩余内容 以及如何根据身份证号求算年龄 如何根据入职时间计算员工工龄两个案例 那么就让我们开始吧 操作一
  • jmeter errstr :“unsupported field type for multipart.FileHeader“

    在使用jmeter测试接口的时候 提示errstr unsupported field type for multipart FileHeader 如图所示 这是因为我们 在HTTP信息头管理加content type参数有问题 直接在HT
  • VS社区版许可证过期更新

    VS社区版许可证过期更新 VS社区版是免费使用的 然而会有许可证过期的问题 这时候就需要登录 再进行更新许可证 在登录时卡顿了一天还没有解决问题 我们无法下载许可证 请检查你的网络连接或代理设置 查找方案 有一个方案比较普遍 1 在VS安装
  • brk(), sbrk() 用法详解

    贴上原文地址 好不容易找到了 brk sbrk 改变数据段长度 brk sbrk 的声明如下 include
  • 机器学习实验 - MeanShift聚类

    目录 一 报告摘要 1 1 实验要求 1 2 实验思路 1 3 实验结论 二 实验内容 2 1 方法介绍 2 2 实验细节 2 2 1 实验环境 2 2 2 实验过程 2 2 3 实验与理论内容的不同点 2 3 实验数据介绍 2 4 评价指
  • Spring5 框架 详解 (一) ---- IOC控制反转

    Spring5 框架 spring 框架概述 IOC 容器 1 什么是 IOC 2 IOC底层原理 IOC 过程 IOC 接口 IOC 操作 Bean 管理 IOC 操作 Bean 管理 xml 注入其他类型属性 IOC 操作 Bean 管
  • PHP底层工作原理

    原文地址 http www cnblogs com phphuaibei archive 2011 09 13 2174927 html 最近搭建服务器 突然感觉lamp之间到底是怎么工作的 或者是怎么联系起来 平时只是写程序 重来没有思考
  • Java中数据类型详解

    文章目录 一 数据类型的作用 二 两种数据类型 1 基本数据类型 1 第一类 整数型 2 第二类 浮点型 3 第三类 布尔型 4 第四类 字符型 2 引用数据类型 1 类 2 数组 3 接口 三 基本数据类型之间的转化 一 数据类型的作用
  • Vue 项目如何实现一个全局菜单搜索框

    个人主页 山山而川 xyj 作者简介 前端领域新星创作者 专注于前端各领域技术 共同学习共同进步 一起加油 系列专栏 Vue 系列 学习格言 与其临渊羡鱼 不如退而结网 目录 前言 一 过滤路由 二 搜索框展示路由 三 雏形出现但有缺陷 四
  • 九宫格选择照片

    一 Adapter public class RecyclerImageAdapter extends RecyclerView Adapter
  • 使用IDEA创建JavaWeb项目

    由于看的视频教程比较老 且开发工具为Eclipse 本人已习惯使用IDEA 在此记录一下创建JavaWeb项目过程 创建一个项目 左侧选择 Java Enterprise 右侧选择 Web Application 这里我输入的项目名字为 F
  • 公开课精华

    本文章总结于大疆前技术总监 目前在卡内基梅隆大学读博的杨硕博士在深蓝学院的关于机器人的带约束轨迹规划的公开课演讲内容 全文约5000字 笔者不是机器人领域的 因此特地去了解了一下杨硕博士 深感佩服 不仅是他的履历 更多的是他关于学术上的至臻
  • 2021-09-15 C++ 继承和多态(虚函数,纯虚函数,虚继承)

    C 继承和多态 虚函数 纯虚函数 虚继承 一 继承 继承的概念 为了代码的复用 保留基类的原始结构 并添加派生类的新成员 继承的本质 代码复用 我们用下图解释下 那么我们这里就可以提出几个问题了 进程的方式有哪些呢 这里有三种继承方式 pu
  • Vue3 实现背景水印功能

    在 web 的世界里 对于图片文档等增加水印处理是十分有必要的 水印的添加根据环境可以分为两大类 前端浏览器环境添加和后端服务环境添加 通过 canvas 创建一张含有水印信息的背景图片 通过 hooks 函数插入到页面中 对外暴露方法 设