「微信小程序」生成水印原理与插件编写

简介: 小程序页面生成水印流程

一 前言

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

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

我们先来看看demo效果。

WechatIMG157.jpeg

二 原理实现

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

  • 第一步冰箱门打开,因为这个功能是前端实现的,而且是canvas画出来的,所以我们需要海报的基础配置,比如canvas海报宽高文字内容文字颜色海报文字的旋转角度等。

  • 第二步把大象🐘放进去,canvas得到配置,绘制水印底图。

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

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

111.jpg

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

222.jpg

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

三 细节实现

demo样板

canvas模版
接下来我们开始实现具体的细节,首先我们页面放入一个<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 ;
}
  • 给外层容器设置样式,能够让水印图片平铺整个页面。

插件核心代码

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

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

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

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格式图片

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

效果:

WechatIMG157.jpeg

大功告成!!!

四 总结

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

最后, 送人玫瑰,手留余香,觉得有收获的朋友可以给笔者点赞,关注一波 ,陆续更新前端超硬核文章。

相关文章
|
3月前
|
算法 Java API
用录像代替视频聊天,虚拟视频聊天软件微信QQ, 微信第三方插件虚拟视频插件
核心视频处理模块使用JavaCV实现视频捕获、特效处理和虚拟设备输出 Xposed模块通过Hook微信摄像头相关方法实现视频流替换
|
4月前
|
Shell 数据安全/隐私保护 Python
微信虚拟摄像头插件,QQ虚拟相机拍摄录像工具,替换虚拟视频聊天软件
完整的虚拟摄像头实现方案,包含多个模块的代码实现。这个项目可以模拟摄像头设备,并在微信/QQ视频
|
5月前
|
Android开发
微信自动发朋友圈脚本,定时发朋友圈插件群发,多账户发朋友圈批量工具
整的微信朋友圈自动发布功能,包含环境检查、界面元素定位、图片识别、异常处理等模块
|
3月前
|
Shell Android开发 Python
微信多开脚本,微信双开器脚本插件,autojs开源代码分享
AutoJS脚本实现安卓端微信多开,通过无障碍服务 Python脚本提供跨平台解决方案,自动检测微信安装路径
|
4月前
|
JSON 机器人 API
微信机器人自动回复插件,vx自动回复机器人脚本助手,python框架分享
这个微信机器人系统包含三个主要模块:主程序基于itchat实现微信消息监听和自动回复功能
|
4月前
|
Java 计算机视觉
微信虚拟视频聊天插件,QQ抖音快手虚拟摄像头工具,替换相机视频流java
实现包含了虚拟摄像头核心功能,可以捕获真实摄像头视频流,处理后输出到虚拟摄像头设备。
微信qq陌陌soul,虚拟视频聊天插件,xposed摄像头替换工具
包含三个核心模块:虚拟摄像头服务、视频处理引擎和Xposed框架集成。
|
4月前
|
监控 数据库 数据安全/隐私保护
微信自动抢红包永久免费软件, 自动抢红包软件微信,脚本插件抢红包【python】
该实现包含三个核心模块:主监控程序、数据库记录模块和配置模块。主程序使用itchat监听微信消息
|
4月前
|
Android开发 数据安全/隐私保护 Python
微信抢红包脚本安卓插件,微信xposed抢红包模块, magisk微信抢红包模块
这个代码实现了一个完整的微信抢红包自动化工具,包含红包检测、自动点击、日志记录等功能
|
4月前
|
消息中间件 人工智能 机器人
vx自动回复机器人,ai自动回复机器人,微信自动回复脚本插件
这个微信自动回复机器人包含主程序、配置管理、工具函数和单元测试模块。主程序使用itchat库实现微信登录和消息处理