1、遇到的问题

在网上也有一些封装好的插件和组件,我都体验了一下,发现用起来还是有些小问题,比如我遇到的问题就是使用封装好的组件,直接在App.vue中引入使用,发现本地环境是可以正常展示,但是发布到测试环境后发现登录项目页面竟然没有水印,除非手动刷新页面,水印才能展示出来;

查询原因

我在网上找到水印组件,本地环境没问题,发布到测试环境后,发现有水印DOM结构,但是页面上不展示水印,而这个DOM是有宽高的,所以就暂时排除了DOM方面的问题;然后就怀疑是不是逻辑写错了或者是渲染机制的问题,因为之前有遇到过Echarts在DOM还没生成的时候就绘制了,这样肯定会有警告或报错,原因是代码在 DOM 元素尚未完全加载和渲染时就尝试获取其尺寸;

        一般解决方法就是 1、把调用创建水印的方法放到mounted里面执行,这里DOM就生成了;

   export default {
     mounted() {
       // 在这里执行需要获取 DOM 尺寸的代码
     }
   };

2、要么就是延迟执行,如果不能确定 DOM 何时完全加载,可以使用setTimeout函数延迟执行获取 DOM 尺寸的代码,给页面一些时间来加载和渲染.

   setTimeout(function() {
     // 在这里执行需要获取 DOM 尺寸的代码
   }, 500); // 延迟 500 毫秒执行

然而我使用了很多方法,甚至把组件代码直接放到App.vue里面,调用方法放到mounted里面,发布到测试环境发现还是不行(至今都没搞明白,有没有哪位大佬知道这个问题到底是什么原因呢);最后千钧一发之际,灵机一动,为何不尝试自己用CSS写一个水印呢?进而就有了以下的故事......

方法对比

使用插件或网上现成的组件和直接使用 CSS 样式写水印的对比:

使用插件或网上现成组件:

  1. 增加项目依赖:引入插件会增加项目的依赖项,可能会带来潜在的兼容性问题。如果插件停止维护或者与项目中的其他库冲突,可能会导致项目出现故障。
  2. 性能影响:一些复杂的插件可能会对项目的性能产生一定的影响,尤其是在处理大量数据或频繁更新的场景下。
  3. 学习成本:虽然使用插件相对简单,但仍然需要一定的时间来学习如何配置和使用插件,特别是对于不熟悉该插件的开发者来说。

当然优点也是有的,比如功能丰富,直接使用,不用花费太多的自己的思考时间。

使用css样式:

  1. 轻量级:不会引入额外的依赖,减少了项目的复杂性和潜在的兼容性问题。
  2. 性能较好:由于是直接使用 CSS 实现,通常不会对项目性能产生较大的影响,特别是对于小型项目或对性能要求较高的场景。
  3. 灵活性高:你可以根据项目的具体需求,灵活地调整 CSS 样式来实现不同的水印效果,完全掌控水印的外观和行为。

当前缺点也是有的,比如可维护性相对较差,可能对于一些复杂的水印效果,CSS写起来就不是那么方便了。

2、处理过程

经过大量考究和为了项目的稳定性,我还是决定使用手写CSS来实现水印的效果,前提是我知道我们项目的这个水印一般情况下写好了是不会变动的,也减少后期的维护。

需要思考的是,你想要一行展示多少个(取决于屏幕的宽度 / 每个水印之间的间隔),一个屏幕展示多少行(取决于屏幕的高度 / 每行水印之间的间隔),并且这个水印得根据屏幕大小来更新数量,剩下的就是一些水印本事的样式了(字体大小、颜色、旋转角度等等);最后监听window的宽高就完事了        

3、最终结果

以下是以vue文件为例,可以直接使用的文件,开箱即用,无需其他封装

1、在components文件下创建waterMark.vue文件,专门用于水印的组件;

// waterMark.vue
<template>
  <div class="watermark-container">
    <div
      class="watermark-text"
      v-for="index in totalWatermarks"
      :key="index"
      :style="{
        top: calculateTop(index),
        left: calculateLeft(index),
        transform: 'rotate(-30deg)',
      }"
    >
      {{ watermarkText }}
    </div>
  </div>
</template>

<script>
export default {
  props: {
    // 外部传入的水印文本
    watermarkText: {
      type: String,
      required: true
    }
  },
  data () {
    return {
      innerWidth: null,
      innerHeight: null
    }
  },
  mounted () {
    this.innerWidth = window.innerWidth
    this.innerHeight = window.innerHeight
    window.addEventListener('resize', this.generateWatermarks)
  },
  beforeDestroy () {
    window.removeEventListener('resize', this.generateWatermarks)
  },
  computed: {
    totalWatermarks () {
      const minRows = 3 // 假定至少有3行水印
      const widthCount = Math.ceil(this.innerWidth / 300) 
      const heightCount = Math.max(minRows, Math.ceil(this.innerHeight / 250)) 
      return widthCount * heightCount
    }
  },
  methods: {
    // 计算出每个水印的top值
    calculateTop (index) {
      const widthCount = Math.ceil(this.innerWidth / 300) //每个水印之间的间隔
      const row = Math.floor(index / widthCount)
      return row * 250 + 'px'
    },
    // 计算出每个水印的left值
    calculateLeft (index) {
      const widthCount = Math.ceil(this.innerWidth / 300) //每行水印之间的间隔
      return (index % widthCount) * 400 + 'px'
    },
    generateWatermarks () {
      this.innerWidth = window.innerWidth
      this.innerHeight = window.innerHeight
      const watermarkContainer = this.$el.querySelector('.watermark-container')
      if (watermarkContainer) {
        const watermarkTexts = watermarkContainer.querySelectorAll('.watermark-text')
        watermarkTexts.forEach((watermarkText, index) => {
          watermarkText.style.top = this.calculateTop(index)
          watermarkText.style.left = this.calculateLeft(index)
        })
      }
    }
  }
}
</script>

<style scoped>
.watermark-container {
  position: fixed;
  top: 70px;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 9999;
}

.watermark-text {
  position: absolute;
  transform: translate(-50%, -50%) rotate(-30deg); // 水印偏移量和旋转角度,这里设置的角度要和上面DOM中的角度设置一致
  opacity: 0.2;     // 水印透明度
  color: #808184;   // 字体颜色
  font-size: 14px;  // 字体大小
}
</style>

2、在所需要的页面直接引入使用即可

由于我的项目要求每个页面都要有水印,所以我直接用在了App.vue里面,如果你需要指定页面加水印的话,直接引入指定的页面即可

// App.vue
<template>
  <div id="app">
    <router-view />
    <Watermark :watermarkText="watermarkText"/>
  </div>
</template>

<script>
import Watermark from './components/waterMark/index.vue'
export default {
  name: 'App',
  components: {
    TopAlert,
    Watermark
  },
  data () {
    return {
      watermarkText: 这是水印文本
    }
  },
}
</script>

3、最后看下效果图吧

最后发布到测试和线上环境后依然没问题.

4、总结

平时写代码不能太依赖网上现成的成果,就算有那也的自己有本事消化才行,遇到问题还是要多思考,如果一条路不通,换条路走,如果换条路还不通,那就自己踏出一条路,条条大路通罗马;最后祝愿看到该帖子的小伙子身体健康,事业顺利,从此无bug。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐