您现在的位置是:亿华云 > 系统运维
聊聊生成水印原理与插件编写
亿华云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;就可以了。
三 细节实现
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)