likes
comments
collection
share

canvas实现水印功能

作者站长头像
站长
· 阅读数 15

概述

实际项目中偶尔会遇到给项目页面背景加水印的需求,最近正好遇到,借机自己动手实现了这个功能,这里记录下实现思路和过程,支持文字和图片作为背景水印。

最终效果

  • 默认样式

canvas实现水印功能

  • 自定义颜色、字体大小等 canvas实现水印功能

实现思路

  • 首先不难想到的是,最终的水印文字重复平铺到页面背景上面,我们可以想到盒模型background属性,然后设置其背景图片为我们的文字即可.
  • 其次如何让给定的文本变成图片呢?我们可以考虑使用canvas绘图功能,根据给定的文字,生成canvas图片,然后转成base64格式,赋值给image标签不就可以了嘛,并且还能给文字设置不同的样式,例如:加粗、颜色、背景、渐变、倾斜等等.
  • 最后我们要做的就是将生成的背景图片平铺到页面即可,切记不能影响页面的布局,这里我们就可以考虑定位来实现.

实现过程

这里我们采用vue+ts的方式,将水印封装成组件

  • App.vue
<template>
  <div :class="['vite-app']">
    <watermark
      content="大家辛苦一下"
      :line-height="40"
      :rotate="-15"
      :fullscreen="true"
    >
      this is default text
      <ul>
        <li v-for="item in 5" :key="item">this is test content</li>
      </ul>
    </watermark>
     <div class="main-content">
      <ul>
        <li v-for="item in 60" :key="item">this is test content</li>
      </ul>
    </div>
  </div>
</template>
<script setup lang="ts">
import Watermark from "./components/Watermark.vue";
</script>

<style lang="less">
* {
  margin: 0;
  padding: 0;
}
.vite-app {
  font-weight: bold;
  font-size: 20px;
}
</style>

  • ./component/Watermark.vue
<template>
  <div
    :class="['water-mark', props.fullscreen ? 'water-mark-full-screen' : '']"
    ref="waterMarkContainer"
  >
    <slot></slot>
  </div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
const waterMarkContainer = ref<HTMLElement>();
//poprs属性
export interface WarkMark {
  fullscreen?: boolean;
  fontSize?: number;
  lineHeight?: number;
  fontFamily?: string;
  color?: string;
  width?: number;
  height?: number;
  xOffset?: number;
  yOffset?: number;
  rotate?: number;
  imgSrc?: string;
  content: string;
}
//默认值
const props = withDefaults(defineProps<WarkMark>(), {
  fontSize: 16,
  fontFamily: "宋体",
  color: "rgba(128, 128, 128, .5)",
  width: 250,
  height: 258,
  // height: 100,
  lineHeight: 16,
  xOffset: 10,
  yOffset: 28,
  rotate: 30,
});
function createWaterMark() {
  const canvas = document.createElement("canvas");
  canvas.height = props.height;
  canvas.width = props.width;
  const ctx = canvas.getContext("2d");
  let imageUrl = "";
  ctx!.rotate(props.rotate * (Math.PI / 180));
  ctx!.fillStyle = props.color;
  ctx!.font = `${props.fontSize}px ${props.fontFamily}`;
  const wrap = document.createElement("div");
  wrap.classList.add("mak-wrap");
  if (props.imgSrc) {
  //图片作为背景
    createImageWaterMark();
  } else {
  //文字作为背景
    createTextWaterMark();
  }
  function createDom() {
    wrap.style.backgroundImage =
      "url(" + imageUrl + ")," + "url(" + imageUrl + ")";
    wrap.style.backgroundPosition = `${props.width / 2}px ${
      props.height / 2
    }px, 0 0`;
    wrap.style.backgroundSize = props.width + "px";
    waterMarkContainer.value!.appendChild(wrap);
  }
  function createImageWaterMark() {
    const image = new Image();
    image.src = props.imgSrc!;
    image.width = props.width * 0.2;
    image.crossOrigin = "anonymous"; //允许图片跨域访问
    image.onload = () => {
      ctx!.drawImage(
        image,
        props.xOffset,
        props.xOffset + props.lineHeight + 10
      );
      imageUrl = canvas.toDataURL();
      canvas.style.opacity = ".5";
      createDom();
    };
  }

  function createTextWaterMark() {
    ctx!.fillText(
      props.content,
      props.xOffset,
      props.xOffset + props.lineHeight + 10
    );
    imageUrl = canvas.toDataURL();
    createDom();
  }
}

onMounted(() => {
  createWaterMark();
});
</script>

<style lang="less">
.water-mark {
  position: relative;
  pointer-events: none;
  .mak-wrap {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }
}
.water-mark.water-mark-full-screen {
  .mak-wrap {
    position: fixed;
    z-index:10;
  }
}
</style>

总结

实现思路明白后,上面的样式方面,可以根据自己需求进行自定义修改。