Vue3 實(shí)現(xiàn)網(wǎng)頁(yè)背景水印功能

經(jīng)常有一些公司和組織出于系統(tǒng)文件或信息安全保密的需要呐舔,需要在系統(tǒng)網(wǎng)頁(yè)上增加帶有個(gè)人標(biāo)識(shí)(系統(tǒng)賬號(hào)或個(gè)人信息)的水印漆羔,可以簡(jiǎn)單防止截圖外傳

首先我們來(lái)看這樣一個(gè)水印功能的實(shí)現(xiàn)思路奇瘦,通常是在我們?cè)械木W(wǎng)頁(yè)上附上一個(gè) DIV 層章咧,將它設(shè)置絕對(duì)定位鋪滿整個(gè)窗口闷盔,然后 z-index 值盡量往大了設(shè)弯洗,保證讓水印層處于當(dāng)前網(wǎng)頁(yè)所有元素的上面,又不影響當(dāng)前網(wǎng)頁(yè)的操作馁筐。

水印上的字體有兩種方式添加:

  • 第一種直接將字體用塊元素包裹涂召,動(dòng)態(tài)設(shè)置絕對(duì)定位,然后通過(guò) transform 屬性旋轉(zhuǎn)敏沉;
  • 第二種通過(guò)在 canvas 上繪制出字體果正,設(shè)置好樣式,然后以圖片的樣式導(dǎo)出盟迟,最后用圖片作為水印層的背景圖秋泳。

處于性能方面考慮,第二種方式最優(yōu)攒菠。我們來(lái)看具體怎么實(shí)現(xiàn)迫皱?

作為一塊獨(dú)立的功能,我們?cè)?Vue3 中常用 hooks 來(lái)實(shí)現(xiàn)辖众,通過(guò)分析我們概括出實(shí)現(xiàn)水印需要的幾個(gè)功能函數(shù)和對(duì)外接口:

對(duì)外接口

  • 清除水幼科稹(clear)
  • 設(shè)置水印(setWatermark)

核心功能函數(shù)

  • 繪制文字背景圖(createBase64)
  • 繪制水印層(createWatermark)
  • 頁(yè)面隨窗口大小調(diào)整更新(updateWatermark)
export function useWatermark(
  appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>
) {
   // 繪制文字背景圖
  function createBase64() {}

  // 繪制水印層
  const createWatermark = () => {};

  // 頁(yè)面隨窗口調(diào)整更新水印
  function updateWatermark(){}

  // 對(duì)外提供的設(shè)置水印方法
  function setWatermark() {}

  // 清除水印
  const clear = () => {};

  return { setWatermark, clear };
}

有了代碼框架凹炸,就只需要實(shí)現(xiàn)函數(shù)和接口的內(nèi)部實(shí)現(xiàn)了戏阅,另外還要考慮傳參,來(lái)實(shí)現(xiàn)代碼復(fù)用的靈活度和接口參數(shù)的可配置啤它。

我們從具體的功能函數(shù)開始:

繪制文字背景圖

這里的參數(shù) str 就是要添加的水印文字奕筐,attr 為文字樣式的屬性,我們定義了屬性的類型為 attr变骡,它包含文字的字體和大小以及顏色等值

function createBase64(str: string, attr?: attr) {
    const can = document.createElement("canvas");
    const width = 200;
    const height = 140;
    Object.assign(can, { width, height });

    const cans = can.getContext("2d");
    if (cans) {
      cans.rotate((-20 * Math.PI) / 120);
      cans.font = attr?.font ?? "12px Reggae One";
      cans.fillStyle = attr?.fillStyle ?? "rgba(0, 0, 0, 0.12)";
      cans.textAlign = "left";
      cans.textBaseline = "middle";
      cans.fillText(str, width / 20, height);
    }
    return can.toDataURL("image/png");
  }
type attr = {
  font?: string;
  fillStyle?: string;
};

繪制水印層

這個(gè)函數(shù)的主要邏輯是先判斷如果已經(jīng)繪制了水印層离赫,直接調(diào)用更新水印方法,如果還沒(méi)有塌碌,先動(dòng)態(tài)創(chuàng)建一個(gè) DIV 層渊胸,設(shè)置絕對(duì)定位,鋪滿當(dāng)前整個(gè)瀏覽器窗口台妆。

const id = domSymbol.toString();
const watermarkEl = shallowRef<HTMLElement>();

const createWatermark = (str: string, attr?: attr) => {
    if (unref(watermarkEl)) {
      updateWatermark({ str, attr });
      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, attr });
    el.appendChild(div);
    return id;
  };

更新水印

因?yàn)楦滤》椒ㄖ饕歉鶕?jù)當(dāng)前窗口高度和寬度來(lái)的更新水印背景的設(shè)置蹬刷,利用一張 Base64 格式的圖片平鋪即可瓢捉。

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

到此,我們實(shí)現(xiàn)了主要的三個(gè)功能函數(shù)办成,下面就是兩個(gè)對(duì)外接口:

設(shè)置水印

這里的主要點(diǎn)是考慮設(shè)置頁(yè)面resize監(jiān)聽泡态,來(lái)及時(shí)更新水印的位置。還要考慮 Vue 的生命周期迂卢,當(dāng)我們卸載頁(yè)面的時(shí)候要進(jìn)行清除水印某弦。

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

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

清除水印

清除水印的時(shí)候順便移除窗口大小監(jiān)聽函數(shù)

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

水印功能 hooks 的使用

import { useWatermark } from "/@/hooks/watermark";
const { setWatermark, clear } = useWatermark();

onMounted(() => {
  nextTick(() => {
    setWatermark(watermarkText.value);
  });
});

onBeforeUnmount(() => {
  clear();
});

至此,Vue3 版的網(wǎng)頁(yè)水印功能實(shí)現(xiàn)全部完成而克。這里水印的字體大小靶壮、顏色和排布參考了企業(yè)微信的背景水印,使得看起來(lái)不那么突兀员萍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腾降,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碎绎,更是在濱河造成了極大的恐慌螃壤,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筋帖,死亡現(xiàn)場(chǎng)離奇詭異奸晴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)日麸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門寄啼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人代箭,你說(shuō)我怎么就攤上這事墩划。” “怎么了嗡综?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵乙帮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛤高,道長(zhǎng),這世上最難降的妖魔是什么碑幅? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任戴陡,我火速辦了婚禮,結(jié)果婚禮上沟涨,老公的妹妹穿的比我還像新娘恤批。我一直安慰自己,他們只是感情好裹赴,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布喜庞。 她就那樣靜靜地躺著诀浪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪延都。 梳的紋絲不亂的頭發(fā)上雷猪,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音晰房,去河邊找鬼求摇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛殊者,可吹牛的內(nèi)容都是我干的与境。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼猖吴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摔刁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起海蔽,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤共屈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后准潭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趁俊,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年刑然,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寺擂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泼掠,死狀恐怖怔软,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情择镇,我是刑警寧澤挡逼,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站腻豌,受9級(jí)特大地震影響家坎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吝梅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一虱疏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苏携,春花似錦做瞪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)著拭。三九已至,卻和暖如春牍帚,著一層夾襖步出監(jiān)牢的瞬間儡遮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工履羞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峦萎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓忆首,卻偏偏與公主長(zhǎng)得像爱榔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糙及,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容