canvas实现水印功能

概述

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

最终效果

  • 默认样式

image.png

  • 自定义颜色、字体大小等 image.png

实现思路

  • 首先不难想到的是,最终的水印文字重复平铺到页面背景上面,我们可以想到image标签,然后设置其背景图片为我们的文字即可.
  • 其次如何让给定的文本变成图片呢?我们可以考虑使用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>();
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");
  wrap.style.zIndex = "-1";
  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;
  }
}
</style>

总结

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

全部评论

相关推荐

头顶尖尖的程序员:我也是面了三四次才放平心态的。准备好自我介绍,不一定要背熟,可以记事本写下来读。全程控制语速,所有问题都先思考几秒,不要急着答,不要打断面试官说话。
点赞 评论 收藏
分享
06-15 20:57
已编辑
门头沟学院 Java
CARLJOSEPH...:年轻人有傲气很正常,但是建议工作前洗净傲气。 说实在的,什么奖学金什么奖项的都很一般。尊重你的老师,在有时间的时候去上课,真遇到走不开的事,请态度端正地向你的老师说明情况,请求请假。我相信任何一个有师德的老师都会允许的(我的老师就是这样)。
点赞 评论 收藏
分享
昨天 11:26
清华大学 Java
打开电脑,思绪又回到了7月份刚开始的时候,感觉这个月过的如梦如幻,发生了太多事,也算是丰富了我本就是平淡的人生吧太早独立的我习惯了一切都是自己做决定,拥有绝对的决定权,而且永远不会听取别人的建议。我就是那个恋爱四年出轨的男主啦,感觉既然在牛客开了这个头,那我就要做个有始有终的人。从我出轨到结束再到和女朋友和好如初真的太像一场梦了,短短的一个月我经历了太多,也成长了很多,放下了那些本就不属于我的,找回了那些我不该放弃的。我的人生丰富且多彩,但人不能一直顺,上天总会让你的生活中出点乱子,有好有坏,让你学会一些东西,让你有成长。我和女朋友的恋爱四年太过于平淡,日常除了会制造一些小浪漫之外,我们的生活...
段哥亡命职场:不得不说,我是理解你的,你能发出来足见你是个坦诚的人,至少敢于直面自己的内心和过往的过错。 这个世界没有想象中那样非黑即白,无论是农村还是城市,在看不见的阴影里,多的是这样的事。 更多的人选择站在制高点去谩骂,一方面是社会的道德是需要制高点的,另一方面,很多人不经他人苦,却劝他人善。 大部分的我们,连自己生命的意义尚且不能明晰,道德、法律、困境,众多因果交织,人会迷失在其中,只有真的走出来之后才能看明白,可是没走出来的时候呢?谁又能保证自己能走的好,走的对呢? 可是这种问题有些人是遇不到的,不去追寻,不去探寻,也就没了这些烦恼,我总说人生的意义在过程里,没了目标也就没了过程。 限于篇幅,没法完全言明,总之,这世界是个巨大的草台班子,没什么过不去了,勇敢面对,革故鼎新才是正确,祝你早日走出来。查看图片
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务