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

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

一 前言

今天分享一个小程序生成水印的小技巧——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

大功告成!!!

四 总结

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

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

相关文章
|
27天前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的《数据库系统原理》课程平台附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的《数据库系统原理》课程平台附带文章和源代码设计说明文档ppt
24 1
|
6天前
|
Java 测试技术 数据安全/隐私保护
微信授权就是这个原理,Spring Cloud OAuth2 授权码模式
微信授权就是这个原理,Spring Cloud OAuth2 授权码模式
|
1月前
|
小程序 JavaScript 前端开发
简述微信小程序原理
简述微信小程序原理
|
1月前
|
JSON 小程序 前端开发
小程序使用npm以及常用的移动端UI插件
小程序使用npm以及常用的移动端UI插件
20 0
|
1月前
|
缓存 小程序 JavaScript
支付宝小程序性能优化原理及手段
支付宝小程序性能优化原理及手段
146 11
|
1月前
|
移动开发 小程序 Go
【社区每周】小程序消息订阅插件升级为消息订阅接口(2022年8月第五期)
【社区每周】小程序消息订阅插件升级为消息订阅接口(2022年8月第五期)
25 0
|
1月前
|
小程序 IDE TensorFlow
【社区每周】插件开发支持“静态懒加载”模式;小程序新增“占位组件”(2022年5月第三期)
【社区每周】插件开发支持“静态懒加载”模式;小程序新增“占位组件”(2022年5月第三期)
30 0
|
1月前
|
XML 开发框架 数据格式
微信开发框架原理
微信开发框架原理
71 0
|
7月前
|
JavaScript 前端开发 API
04 微信公众号开发原理
04 微信公众号开发原理
69 0
04 微信公众号开发原理
|
7月前
|
小程序 JavaScript
微信小程序统计图表插件wxcharts的使用汇总
微信小程序统计图表插件wxcharts的使用汇总
114 0