目次
ウォーターマーク関数の実装
实现禁止用户修改水印
ホームページ ウェブフロントエンド Vue.js Vue3 命令を使用して透かしの背景を実装する方法

Vue3 命令を使用して透かしの背景を実装する方法

May 12, 2023 pm 11:07 PM
vue3

誰もがページの透かしビジネスに遭遇したことがあると思いますが、なぜページに透かしを追加する必要があるのでしょうか?自分の著作権と知的財産権を保護するために、画像に透かしを追加するのは一般に、海賊版が画像を商業目的で使用したり、元の作者の権利を侵害したりするのを防ぐためです。では、私たちの開発ではどのような方法が実現できるのでしょうか?フロントエンド実装とバックエンド実装の 2 つの方法に大別されますが、この記事では主にフロントエンド実装方法の学習に焦点を当てます:

  • 方法 1: フォントをブロックで直接囲む要素を作成し、絶対位置を動的に設定してから、transform 属性を使用して回転させます。ただし、考慮する必要がある問題があり、画像が大きすぎる場合や画像の数が多すぎる場合は、パフォーマンスに重大な影響を与えるため、この方法については詳しく説明しません。

  • 方法 2: キャンバス上にフォントを描画し、スタイルを設定し、最後にそれを画像としてエクスポートし、その画像を透かしレイヤーの背景画像として使用します。

ウォーターマーク レイヤーについて学ぶ前に、まず 2 つの質問をします。

  • ウォーターマーク テキストが長い場合、ウォーターマークは適応可能ですか?

  • ユーザーによるウォーターマークの変更と削除を制限できますか?

実際、上記の 2 つの質問は、ページの透かしを作成するときに考慮する必要がある 2 つの中心的な問題です。それでは、早速、質問を一緒に見ていきましょう??? 。

最初にコマンドを定義します。名前付け (v-water-mask) とバインド値 (設定値、オプション) の 2 点を明確にする必要があります。実装は次のとおりです:

<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,
});
ログイン後にコピー

The効果は次の図に示すとおりです。 表示:

Vue3 命令を使用して透かしの背景を実装する方法

上の図から、テキストにはテキストと時間の文字列が含まれており、透かしテキストは一定の角度で傾いていることがわかります。角度、実際には特定の角度で回転します。そこで疑問が生じます。これらはどのように設定されているのでしょうか?まず第一に、これには命令を使用するときにいくつかの固定値を達成するためにいくつかの設定が必要です。以下では、これらの設定はクラスにカプセル化されています。なぜこれを行うのでしょうか?この方法では、使用するたびにデフォルト値を設定する必要はありません。たとえば、インターフェイスを定義してこれらの構成を参照する場合、毎回デフォルト値を設定する必要があります:

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定位)
}
ログイン後にコピー

注意してみると、表示位置が配列になっていることがわかりますが、これは主に改行の便宜を図るためのもので、どちらか一方が長い場合はどのように改行すればよいのでしょうか?心配しないでください。まず、命令がどのように定義されているかを理解しましょう。

命令を定義します。命令は現在のオブジェクトに対していくつかの操作を実行するため、まず ObjectDirective オブジェクト タイプとして定義します。さまざまなライフサイクルの要素。

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 メソッド: ウォーターマークのビジネス詳細の表示、テキストの適応的な行折り返しを実現し、ページ要素のサイズに基づいて適切な幅と高さの値を計算します。

  • disablePatchWaterMask メソッド: MutationObserver メソッドを通じて DOM 要素の変更を監視し、ユーザーがウォーターマークの表示をキャンセルできないようにします。

宣言命令: この命令をグローバルに使用できるように、メイン ファイルで宣言命令を定義します。

app.directive(&#39;water-mask&#39;, WaterMask);
ログイン後にコピー

次に、2 つのウォーターマークを 1 つずつ分析しましょう。 2 つのコア メソッド:waterMask と disablePatchWaterMask。

ウォーターマーク関数の実装

waterMask メソッドによって達成されます。waterMask メソッドは主に 4 つのことを行います:

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); // 转化图像
};
ログイン後にコピー

設定のデフォルト値を取得します: 開発者がパラメーターを渡すとき必ずしもすべての設定を渡す必要はありません。実際には、デフォルト値の一部に従うだけです。指示によってバインドされた値を浅くコピーし、それらを融合することで、デフォルト設定を更新できます:

キャンバス タグの作成 : キャンバス タグはキャンバスを通じて実装されるため、このラベルはテンプレートに直接表示されません。したがって、ドキュメント オブジェクトを通じてキャンバス ラベルを作成する必要があります:

function createCanvas() {
  const c = document.createElement(&#39;canvas&#39;);
  c.style.display = &#39;none&#39;;
  document.body.appendChild(c);
  return c;
}
ログイン後にコピー

描画テキスト:まず、表示する必要がある透かし情報を走査します。これは textArr テキスト配列です。配列を走査して、配列要素が構成されている各透かしのデフォルトの幅と高さを超えているかどうかを判断します。次に、この配列を超えるテキスト セグメンテーション配列を返します。テキスト要素に基づいてテキストの長さを返します。同時にテキストの最大幅を返します。最後に、キャンバスはカット結果を通じて動的に変更されます。幅と高さです。

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);
  });
}
ログイン後にコピー

上記のコードから、テキスト描画の中心的な操作は、長すぎるテキストを切り取り、キャンバスの幅と高さを動的に変更することであることがわかります。これら 2 つの操作がどのように実装されるかを見てみましょう。

measureText() メソッドは、現在のフォントに基づいて文字列の幅を計算します。

// 根据最大宽度切割文字
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;
}
ログイン後にコピー

Vue3 命令を使用して透かしの背景を実装する方法

したがって、キャンバスのグラフィック幅は hSinDeg wCosDeg settings.marginRight になります。キャンバス グラフィックの高さは、wSinDeg hCosDeg settings.marginBottom です。

  • 非常に長いテキストを切り取る:

  • 切り取りポイントを見つける: 二分検索メソッドを使用して、非常に長い文字列の位置をクエリします:

  • キャンバスの幅と高さを動的に変更する: 回転角度の値、最大幅の値、およびピタゴラスの定理を使用して、幅と高さを 1 つずつ計算します。回転角度をラジアン値に変換します (式: π/180×Angle、つまり (Math.PI*settings.deg!) / 180)、まず次の図を見てみましょう:

转化图像:通过对当前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;
}
ログイン後にコピー

Vue3 命令を使用して透かしの背景を実装する方法

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

Vue3 命令を使用して透かしの背景を実装する方法

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

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

以上がVue3 命令を使用して透かしの背景を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

vue3+vite: src に画像を動的にインポートするために require を使用するときのエラーを解決する方法 vue3+vite: src に画像を動的にインポートするために require を使用するときのエラーを解決する方法 May 21, 2023 pm 03:16 PM

vue3+vite:src は、イメージとエラー レポートと解決策を動的にインポートするために require を使用します。vue3+vite は複数のイメージを動的にインポートします。vue3。TypeScript 開発を使用している場合、イメージを導入するために require のエラー メッセージが表示されます。requireisnotdefined は使用できません。 vue2 のような imgUrl:require(' .../assets/test.png') は、typescript が require をサポートしていないため、インポートされます。そのため、import が使用されます。解決方法は次のとおりです: awaitimport を使用します

vue3 プロジェクトで tinymce を使用する方法 vue3 プロジェクトで tinymce を使用する方法 May 19, 2023 pm 08:40 PM

tinymce はフル機能のリッチ テキスト エディター プラグインですが、tinymce を vue に導入するのは他の Vue リッチ テキスト プラグインほどスムーズではありません。tinymce 自体は Vue には適しておらず、@tinymce/tinymce-vue を導入する必要があります。外国のリッチテキストプラグインであり、中国語版を通過していないため、公式 Web サイトから翻訳パッケージをダウンロードする必要があります (ファイアウォールをバイパスする必要がある場合があります)。 1. 関連する依存関係をインストールします npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2. 中国語パッケージをダウンロードします 3. スキンと中国語パッケージを導入します. プロジェクトのパブリック フォルダーに新しい tinymce フォルダーを作成し、

Vue3 がマークダウンを解析し、コードのハイライトを実装する方法 Vue3 がマークダウンを解析し、コードのハイライトを実装する方法 May 20, 2023 pm 04:16 PM

Vue はブログ フロントエンドを実装しており、マークダウン解析を実装する必要があり、コードがある場合はコードのハイライトを実装する必要があります。 Vue には、markdown-it、vue-markdown-loader、marked、vue-markdown など、マークダウン解析ライブラリが多数あります。これらのライブラリはすべて非常に似ています。ここではMarkedが使用され、コード強調表示ライブラリとしてhighlight.jsが使用されます。 1. 依存ライブラリをインストールする vue プロジェクトの下でコマンド ウィンドウを開き、次のコマンド npminstallmarked-save//marked を入力して、マークダウンを htmlnpmins に変換します。

Vue3 でページの部分的なコンテンツを更新する方法 Vue3 でページの部分的なコンテンツを更新する方法 May 26, 2023 pm 05:31 PM

ページの部分的な更新を実現するには、ローカル コンポーネント (dom) の再レンダリングを実装するだけで済みます。 Vue でこの効果を実現する最も簡単な方法は、v-if ディレクティブを使用することです。 Vue2 では、v-if 命令を使用してローカル dom を再レンダリングすることに加えて、新しい空のコンポーネントを作成することもできます。ローカル ページを更新する必要がある場合は、この空のコンポーネント ページにジャンプしてから、再びジャンプします。 beforeRouteEnter ガードを空白のコンポーネントに配置します。元のページ。以下の図に示すように、Vue3.X の更新ボタンをクリックして赤枠内の DOM を再読み込みし、対応する読み込みステータスを表示する方法を示します。 Vue3.X の scriptsetup 構文のコンポーネントのガードには o しかないので、

Vue3 の再利用可能なコンポーネントの使用方法 Vue3 の再利用可能なコンポーネントの使用方法 May 20, 2023 pm 07:25 PM

はじめに vue であれ、react であれ、複数の繰り返しコードに遭遇した場合、ファイルを冗長なコードの束で埋めるのではなく、これらのコードを再利用する方法を考えます。実際、vue と React はどちらもコンポーネントを抽出することで再利用を実現できますが、小さなコードの断片に遭遇し、別のファイルを抽出したくない場合は、それに比べて、React は同じファイル内で対応するウィジェットを宣言して使用できます。または、次のような renderfunction を通じて実装します。 constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

vue3 プロジェクトをパッケージ化してサーバーに公開した後、アクセス ページが空白で表示される問題の解決方法 vue3 プロジェクトをパッケージ化してサーバーに公開した後、アクセス ページが空白で表示される問題の解決方法 May 17, 2023 am 08:19 AM

vue3 プロジェクトがパッケージ化され、サーバーに公開されると、アクセス ページに空白の 1 が表示されます。vue.config.js ファイル内の publicPath は次のように処理されます: const{defineConfig}=require('@vue/cli-service') module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&

DefineCustomElement を使用して Vue3 でコンポーネントを定義する方法 DefineCustomElement を使用して Vue3 でコンポーネントを定義する方法 May 28, 2023 am 11:29 AM

Vue を使用してカスタム要素を構築する WebComponents は、開発者が再利用可能なカスタム要素 (カスタム要素) を作成できるようにする一連の Web ネイティブ API の総称です。カスタム要素の主な利点は、フレームワークがなくても、任意のフレームワークで使用できることです。これらは、異なるフロントエンド テクノロジ スタックを使用している可能性のあるエンド ユーザーをターゲットにする場合、または最終アプリケーションを使用するコンポーネントの実装の詳細から切り離したい場合に最適です。 Vue と WebComponents は補完的なテクノロジであり、Vue はカスタム要素の使用と作成に対する優れたサポートを提供します。カスタム要素を既存の Vue アプリケーションに統合したり、Vue を使用してビルドしたりできます。

Vue3 でアバターを選択してトリミングする方法 Vue3 でアバターを選択してトリミングする方法 May 29, 2023 am 10:22 AM

最終的な効果は、VueCropper コンポーネントのyarnaddvue-cropper@next をインストールすることです。上記のインストール値は Vue3 用です。Vue2 の場合、または他の方法を参照したい場合は、公式 npm アドレス: 公式チュートリアルにアクセスしてください。また、コンポーネント内で参照して使用するのも非常に簡単です。必要なのは、対応するコンポーネントとそのスタイル ファイルを導入することだけです。ここではグローバルに参照しませんが、import{userInfoByRequest}from'../js/api を導入するだけです。 ' コンポーネント ファイルにインポートします。import{VueCropper}from'vue-cropper&

See all articles