Heim  >  Artikel  >  Web-Frontend  >  So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

WBOY
WBOYnach vorne
2023-05-12 23:07:141950Durchsuche

Ich glaube, wir kennen alle das Geschäft mit Seitenwasserzeichen. Warum müssen wir der Seite ein Wasserzeichen hinzufügen? Um Ihre eigenen Urheber- und geistigen Eigentumsrechte zu schützen, dient das Hinzufügen von Wasserzeichen zu Bildern im Allgemeinen dazu, Piraten daran zu hindern, sie für kommerzielle Zwecke zu nutzen und die Rechte des ursprünglichen Autors zu verletzen. Welche Methoden können also in unserer Entwicklung erreicht werden? Im Allgemeinen in zwei Methoden unterteilt: Front-End-Implementierung und Back-End-Implementierung. Dieser Artikel konzentriert sich hauptsächlich auf das Erlernen der Front-End-Implementierungsmethode:

  • Methode 1: Umschließen Sie die Schriftart direkt mit Blockelementen und legen Sie die absolute Positionierung dynamisch fest. und dann durch das Transformationsattribut drehen. Es gibt jedoch ein Problem, das berücksichtigt werden muss, wenn das Bild zu groß ist oder zu viele Bilder vorhanden sind. Daher werde ich nicht näher auf diese Methode eingehen.

  • Methode 2: Zeichnen Sie die Schriftart auf die Leinwand, legen Sie den Stil fest, exportieren Sie sie schließlich als Bild und verwenden Sie das Bild als Hintergrundbild der Wasserzeichenebene.

Bevor ich die Wasserzeichenebene lerne, stelle ich zunächst zwei Fragen:

  • Wenn der Wasserzeichentext lang ist, kann das Wasserzeichen anpassungsfähig sein?

  • Kann Benutzern das Ändern und Löschen von Wasserzeichen untersagt werden?

Tatsächlich sind die beiden oben genannten Probleme die beiden Kernprobleme, die wir bei der Erstellung von Seitenwasserzeichen berücksichtigen müssen. Lassen Sie uns ohne weitere Umschweife gemeinsam mit den Fragen nachgehen????

Zuerst müssen wir zwei Punkte klären: Benennung (V-Wassermaske) und Bindungswert (Konfigurationswert, Option). Die Implementierung ist wie folgt:

<div v-water-mask:options="wmOption"></div>
// 配置值
const wmOption = reactive<WMOptions>({
  textArr: [&#39;路灯下的光&#39;, `${dayjs().format(&#39;YYYY-MM-DD HH:mm&#39;)}`],
  deg: -35,
});

Der Effekt ist wie folgt:

So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

Aus dem Bild oben können wir ersehen, dass der Text Text- und Zeitzeichenfolgen enthält und der Wasserzeichentext in einem bestimmten Winkel geneigt ist, der tatsächlich in einem bestimmten Winkel gedreht wird. Es stellt sich also die Frage: Wie sind diese eingerichtet? Dies erfordert zunächst einige Konfigurationen, um bei der Verwendung von Anweisungen einige feste Werte zu erreichen. Im Folgenden werden diese Konfigurationen in einer Klasse gekapselt. Auf diese Weise müssen Sie nicht jedes Mal einen Standardwert festlegen, wenn Sie diese Konfigurationen durch Definieren einer Schnittstelle referenzieren, Sie müssen jedes Mal einen Standardwert festlegen:

export class WMOptions {
  constructor(init?: WMOptions) {
    if (init) {
      Object.assign(this, init);
    }
  }
  textArr: Array<string> = [&#39;test&#39;, &#39;自定义水印&#39;]; // 需要展示的文字,多行就多个元素【必填】
  font?: string = &#39;16px "微软雅黑"&#39;; // 字体样式
  fillStyle?: string = &#39;rgba(170,170,170,0.4)&#39;; // 描边样式
  maxWidth?: number = 200; // 文字水平时最大宽度
  minWidth?: number = 120; // 文字水平时最小宽度
  lineHeight?: number = 24; // 文字行高
  deg?: number = -45; // 旋转的角度 0至-90之间
  marginRight?: number = 120; // 每个水印的右间隔
  marginBottom?: number = 40; // 每个水印的下间隔
  left?: number = 20; // 整体背景距左边的距离
  top?: number = 20; // 整体背景距上边的距离
  opacity?: string = &#39;.75&#39;; // 文字透明度
  position?: &#39;fixed&#39; | &#39;absolute&#39; = &#39;fixed&#39;; // 容器定位方式(值为absolute时,需要指定一个父元素非static定位)
}

Wenn Sie vorsichtig sind , stellen wir möglicherweise fest, dass der angezeigte Text ein Array ist. Dies dient hauptsächlich der Vereinfachung der Verzweigung. Wir fragen uns möglicherweise: Wie kann die Zeile umbrochen werden, wenn eine davon länger ist? Machen Sie sich keine Sorgen, machen Sie sich keine Sorgen, lassen Sie uns zunächst verstehen, wie die Anweisung definiert ist:

Definieren Sie die Anweisung: Definieren Sie sie zunächst als ObjectDirective-Objekttyp, da die Anweisung darin besteht, einige Operationen für das aktuelle Element in einem anderen Leben auszuführen Zyklen.

const WaterMask: ObjectDirective = {
  // el为当前元素
  // bind是当前绑定的属性,注意地,由于是vue3实现,这个值是一个ref类型
    beforeMount(el: HTMLElement, binding: DirectiveBinding) {
        // 实现水印的核心方法
        waterMask(el, binding);
    },
    mounted(el: HTMLElement, binding: DirectiveBinding) {
        nextTick(() => {
          // 禁止修改水印
          disablePatchWaterMask(el);
        });
    },
    beforeUnmount() {
        // 清除监听DOM节点的监听器
        if (observerTemp.value) {
          observerTemp.value.disconnect();
          observerTemp.value = null;
        }
    },
};
export default WaterMask;
  • waterMask-Methode: Realisiert die Darstellung von Wasserzeichen-Geschäftsdetails, den adaptiven Zeilenumbruch von Text und berechnet geeignete Breiten- und Höhenwerte basierend auf der Größe der Seitenelemente.

  • DisablePatchWaterMask-Methode: Überwachen Sie DOM-Elementänderungen mit der MutationObserver-Methode, um zu verhindern, dass der Benutzer die Präsentation des Wasserzeichens abbricht.

Deklarationsanweisung: Definieren Sie die Deklarationsanweisung in der Hauptdatei, damit wir diese Anweisung global verwenden können

app.directive(&#39;water-mask&#39;, WaterMask);

Als Nächstes werfen wir einen Blick auf die beiden Kernmethoden der Wasserzeichenmarkierung: WaterMask und DisablePatchWaterMask.

Implementieren Sie die Wasserzeichenfunktion

über die WaterMask-Methode. Die WaterMask-Methode führt hauptsächlich vier Dinge aus:

let defaultSettings = new WMOptions();
const waterMask = function (element: HTMLElement, binding: DirectiveBinding) {
  // 合并默认值和传参配置
  defaultSettings = Object.assign({}, defaultSettings, binding.value || {});
  defaultSettings.minWidth = Math.min(
    defaultSettings.maxWidth!,
    defaultSettings.minWidth!
  ); // 重置最小宽度
  const textArr = defaultSettings.textArr;
  if (!Util.isArray(textArr)) {
    throw Error(&#39;水印文本必须放在数组中!&#39;);
  }
  const c = createCanvas(); // 动态创建隐藏的canvas
  draw(c, defaultSettings); // 绘制文本
  convertCanvasToImage(c, element); // 转化图像
};

Erhalten Sie den Standardwert der Konfiguration: Da Entwickler bei der Übergabe von Parametern nicht unbedingt alle Konfigurationen übergeben müssen, Gemäß Nur einige Standardwerte reichen aus. Durch flaches Kopieren werden die an die Anweisungen gebundenen Werte übergeben und zusammengeführt, um die Standardkonfiguration zu aktualisieren:

Canvas-Tag erstellen: Da es über Canvas implementiert wird, tun wir dies Um diese Beschriftung nicht direkt hinzuzufügen, müssen Sie über das Dokumentobjekt eine Leinwandbeschriftung erstellen:

function createCanvas() {
  const c = document.createElement(&#39;canvas&#39;);
  c.style.display = &#39;none&#39;;
  document.body.appendChild(c);
  return c;
}

Zeichnungstext: Durchlaufen Sie zunächst die anzuzeigenden Wasserzeicheninformationen, bei denen es sich um das Textarray textArr handelt Bestimmen Sie mithilfe des Arrays, ob die Array-Elemente die für jedes Wasserzeichen konfigurierte Standardbreite und -höhe überschreiten, und geben Sie dann das Textsegmentierungsarray zurück, das die Textlänge gemäß dem Textelement überschreitet, und geben Sie gleichzeitig die maximale Breite des Texts zurück Ändern Sie schließlich die Breite und Höhe der Leinwand dynamisch durch die Schnittergebnisse.

function draw(c: any, settings: WMOptions) {
  const ctx = c.getContext(&#39;2d&#39;);
  // 切割超过最大宽度的文本并获取最大宽度
  const textArr = settings.textArr || []; // 水印文本数组
  let wordBreakTextArr: Array<any> = [];
  const maxWidthArr: Array<number> = [];
  // 遍历水印文本数组,判断每个元素的长度
  textArr.forEach((text) => {
    const result = breakLinesForCanvas(ctx,text + &#39;&#39;,settings.maxWidth!,settings.font!);
    // 合并超出最大宽度的分割数组
    wordBreakTextArr = wordBreakTextArr.concat(result.textArr);
    // 最大宽度
    maxWidthArr.push(result.maxWidth);
  });
  // 最大宽度排序,最后取最大的最大宽度maxWidthArr[0]
  maxWidthArr.sort((a, b) => {
    return b - a;
  });
  // 根据需要切割结果,动态改变canvas的宽和高
  const maxWidth = Math.max(maxWidthArr[0], defaultSettings.minWidth!);
  const lineHeight = settings.lineHeight!;
  const height = wordBreakTextArr.length * lineHeight;
  const degToPI = (Math.PI * settings.deg!) / 180;
  const absDeg = Math.abs(degToPI);
  // 根据旋转后的矩形计算最小画布的宽高
  const hSinDeg = height * Math.sin(absDeg);
  const hCosDeg = height * Math.cos(absDeg);
  const wSinDeg = maxWidth * Math.sin(absDeg);
  const wCosDeg = maxWidth * Math.cos(absDeg);
  c.width = parseInt(hSinDeg + wCosDeg + settings.marginRight! + &#39;&#39;, 10);
  c.height = parseInt(wSinDeg + hCosDeg + settings.marginBottom! + &#39;&#39;, 10);
  // 宽高重置后,样式也需重置
  ctx.font = settings.font;
  ctx.fillStyle = settings.fillStyle;
  ctx.textBaseline = &#39;hanging&#39;; // 默认是alphabetic,需改基准线为贴着线的方式
  // 移动并旋转画布
  ctx.translate(0, wSinDeg);
  ctx.rotate(degToPI);
  // 绘制文本
  wordBreakTextArr.forEach((text, index) => {
    ctx.fillText(text, 0, lineHeight * index);
  });
}

Aus dem obigen Code können wir ersehen, dass der Kernvorgang beim Zeichnen von Text darin besteht, extrem langen Text auszuschneiden und die Breite und Höhe der Leinwand dynamisch zu ändern. Schauen wir uns an, wie diese beiden Vorgänge implementiert werden. Die Methode

measureText() berechnet die Zeichenfolgenbreite basierend auf der aktuellen Schriftart.

// 根据最大宽度切割文字
function breakLinesForCanvas(context: any,text: string,width: number,font: string) {
  const result = [];
  let maxWidth = 0;
  if (font) {
    context.font = font;
  }
  // 查找切割点
  let breakPoint = findBreakPoint(text, width, context);
  while (breakPoint !== -1) {
    // 切割点前的元素入栈
    result.push(text.substring(0, breakPoint));
    // 切割点后的元素
    text = text.substring(breakPoint);
    maxWidth = width;
    // 查找切割点后的元素是否还有切割点
    breakPoint = findBreakPoint(text, width, context);
  }
  // 如果切割的最后文本还有文本就push
  if (text) {
    result.push(text);
    const lastTextWidth = context.measureText(text).width;
    maxWidth = maxWidth !== 0 ? maxWidth : lastTextWidth;
  }
  return {
    textArr: result,
    maxWidth: maxWidth,
  };
}
// 寻找切换断点
function findBreakPoint(text: string, width: number, context: any) {
  let min = 0;
  let max = text.length - 1;
  while (min <= max) {
    // 二分字符串中点
    const middle = Math.floor((min + max) / 2);
    // measureText()方法是基于当前字型来计算字符串宽度的
    const middleWidth = context.measureText(text.substring(0, middle)).width;
    const oneCharWiderThanMiddleWidth = context.measureText(
      text.substring(0, middle + 1)
    ).width;
    // 判断当前文本切割是否超了的临界点
    if (middleWidth <= width && oneCharWiderThanMiddleWidth > width) {
      return middle;
    }
    // 如果没超继续遍历查找
    if (middleWidth < width) {
      min = middle + 1;
    } else {
      max = middle - 1;
    }
  }
  return -1;
}

So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

Die Breite der Leinwandgrafik beträgt also hSinDeg + wCosDeg + Settings.marginRight. Die Höhe der Leinwandgrafik beträgt: wSinDeg + hCosDeg + Settings.marginBottom.

  • Super langen Text ausschneiden:

  • Schnittpunkt finden: Position der superlangen Zeichenfolge mit der binären Suchmethode abfragen:

  • Dynamische Änderung der Breite und Höhe der Leinwand: durch den Drehwinkel Wert, maximaler Breitenwert und der Satz des Pythagoras, um die Breite und Höhe einzeln zu berechnen. Zuerst müssen wir den Drehwinkel in einen Bogenmaßwert umwandeln (Formel: π/180× Winkel, d. h. (Math.PI *settings.deg!) / 180), schauen wir uns zunächst das Bild unten an:

转化图像:通过对当前canvas配置转化为图形url,然后配置元素的style属性。

// 将绘制好的canvas转成图片
function convertCanvasToImage(canvas: any, el: HTMLElement) {
  // 判断是否为空渲染器
  if (Util.isUndefinedOrNull(el)) {
    console.error(&#39;请绑定渲染容器&#39;);
  } else {
    // 转化为图形数据的url
    const imgData = canvas.toDataURL(&#39;image/png&#39;);
    const divMask = el;
    divMask.style.cssText = `position: ${defaultSettings.position}; left:0; top:0; right:0; bottom:0; z-index:9999; pointer-events:none;opacity:${defaultSettings.opacity}`;
    divMask.style.backgroundImage = &#39;url(&#39; + imgData + &#39;)&#39;;
    divMask.style.backgroundPosition =
      defaultSettings.left + &#39;px &#39; + defaultSettings.top + &#39;px&#39;;
  }
}

实现禁止用户修改水印

我们都知道,如果用户需要修改html一般都会浏览器调式中的Elements中修改我们网页的元素的样式就可以,也就是我们只要监听到DOM元素被修改就可以,控制修改DOM无法生效。

由于修改DOM有两种方法:修改元素节点和修改元素属性,所以只要控制元素的相关DOM方法中进行相应操作就可以实现我们的禁止。而通过disablePatchWaterMask方法主要做了三件事情:

  • 创建MutationObserver实例:也就是实例化MutationObserver,这样才能调用MutationObserver中的observe函数实现DOM修改的监听。

  • 创建MutationObserver回调函数:通过传入的两个参数,一个当前元素集合和observer监听器。

  • 监听需要监听的元素:调用observer需要传入监听元素以及监听配置,这个可以参考一下MutationObserver用法配置。

function disablePatchWaterMask(el: HTMLElement) {
  // 观察器的配置(需要观察什么变动)
  const config = {
    attributes: true,
    childList: true,
    subtree: true,
    attributeOldValue: true,
  };
  /* MutationObserver 是一个可以监听DOM结构变化的接口。 */
  const MutationObserver =
    window.MutationObserver || window.WebKitMutationObserver;
  // 当观察到变动时执行的回调函数
  const callback = function (mutationsList: any, observer: any) {
    console.log(mutationsList);
    for (let mutation of mutationsList) {
      let type = mutation.type;
      switch (type) {
        case &#39;childList&#39;:
          if (mutation.removedNodes.length > 0) {
            // 删除节点,直接从删除的节点数组中添加回来
            mutation.target.append(mutation.removedNodes[0]);
          }
          break;
        case &#39;attributes&#39;:
          // 为什么是这样处理,我们看一下下面两幅图
          mutation.target.setAttribute(&#39;style&#39;, mutation.target.oldValue);
          break;
        default:
          break;
      }
    }
  };
  // 创建一个观察器实例并传入回调函数
  const observer = new MutationObserver(callback);
  // 以上述配置开始观察目标节点
  observer.observe(el, config);
  observerTemp.value = observer;
}

So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

从水印到取消水印(勾选到不勾选background-image):我们发现mutation.target属性中的oldValue值就是我们设置style。

So verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds

从取消水印到恢复水印(不勾选到勾选background-image):我们发现mutation.target属性中的oldValue值的background-image被注释掉了。

从上面两个转化中,我们就可以直接得出直接赋值当勾选到不勾选是监听到DOM修改的oldValue(真正的style),因为这时候获取到的才是真正style,反之就不是了,由于我们不勾选时的oldValue赋值给不勾选时的style,所以当我们不勾选时再转化为勾选时就是真正style,从而实现不管用户怎么操作都不能取消水印。

Das obige ist der detaillierte Inhalt vonSo verwenden Sie Vue3-Anweisungen zum Implementieren eines Wasserzeichenhintergrunds. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen