您现在的位置是:亿华云 > 系统运维

聊聊生成水印原理与插件编写

亿华云2025-10-03 07:07:16【系统运维】3人已围观

简介本文转载自微信公众号「前端Sharing」,作者前端Sharing。转载本文请联系前端Sharing公众号。一 前言今天分享一个小程序生成水印的小技巧——canvas绘制背景图,接下来我会详细介绍绘制

本文转载自微信公众号「前端Sharing」,聊聊作者前端Sharing。生成水印转载本文请联系前端Sharing公众号。原理

一 前言

今天分享一个小程序生成水印的插件小技巧——canvas绘制背景图,接下来我会详细介绍绘制的编写细节。希望开发过微信小程序的聊聊同学可以把文章收藏起来,这样如果以后遇到类似的生成水印需求,可以翻出来作为参考。原理

本文的插件插件同样适用于Taro,uniapp,编写原生等构建的聊聊小程序项目,项目demo是生成水印采用Taro-Vue构建的。

我们先来看看demo效果。原理

二 原理实现

canvas绘制背景图这个方案原理本质上是插件非常简单的,就如把大象放冰箱一共分成三步那样简单?编写???。

第一步冰箱门打开,因为这个功能是前端实现的,而且是canvas画出来的,所以我们需要海报的基础配置,服务器租用比如canvas海报宽高,文字内容,文字颜色,海报文字的旋转角度等。 第二步把大象??放进去,canvas得到配置,绘制水印底图。

那么问题来了,我们绘制的底图是整张水印底图吗?

答案是否定的。我们只需要画一个模版图片就可以了,如下图所示:

但是为了让水印填充整个手机屏幕,我们需要将水印图片作为背景图片,然后设置background-repeat:repeat;就可以了。

第三步把冰箱门关上,我们通过canvas生成的图片,将图片填充整个屏幕就可以了。

三 细节实现

demo样板

canvas模版接下来我们开始实现具体的细节,首先我们页面放入一个

<view     class="markBox"     :style="{  backgroundImage: `url(${ url})` }" >       <canvas       v-show="isShow"       :id="settings.id"       type="2d"       :class="settings.id"       :style="{  width:settings.width + px , height : settings.height + px }"     /> </view>  这里有一点值得注意,就是我们设置的type = 2d最好不要用canvasId方式来操作canvas,包括获取canvas实例,调用canvasToTempFilePathapi等,不然可能会失效。这里采用的站群服务器是通过id方式,来获取canvas实例的。 当我们绘制完成后,隐藏canvas,将view容器设置背景图片,背景图片就是canvas绘制形成的图片。 canvas宽度和高度是根据canvas的配置项添加的,所以我们要动态用style属性设置宽高。

配置项

export default {     /* 前端生成水印 */    name:"MakeWaterMark",    data(){         return {             isShow:true,            url:,            settings: {                  id: waterMark,                  /* canvas id */                 content: 我不是外星人,            /* 水印内容 */                 width: 200,                       /* 图片/canvas 宽度 */                 height: 200,                      /* 图片/canvas 高度 */                  rotate: -45,                      /* 水印文案旋转角度*/                  left:60,                          /* 水印文案相对图片水平偏移量 */                 top:80,                           /* 水印文案相对图片垂直偏移量 */                 fontSize: 15px,                 /* 水印文字大小 */                 fontFamily: Microsoft JhengHei, /* 水印文字样式 */                 bg: #fff,                       /* 图片背景颜色 */                  color: #ccc,                    /* 水印文字颜色 */                 fontWeight: normal,             /* 水印文字宽度 */             }        }    }, } 

样式处理

.markBox{      position: fixed;     left:0;     right:0;     bottom: 0;     top:0;     background-repeat:repeat ; }  给外层容器设置样式,能够让水印图片平铺整个页面。

插件核心代码

插件的核心功能就是生成水印图片,除此之外还要满足两个要求:

插件本身和页面/组件低耦合。插件本身可以在任何组件中使用。 插件不受构建平台的限制,就是既能在原生微信小程序中使用,也能在Taro,uniapp等构建工具中使用。

接下来我们看一下具体细节:

function createPromise(callback){      return new Promise((resolve,reject)=>{          callback(resolve,reject)     }) } /**  * 制作水印图片  */ class MarkwaterMark{      constructor(options){          /* 初始化配置 */        this.platform  = null         this.pixelRatio = null        this.context = null        this.options = options         this.ready = createPromise(this._init.bind(this))           /* 生成组件方法 */        this.make = (cb) => {             if(this.context){               this._markPicture(cb)           }else{                this.ready.then(()=>{                   this.context && this._markPicture(cb)               })           }            }     }     /* 初始化方法 */     _init(next,fail){         const {  platform , pixelRatio } = wx.getSystemInfoSync()        this.platform = platform        this.pixelRatio = pixelRatio        const query = wx.createSelectorQuery()        query.select(# + this.options.id ).fields({          node: true,         size: true       }).exec((res)=>{          let {              node,         } = res[0] || { }          if (!node) return fail && fail()         this.context = node.getContext(2d)             this.node = node         next()       })     }     /* 制作水印图片 */     _markPicture(cb){         const {  width , height , bg ,color ,fontSize, fontFamily,fontWeight ,content , left, top ,rotate } = this.options        this.node.width = (width || 200) * this.pixelRatio        this.node.height =( height || 200) * this.pixelRatio        this.context.scale(this.pixelRatio,this.pixelRatio)        this.context.fillStyle = bg || #fff        this.context.fillRect(0, 0, width, height)        this.context.fillStyle = color || #000        this.context.save()        this.context.translate(left,top)        this.context.rotate(Math.PI * rotate / 180 )        this.context.font =  `${ fontWeight} 400 ${ fontSize} ${ fontFamily}`        this.context.fillText(content, 0, 0)        this.context.restore()         this._savePicture(cb)     }     /* 生成图片 */     _savePicture(cb){       const {  width , height  } = this.options        wx.canvasToTempFilePath({            x:0,           y:0,           width,           height,           destWidth:width*1,           destHeight:height*1,           canvas:this.node,           success:function(res){               cb && cb(res.tempFilePath)           }        })     } } /**  *   * @param { *} options 配置项  */ function makeWatermark(options){    if(!wx) return null   return new MarkwaterMark(options)  } module.exports = makeWatermark 

核心功能流程分析:

第一步:暴露makeWatermark接口,可以实例化一个MarkwaterMark对象,实例化过程中本身先进行初始化配置,包裹一层Promise用于创建图片。源码库由于canvas操作中,很多方法都是异步的,所以用 createPromise 方法代理一层Promise。 第二步:MarkwaterMark对象上,有make方法,会获取canvas实例,然后设置canvas画布的宽高和缩放比,绘制水印canvas。 第三步:将canvas通过canvasToTempFilePath接口把canvas画布转化成临时图片,并把临时图片路径通过callback形式,传递给业务组件或者页面。

插件使用

在业务组件(上述demo)中,我们就可以使用上述插件了。具体参考如下:

mounted(){         Taro.nextTick(()=>{            /* 创建一个  makeWatermark 对象 */           const marker = makeWatermark(this.settings)           /* 调用make,生成图片 */           marker.make((url)=>{                /* url 为临时路径  */               const fileManage = Taro.getFileSystemManager()               let base64 = data:image/jpg;base64, + fileManage.readFileSync(url,base64);               this.url = base64               this.isShow  = false           })       })   }, 

重要细节:

这里还有一个比较重要细节就是,小程序中背景图片一般都是网络图片或者base64图片,对于临时路径的图片在真机上是不显示的。为了解决这个问题,我们需要把临时图片转换成base64格式图片。

通过 getFileSystemManager 以 base64方式访问刚生成的临时图片,然后返回base64格式,接下来就可以把 base64 图片设置为背景图片了。

效果:

大功告成!!!

四 总结

通过本文我们学习了微信小程序生成水印的方式和流程。还有一些开发中的细节问题。感兴趣的同学可以收藏起来,以备不时之需。

很赞哦!(92)