前端水印svg生成方案&svg動態(tài)寬高計算

有個web項目要有水印需求,后端可以提供文本或者圖片。
參考網(wǎng)絡(luò)上的前端水印方案,目前選擇的是獲取文本,通過文本生成svg渲染水印。

1. svg生成文本水印

參考文章6. 前端水印生成方案 的svg方案是ES6語法的,項目問題,我把它改寫成ES5語法了。
js代碼如下:

     /* svg 實現(xiàn)水印 */
     function wmSvg(wmText, container) {
          if (!wmText || typeof wmText !== 'string') return;

          var svgStr = '<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="200px">  <text x="50%" y="50%" dy="12px"    text-anchor="middle"    stroke="#000000"    stroke-width="1"    stroke-opacity="0.2"    fill="none"    transform="rotate(-45, 120 120)"    style="font-size: 20px;"> '+ wmText +'</text></svg>'; 
          var base64Url = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgStr)));  
          var __wm = document.querySelector('.__wm');        
          var wmDiv = __wm || document.createElement("div");
          wmDiv.className = '_wm';
          wmDiv.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:2000;pointer-events:none;background-repeat;background-image:url('+base64Url+');');


          if(!container) container = document.body;
          container.style.position = 'relative';
          container.insertBefore(wmDiv, container.firstChild);

     }

     // 調(diào)用方法,生成水印
     wmSvg('Celine~');

渲染效果:


image.png

2. 動態(tài)計算svg的寬高

本次項目中的水印文字是由后端提供的,水印文字是由用戶自定義。再上面的代碼中,生成的svg寬高是寫死的300*200px,如果文字太長,這個寬度不夠容納就會有遮擋效果,所以自己用粗略估算的方式,動態(tài)計算了svg的寬高。

js代碼如下:

     /* svg 實現(xiàn)水印 */
     function wmSvg(wmText, container) {
          if (!wmText || typeof wmText !== 'string') return;

          var fontSize = 16,
              rotateAngle = 30,
              textLen = wmText.length, // 字符串長度(中文雙字符和英文單字符都是按照一個字符計算)
              // textWidth = Math.ceil((textLen -20)*fontSize) + 200, // 時間戳是必須項的計算方式
              textWidth = Math.ceil(textLen *fontSize), // 通用版的計算方式
              textWidth =  textWidth < 300 ? 300 : textWidth, // 設(shè)置最小長度,不然文字少水印太密集。
              svgWidth = Math.ceil(textWidth * Math.cos(Math.PI/180*rotateAngle)),
              svgHeight = Math.ceil(textWidth * Math.sin(Math.PI/180*rotateAngle)),
              rotateX = Math.floor(svgWidth/2),
              rotateY = Math.floor(svgHeight/2);

          console.log(textLen, textWidth);
          console.log(svgWidth,svgHeight);

          // 計算規(guī)則說明:
          // 本次項目的水印格式是:“用戶名 自定義內(nèi)容 時間戳” ,比如“celine 個人博客 2023-08-04 12:00:00”
          // 其中,時間戳 -> 單字符,必須要有,拿出來單獨計算下。大致占20個字符長度,渲染寬度是150px左右,此處取寬容值200px。(若沒有必須的時間戳,可以用通用計算方式)
          // 自定義內(nèi)容 -> 默認(rèn)為中文雙字符(取寬容場景)。渲染寬度和字體大小相關(guān),所以此處粗略用字符長度*字體大小。
          // svg的寬高是根據(jù)文本長度結(jié)合選擇角度,根據(jù)直角三角形的勾股定理做計算
          // rotate 的旋轉(zhuǎn)中心點偏移XY,根據(jù)svg寬高取一半。


          var svgStr = '<svg xmlns="http://www.w3.org/2000/svg" width="'+svgWidth+'px" height="'+svgHeight+'px">  <text x="50%" y="50%" dy="12px"    text-anchor="middle"    stroke="#000000"    stroke-width="1"    stroke-opacity="0.2"    fill="none"    transform="rotate(-'+rotateAngle+', '+rotateX+' ,'+rotateY+')"    style="font-size: '+fontSize+'px;"> '+ wmText +'</text></svg>'; 
          var base64Url = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgStr)));  
          var __wm = document.querySelector('._wm');        
          var wmDiv = __wm || document.createElement("div");
          wmDiv.className = '_wm';
          wmDiv.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:2000;pointer-events:none;background-repeat:repeat;background-image:url('+base64Url+');');

          if(!container) container = document.body;
          container.style.position = 'relative';
          container.insertBefore(wmDiv, container.firstChild);

     }

     // 調(diào)用方法,生成水印
     // wmSvg('Celine~');
     wmSvg('Celine 我的博客是有個性的不要隨便惹毛它哦 2023-08-04 12:00:00');

效果圖

3. 如何防止控制臺刪水印呢?

這個就可以用到new MutationObserver()這個東西了。這個方法的詳解,可以看這篇 csdn:MutationObserver詳解,
下面直接貼demo的代碼:

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<div>
<p>我是頁面內(nèi)容,哈哈哈</p>
<div style="width: 450px;height: 800px;background: pink;">我是有指定寬高的div,我是來占大位的,哈哈哈</div>

</div>

<script type="text/javascript">

     // 調(diào)用方法,生成水印
     // wmSvg('Celine~');
     wmSvg('Celine~ 2023-08-04 12:00:00');
     // wmSvg('Celine 我的博客是有個性的不要隨便惹毛它哦我的博客是有個性的不要隨便惹毛它哦我的博客是有個性的不要隨便惹毛它哦我的博客是有個性的不要隨便惹毛它哦我的博客是有個性的不要隨便惹毛它哦我的博客是有個性的不要隨便惹毛它哦 2023-08-04 12:00:00');

     /* svg 實現(xiàn)水印 */
     function wmSvg(wmText, container) {
          if (!wmText || typeof wmText !== 'string') return;

          var fontSize = 16,
              rotateAngle = 30,
              textLen = wmText.length, // 字符串長度(中文雙字符和英文單字符都是按照一個字符計算)
              // textWidth = Math.ceil((textLen -20)*fontSize) + 200, // 時間戳是必須項的計算方式
              textWidth = Math.ceil(textLen *fontSize), // 通用版的計算方式
              textWidth =  textWidth < 300 ? 300 : textWidth, // 設(shè)置最小長度,不然文字少水印太密集。
              textWidth =  textWidth > 600 ? 600 : textWidth, // 設(shè)置最大長度,不然文字多水印跑了。

              svgWidth = Math.ceil(textWidth * Math.cos(Math.PI/180*rotateAngle)),
              svgHeight = Math.ceil(textWidth * Math.sin(Math.PI/180*rotateAngle)),
              rotateX = Math.floor(svgWidth/2)-50,
              rotateY = Math.floor(svgHeight/2)+50;

          var svgStr = '<svg xmlns="http://www.w3.org/2000/svg" width="'+svgWidth+'px" height="'+svgHeight+'px">  <text x="50%" y="50%" dy="12px"    text-anchor="middle"    stroke="#000000"    stroke-width="1"    stroke-opacity="0.2"    fill="none"    transform="rotate(-'+rotateAngle+', '+rotateX+' ,'+rotateY+')"    style="font-size: '+fontSize+'px;"> '+ wmText +'</text></svg>';

          createWm(svgStr, container);          

     }

     /* 抽離生成base64和填充水印功能,單獨一個方法,便于后面的監(jiān)聽復(fù)用。*/
     function createWm(svgStr, container){
        var base64Url = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgStr)));  
        if (document.querySelector('._wm')) return; // 已有水印直接退出。
        // var wmDiv = document.querySelector('._wm') || document.createElement("div"); // 這種方式會導(dǎo)致監(jiān)聽死循環(huán),因為新水印的svgStr和頁面上不一致,會一直觸發(fā)監(jiān)聽。
        var wmDiv = document.createElement("div");
        wmDiv.className = '_wm';
        var wmStyleStr = 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:2000;pointer-events:none;background-repeat:repeat;background-image:url('+base64Url+');'

        wmDiv.setAttribute('style', wmStyleStr);


        if(!container) container = document.body;
        container.style.position = 'relative';
        container.insertBefore(wmDiv, container.firstChild);


        wmObserver(svgStr, container, wmStyleStr);

     }

  
     /* 水印監(jiān)聽 */
     function wmObserver(svgStr, container, wmStyleStr){
        const options = {
            childList: true,
            attributes: true,
            characterData: true,
            subtree: true,
            attributeOldValue: true,
            characterDataOldValue: true
        }

        // console.log(window.MutationObserver, window.WebKitMutationObserver, window.MozMutationObserver );
        var mutation = new MutationObserver(function(mutationRecoards, observer){
            console.log(mutationRecoards, observer);
            // 沒有水印時
             var wmDiv = document.querySelector('._wm');
             if (!wmDiv) {
               createWm(svgStr, container);
               return;
             }

             // 水印樣式被修改時
             if (wmDiv.getAttribute('style') != wmStyleStr) {
               wmDiv.setAttribute('style', wmStyleStr);
             }

         });

          // 安裝監(jiān)聽
          mutation.observe(container, options);

     }


</script>

</body>
</html>

4. 真實打印呢?

這是我目前遇到的困境,前面的方案頁面打印機(jī)打印除了是沒有水印的。。
誰有什么好方案再告知下。


參考文章

  1. svg文本<text>詳解
    挺詳細(xì)的一篇講解,有代碼有效果圖。其中有多行文字的svg展示代碼。我是因為要設(shè)置水印樣式查看了該文章。
  2. JavaScript中的三角函數(shù)
    簡略回顧了下三角函數(shù)。主要是要看下Js中的Math.sin()里面填入的角度要怎么設(shè)置。(舉例:30°的話,Math.sin(Math.PI/180*30)
  3. jquery.watermark.js 在網(wǎng)頁中添加水印,打印時水印背景不見了,辦法來了
    號稱可以通過print-color-adjust:exact;來改變打印時沒有水印的情況。但我自己的代碼(沒有使用jquery.watermark.js的)初步嘗試,真實打印機(jī)打印處理依舊沒有。后續(xù)再探究下。
  4. Js和Canvas實現(xiàn)水印且控制臺不可刪除
    防止控制臺刪水印那段可以好好借鑒下。
  5. 【W(wǎng)eb技術(shù)】談?wù)勊崿F(xiàn)的幾種方式
    沒認(rèn)真看,有需要再來看。
  6. 前端水印生成方案
    svg方案參考的文章,里面有canvas方案(HTMLCanvasElement.toDataURL)。
  7. 個人博客krryblog:頁面水印的實現(xiàn)以及防刪除方案
    一個畢業(yè)去京東,現(xiàn)在在騰訊的前端小大佬?還是很佩服他的強(qiáng)迫癥的,哈哈哈。這篇文章很詳細(xì)的給出的代碼方案,值得借鑒。
  8. csdn:MutationObserver詳解
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前端水印生成方案 舉個??: 視覺中國網(wǎng)站的圖片有明顯的水?。?知乎圖片: 以上是圖片上加水?。?背景水印??: 釘釘...
    新一Link閱讀 2,237評論 0 0
  • 先看下效果: 思路1: 使用canvas進(jìn)行生成圖片,然后動態(tài)生成div填充整個背景,將生成的圖片用與 backg...
    wwmin_閱讀 13,389評論 0 54
  • 導(dǎo)語:前段時間做某系統(tǒng)審核后臺,出現(xiàn)了審核人員截圖把內(nèi)容外泄露的情況,雖然截圖內(nèi)容不是特別敏感,但是安全問題還是不...
    李亞_45be閱讀 1,257評論 0 0
  • 關(guān)鍵詞:前端 明水印 暗水印 明水印和暗水印的區(qū)別 前端水印可以分為明水印和暗水印兩種類型。它們的區(qū)別如下: 明水...
    晴小篆閱讀 514評論 0 0
  • 由于三方的報表軟件做報表不錯,可要做合同模板的有些功能實現(xiàn)不了。故用前端畫html實現(xiàn)合同模板頁面,一開始合同模板...
    靈籟閱讀 2,390評論 0 0

友情鏈接更多精彩內(nèi)容