实战:前端项目中Watermark水印组件的实现过程
「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」
前言
2022首次更文挑战已经断更三天了,有存货但是没有发,毕竟想搏一搏春节幸运奖,梦想还是要有的,万一实现了呢?对于此次更文我本身没有打算全勤40天的计划,反正能更多少天算多少天,主要还是为了给自己留下记录,顺便把自己遇到的问题分享出来,避免大家再去踩坑。
这次主要来聊聊如何封装一个Vue
的水印组件?这次不是React
了哦,毕竟React
相搭配的Antd pro
提供的水印组件还是很优秀的,Vue
项目用别人的又怕删库跑路倒是GG了,所以还是自己动手,丰衣足食吧。
需求背景
好了,来水印组件的需求没有多复杂,就是一句话:所有页面给加上水印,水印文字为用户姓名和手机号码。其实这里为什么要封装Vue
的水印组件,主要是因为我们的系统是一个融合平台,主平台的开发技术栈是Vue
,而嵌入的页面是React
,而嵌入的页面我们使用了Antd pro
的水印组件,所以产品认为拿过来使用就行了,听到我就想笑,不懂技术的产品还真是可爱呀^_^。没办法,既然没有那我们就需要造轮子,以和为贵。
开发
既然说到了React
和Vue
中都使用了水印组件,那我们就在开发里面都说说两者是如何使用的,先来说说Vue
项目中是如何封装的,毕竟别人的是现成的拿过来直接使用就行,讲讲用法就好。
Vue项目中的水印组件
其实封装组件不知道如何下手时,先去看看别人现有的组件是怎么实现的,参考一下实现思路和样式布局这些,这样就会灵感大开,自然就会手到擒来了。我也是参考了Antd pro
的水印组件的实现,只不过技术变成了Vue
而已。好了,纸上得来终觉浅,开撸。
创建Watermark.vue
文件:
<template>
<div :style="overlapStyle">
<slot></slot>
<div class="bit-mark-wrap" :style="{'background-image': `url(${base64URL})`}"></div>
<canvas class="bit-watermark-canvas" ref="canvas" width="450" height="320" />
</div>
</template>
<script>
/**
* 背景水印组件
*/
export default {
name: 'Watermark',
props: {
text: String,
overlapStyle: {
type: String,
default () {
return ''
}
}
},
data () {
return {
base64URL: ''
}
},
mounted () {
this.drawInit()
},
methods: {
drawInit: function () {
const { text } = this
let ctx = this.$refs.canvas.getContext('2d')
ctx.width = 120
ctx.height = 160
ctx.font = '22px normal'
ctx.fillStyle = 'rgba(0, 0, 0, .15)'
ctx.rotate(-22 * Math.PI / 180)
ctx.fillText(text, 0, 120)
// 转base64图片
this.base64URL = this.$refs.canvas.toDataURL('image/png')
}
}
}
</script>
<style type="text/css">
.bit-watermark-canvas {
position: fixed;
bottom: -1000px;
right: -1000px;
}
.bit-mark-wrap {
z-index: 9;
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-size: 460px;
pointer-events: none;
background-repeat: repeat;
}
</style>
从上面代码可以看到,水印的核心还是通过canvas
绘制,然后将绘制的内容转成base64
格式图片,通过样式控制其是否平铺重复展示。但是可以看到在封装这个组件很多属性的值都是写死的,对于变化的需求来说达不到很好的复用性,所以需要优化代码,使组件的复用性更强。
优化后:
<template>
<div :style="overlapStyle">
<slot></slot>
<div
class="bit-mark-wrap"
:style="{'background-image': `url(${base64URL})`, 'background-size': `${gapX}px ${gapY}px`}"
></div>
<canvas class="bit-watermark-canvas" ref="canvas" width="450" height="320" />
</div>
</template>
<script>
/**
* 背景水印组件
*/
export default {
name: 'Watermark',
props: {
text: String,
overlapStyle: {
type: String,
default () {
return ''
}
},
width: {
type: Number,
default () {
return 120
}
},
height: {
type: Number,
default () {
return 160
}
},
fontSize: {
type: Number,
default () {
return 16
}
},
fontColor: {
type: String,
default () {
return '#ccc'
}
},
rotate: {
type: Number,
default () {
return -22
}
},
gapX: {
type: Number,
default () {
return 320
}
},
gapY: {
type: Number,
default () {
return 222
}
}
},
data () {
return {
base64URL: ''
}
},
mounted () {
this.drawInit()
},
methods: {
drawInit: function () {
const { text, width, height, fontSize, fontColor, rotate } = this
let ctx = this.$refs.canvas.getContext('2d')
ctx.width = width
ctx.height = height
ctx.font = `${fontSize}px normal`
ctx.fillStyle = fontColor
ctx.rotate(rotate * Math.PI / 180)
ctx.fillText(text, 0, height)
// 转base64图片
this.base64URL = this.$refs.canvas.toDataURL('image/png')
}
}
}
</script>
<style lang="scss" type="text/css">
.bit-watermark-canvas {
position: fixed;
bottom: -1000px;
right: -1000px;
}
.bit-mark-wrap {
z-index: 9;
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-size: 320px;
pointer-events: none;
background-repeat: repeat;
}
</style>
可以看到我把之前代码写死的属性都搞成了参数,这样可以动态传入,方便各个页面复用时有不同的需求样式。具体的参数参考以下表格:
API:
参数 | 说明 | 类型 | 默认值 | |
---|---|---|---|---|
width | 水印的宽度 | number | 120 | |
height | 水印的高度 | number | 160 | |
rotate | 水印绘制时,旋转的角度,单位° | number | -22 | |
fontColor | 水印文字颜色 | string | #ccc | |
fontSize | 文字大小 | string | number | 16 |
overlapStyle | 水印层的样式 | React.CSSProperties | - | |
text | 水印文字内容 | string | - | |
gapX | 水印之间的水平间距 | number | 320 | |
gapY | 水印之间的垂直间距 | number | 222 |
这些参数中重点说一下rotate
、gapX
、gapY
三个参数:rotate
控制水印的旋转角度,默认为-22度;gapX
是控制水印在页面中水平间距,即每一行显示多少个水印;gapY
则是控制水印在页面中垂直间距,即每一列显示多少个水印。
在上面代码中可以看到代码格式没有分号;
,这主要是由于我们的Vue
项目使用了Eslint
规则,配置了不使用分号,但是在我们其他项目并没有采用Eslint
,小伙伴们看文章时不要认为我的代码习惯不好,其实我是有代码洁癖的^_^。
使用方法:
1)导入组件
import Watermark from '../components/Watermark'
components: {
Watermark
}
2)使用组件:
// 效果见图1
<watermark overlapStyle="height: 100%;" :text="'张三130xxxx0000'">
<router-view></router-view>
</watermark>
// 效果见图2
<watermark
overlapStyle="height: 100%;"
:text="'张三130xxxx0000'"
:rotate="-30"
:height="240"
:gap-x="320"
:gap-y="320"
:font-size="18"
:font-color="'red'"
>
<router-view></router-view>
</watermark>
效果:
为了展示效果,我将水印组件套在了路由router-view
的外面,并不代表组件只能这样引用,所以小伙伴们不要误解。水印组件在项目需要的页面都可以引用使用,我只是方便展示效果省事就这样使用,这样使用系统所有的页面都会加上水印,具体需求按照各自真实需求自行引用即可。
React项目中的水印组件
React
项目中使用水印组件,我们就没有必要自己写一个了,Antd pro
提供的水印组件足以满足项目需求,而且可能还比我们自己写得好用,我之所以封装Vue
的是因为没有现成的,只有自己来,所以React
项目中的使用水印组件我只说用法就好,望周知。
安装插件:
我们按需安装pro-layout
就好,没有必要安装所有Antd pro
所有插件。
npm i @ant-design/pro-layout 或 yarn i @ant-design/pro-layout
使用:
1)导入组件:
import {WaterMark} from "@ant-design/pro-layout";
2)使用组件:
<WaterMark {
...{...getWaterMarkProps(),
gapX: 120,
gapY: 140,
}
}>
<div className="bit-chart-box">
<div>...</div>
</div>
</WaterMark>
可以看到先定义了一个getWaterMarkProps()
获取水印组件属性的方法,在里面保证全部页面都有一个统一的样式,而至于需要特性化的页面通过覆盖属性值去做修改并达到不同页面不同效果的目的。
getWaterMarkProps()
方法如下:
/**
* 获取水印属性
*/
export const getWaterMarkProps = (): any => {
const {employee, mobile} = getUserInfo();
let props = {
content: `${employee ? employee : ''} ${mobile ? mobile : ''}`,
gapX: 220,
gapY: 140,
offsetTop: 100,
fontColor: 'rgba(255, 255, 255, 0.1)'
}
return props;
}
API:
具体API
可以通过API文档链接进行查看使用。
效果:
至此,关于在项目中如何封装水印组件,使用水印组件的方法就差不多了,解决问题的方法很多,我只是提供了一种解决办法而已,并且我相信小伙伴们还有更好的解决办法,我只是做一个抛砖引玉之人,更多地方法需要大家一起去优化提升。
纸上得来终觉浅,绝知此事要躬行——陆游
总之,我始终认为技术这条路要想走远并不是看看就能学会,当然不排除天才,所以更多地还是得自己实践实践,上手撸一撸代码方能牢牢掌握。临近过年,这是年前最后一次更文了,新年也会更文,到时的更文就是博运气薅羊毛了,但是我能保证的是绝不是水文,毕竟我对水文是厌恶至极,也不会写水文。好了,最后祝大家身体健康,万事如意,虎年大吉💖。
往期精彩文章
- 最强富文本编辑器?TinyMCE系列文章【3】
- 最强富文本编辑器?TinyMCE系列文章【2】
- 最强富文本编辑器?TinyMCE系列文章【1】
- 在React项目中实现仿饿了么Checkbox多选按钮样式的效果组件
- 2022第一次更文:前端项目实战中的3种Progress进度条效果
- 2022年前端技术趋势:你不进来看看,怎么赚它一个小目标?
- 假如古代有程序员写总结,大概就是这样吧 | 2021年终总结
后语
伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注在走呗^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。
转载自:https://juejin.cn/post/7057428945695670308