笔锋签字页移植微信小程序

2020-01-17  本文已影响0人  Demonskp

签字页移植微信小程序

最近领导提出了一个在手机上签字的需求,而且考虑其他的因素比较适合放到微信小程序当中去实现。因此对小程序端的签字canvas进行了一系列的研究。

获取web端的签字

web端的签字页面之前是写过的,不过当时需求简单就很简单写了。这次是领导的需求,因此还是做个效果比较好的。不重复造轮子,就直接在网上找了一个签字程序(现在忘了出自那里了)。

sign-web.gif

效果还是很不错的,相比于之前我自己简单写的版本,加入了通过速度控制笔锋大小,以及回撤功能。

<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=400">
    <title> canvas手写毛笔字效果 </title>
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
            text-align: center;
            background-color: #936;
        }

        #canvasId {
            background-color: #FFd;
        }

        .button {
            width: 140px;
            height: 60px;
            font-size: 20px;
        }

    </style>
</head>

<body>
    <h1>手写毛笔字效果-手机版</h1>
    <canvas id="canvasId" width="400" height="500"></canvas><br />
    <input type="button" value="全部清除" class="button" onclick="hw.clear();" />
    <input type="button" value="清除最后一笔" class="button" onclick="hw.historyBack();" />
    <script type="text/javascript">
        function Handwriting(id) {
            this.canvas = document.getElementById(id);
            this.ctx = this.canvas.getContext("2d");
            var on = ("ontouchstart" in document) ? {
                start: "touchstart",
                move: "touchmove",
                end: "touchend"
            } : {
                start: "mousedown",
                move: "mousemove",
                end: "mouseup"
            };
            this.canvas.addEventListener(on.start, this.downEvent.bind(this), false);
            this.canvas.addEventListener(on.move, this.moveEvent.bind(this), false);
            this.canvas.addEventListener(on.end, this.upEvent.bind(this), false);
            this.canvas.addEventListener("contextmenu", function (e) {
                e.preventDefault()
            }, false);
            this.moveFlag = false;
            this.upof = {};
            this.radius = 0;
            this.has = [];
            this.startOf = null;
            this.lineMax = 30;
            this.lineMin = 3;
            this.linePressure = 1;
            this.smoothness = 80;
            this.history = [];
            this.setColor("rgba(0,0,0,0.25)");
        }

        Handwriting.prototype.clear = function () {
            this.history = [];
            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }

        Handwriting.prototype.historyBack = function () {
            this.history.pop();
            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
            for (var i = 0; i < this.history.length; i++) {
                var h = this.history[i];
                for (var j = 0; j < h.length; j += 3) {
                    this.ctx.beginPath();
                    this.ctx.arc(h[j], h[j + 1], h[j + 2], 0, 2 * Math.PI, true);
                    this.ctx.fill();
                }
            }
        }

        Handwriting.prototype.downEvent = function (e) {
            this.moveFlag = true;
            this.has = [];
            this.upof = this.getXY(e);
            this.startOf = this.upof;
        }

        Handwriting.prototype.moveEvent = function (e) {
            if (!this.moveFlag)
                return;
            e.preventDefault();
            var of = this.getXY(e);
            var up = this.upof;
            var ur = this.radius;
            this.has.unshift({
                time: new Date().getTime(),
                dis: this.distance(up, of )
            });
            var dis = 0;
            var time = 0;
            for (var n = 0; n < this.has.length - 1; n++) {
                dis += this.has[n].dis;
                time += this.has[n].time - this.has[n + 1].time;
                if (dis > this.smoothness)
                    break;
            }
            var or = Math.min(time / dis * this.linePressure + this.lineMin, this.lineMax) / 2;
            this.radius = or;
            this.upof = of ;
            if (dis < 7)
                return;
            if (this.startOf) {
                up = this.startOf;
                ur = or;
                this.startOf = null;
                this.history.push([]);
            }
            var len = Math.ceil(this.distance(up, of ) / 2);
            for (var i = 0; i < len; i++) {
                var x = up.x + ( of .x - up.x) / len * i;
                var y = up.y + ( of .y - up.y) / len * i;
                var r = ur + (or - ur) / len * i;
                this.ctx.beginPath();
                this.ctx.arc(x, y, r, 0, 2 * Math.PI, true);
                this.ctx.fill();
                this.history[this.history.length - 1].push(x, y, r);
            }
        }

        Handwriting.prototype.upEvent = function (e) {
            this.moveFlag = false;
        }

        Handwriting.prototype.getXY = function (e) {
            var et = e.touches ? e.touches[0] : e;
            var x = et.clientX;
            var y = et.clientY;
            return {
                x: x - this.canvas.offsetLeft + (document.body.scrollLeft || document.documentElement.scrollLeft),
                y: y - this.canvas.offsetTop + (document.body.scrollTop || document.documentElement.scrollTop)
            }
        }

        Handwriting.prototype.distance = function (a, b) {
            var x = b.x - a.x,
                y = b.y - a.y;
            return Math.sqrt(x * x + y * y);
        }

        Handwriting.prototype.setColor = function (c) {
            this.ctx.fillStyle = c;
        }

        var hw = new Handwriting("canvasId");
        hw.setColor("rgba(0,0,210,0.4)"); //设置画笔颜色
        hw.lineMax = 10; //设置画笔最大线宽
        hw.lineMin = 4; //设置画笔最小线宽
        hw.linePressure = 1.2; //设置画笔笔触压力
        hw.smoothness = 30; //设置画笔笔触大小变化的平滑度。
    </script>
</body>

</html>

移植到小程序端

看源码大家就知道,很明显这份代码是没有办法直接移植到小程序端运行的。需要做一些改造。

拆分代码

按照微信小程序的规范,CSS,html,js文件都是拆分开放在不同的地方。因此我们第一步就是先将代码拆分出来。

对于CSS部分来说,直接放到微信小程序是没有什么问题的,只要注意将body标签的样式,转化为一个类的样式就好了。

修改代码

首先对于html文件,修改很简单,将不支持的标签替换掉就可以了。

内容:


// 原内容

<h1>手写毛笔字效果-手机版</h1>
<canvas id="canvasId" width="400" height="500"></canvas>
<br/>
<input type="button" value="全部清除" class="button" onclick="hw.clear();" />
<input type="button" value="清除最后一笔" class="button" onclick="hw.historyBack();" />

// 修改后内容

<!--pages/sign/sign.wxml-->
<view id="body">
<view>手写毛笔字效果-手机版</view>
<canvas id="canvasId" type="2d" canvas-id="canvasId" bindtouchstart="downEvent" bindtouchmove="moveEvent" bindtouchend="upEvent"></canvas>
<i-button bind:click="clear">全部清除</i-button>
<i-button bind:click="historyBack">清除最后一笔</i-button>
</view> 

首先div标签转化为view标签,button我换成了iview的按钮(为了美观),另一个值得注意的是canvas这个对象:

在微信小程序当中的canvas对象和H5里面的canvas是不一样的:

1.首先支持的版本比较靠后,因此需要注意选择基础库版本。

2.其次,其上的事件是不一样的,也需要修改。
在web版本当中,事件是在JS当中手动根据当前平台是PC还是移动来动态绑定的。在小程序中我们不需要考虑这个,因此直接写在标签上。

对于JS内容:

// 原内容

    <script type="text/javascript">
        function Handwriting(id) {
            this.canvas = document.getElementById(id);
            this.ctx = this.canvas.getContext("2d");
            var on = ("ontouchstart" in document) ? {
                start: "touchstart",
                move: "touchmove",
                end: "touchend"
            } : {
                start: "mousedown",
                move: "mousemove",
                end: "mouseup"
            };
            this.canvas.addEventListener(on.start, this.downEvent.bind(this), false);
            this.canvas.addEventListener(on.move, this.moveEvent.bind(this), false);
            this.canvas.addEventListener(on.end, this.upEvent.bind(this), false);
            this.canvas.addEventListener("contextmenu", function (e) {
                e.preventDefault()
            }, false);
            this.moveFlag = false;
            this.upof = {};
            this.radius = 0;
            this.has = [];
            this.startOf = null;
            this.lineMax = 30;
            this.lineMin = 3;
            this.linePressure = 1;
            this.smoothness = 80;
            this.history = [];
            this.setColor("rgba(0,0,0,0.25)");
        }

        Handwriting.prototype.clear = function () {
            this.history = [];
            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }

        Handwriting.prototype.historyBack = function () {
            this.history.pop();
            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
            for (var i = 0; i < this.history.length; i++) {
                var h = this.history[i];
                for (var j = 0; j < h.length; j += 3) {
                    this.ctx.beginPath();
                    this.ctx.arc(h[j], h[j + 1], h[j + 2], 0, 2 * Math.PI, true);
                    this.ctx.fill();
                }
            }
        }

        Handwriting.prototype.downEvent = function (e) {
            this.moveFlag = true;
            this.has = [];
            this.upof = this.getXY(e);
            this.startOf = this.upof;
        }

        Handwriting.prototype.moveEvent = function (e) {
            if (!this.moveFlag)
                return;
            e.preventDefault();
            var of = this.getXY(e);
            var up = this.upof;
            var ur = this.radius;
            this.has.unshift({
                time: new Date().getTime(),
                dis: this.distance(up, of )
            });
            var dis = 0;
            var time = 0;
            for (var n = 0; n < this.has.length - 1; n++) {
                dis += this.has[n].dis;
                time += this.has[n].time - this.has[n + 1].time;
                if (dis > this.smoothness)
                    break;
            }
            var or = Math.min(time / dis * this.linePressure + this.lineMin, this.lineMax) / 2;
            this.radius = or;
            this.upof = of ;
            if (dis < 7)
                return;
            if (this.startOf) {
                up = this.startOf;
                ur = or;
                this.startOf = null;
                this.history.push([]);
            }
            var len = Math.ceil(this.distance(up, of ) / 2);
            for (var i = 0; i < len; i++) {
                var x = up.x + ( of .x - up.x) / len * i;
                var y = up.y + ( of .y - up.y) / len * i;
                var r = ur + (or - ur) / len * i;
                this.ctx.beginPath();
                this.ctx.arc(x, y, r, 0, 2 * Math.PI, true);
                this.ctx.fill();
                this.history[this.history.length - 1].push(x, y, r);
            }
        }

        Handwriting.prototype.upEvent = function (e) {
            this.moveFlag = false;
        }

        Handwriting.prototype.getXY = function (e) {
            var et = e.touches ? e.touches[0] : e;
            var x = et.clientX;
            var y = et.clientY;
            return {
                x: x - this.canvas.offsetLeft + (document.body.scrollLeft || document.documentElement.scrollLeft),
                y: y - this.canvas.offsetTop + (document.body.scrollTop || document.documentElement.scrollTop)
            }
        }

        Handwriting.prototype.distance = function (a, b) {
            var x = b.x - a.x,
                y = b.y - a.y;
            return Math.sqrt(x * x + y * y);
        }

        Handwriting.prototype.setColor = function (c) {
            this.ctx.fillStyle = c;
        }

        var hw = new Handwriting("canvasId");
        hw.setColor("rgba(0,0,210,0.4)"); //设置画笔颜色
        hw.lineMax = 10; //设置画笔最大线宽
        hw.lineMin = 4; //设置画笔最小线宽
        hw.linePressure = 1.2; //设置画笔笔触压力
        hw.smoothness = 30; //设置画笔笔触大小变化的平滑度。
    </script>

// 修改后

// pages/sign/sign.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    canvas: null,
    ctx: null,
    moveFlag: false,
    upof: {},
    radius: 0,
    has: [],
    startof: null,
    lineMax: 10,
    lineMin: 4,
    linePressure: 1.2,
    smoothness: 30,
    history: []
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {
    wx.createSelectorQuery()
      .select('#canvasId')
      .fields({
        node: true,
        size: true,
      })
      .exec(this.init.bind(this))
  },
  init: function(res){
    const canvas = res[0].node
    const ctx = canvas.getContext('2d');

    this.data.canvas = canvas;
    this.data.ctx = ctx;

    const dpr = wx.getSystemInfoSync().pixelRatio
    canvas.width = res[0].width * dpr
    canvas.height = res[0].height * dpr
    ctx.scale(dpr, dpr)

    // ctx.fillRect(0, 0, 100, 100)
  },
  downEvent: function(e) {
    this.data.moveFlag = true;
    this.data.has = [];
    this.data.upof = this.getXY(e);
    this.data.startOf = this.data.upof;
  },
  getXY: function(e) {
    var et = e.touches ? e.touches[0] : e;
    var x = et.x;
    var y = et.y;
    return {
      x: x,
      y: y,
    }
  },
  distance: function(a, b) {
    var x = b.x - a.x,
      y = b.y - a.y;
    return Math.sqrt(x * x + y * y);
  },
  moveEvent: function(e) {
    if (!this.data.moveFlag){
      return;
    }
    // e.preventDefault();
    var of = this.getXY(e);
    var up = this.data.upof;
    var ur = this.data.radius;
    this.data.has.unshift({
      time: new Date().getTime(),
      dis: this.distance(up, of )
    });
    var dis = 0;
    var time = 0;
    for (var n = 0; n < this.data.has.length - 1; n++) {
      dis += this.data.has[n].dis;
      time += this.data.has[n].time - this.data.has[n + 1].time;
      if (dis > this.data.smoothness)
        break;
    }
    var or = Math.min(time / dis * this.data.linePressure + this.data.lineMin, this.data.lineMax) / 2;
    this.data.radius = or;
    this.data.upof = of ;
    if (dis < 7)
      return;
    if (this.data.startOf) {
      up = this.data.startOf;
      ur = or;
      this.data.startOf = null;
      this.data.history.push([]);
    }
    var len = Math.ceil(this.distance(up, of ) / 2);
    for (var i = 0; i < len; i++) {
      var x = up.x + ( of .x - up.x) / len * i;
      var y = up.y + ( of .y - up.y) / len * i;
      var r = ur + (or - ur) / len * i;
      this.data.ctx.beginPath();
      this.data.ctx.arc(x, y, r, 0, 2 * Math.PI, true);
      this.data.ctx.fillStyle = '#1aad19'
      this.data.ctx.strokeStyle = 'rgba(1,1,1,0)'
      this.data.ctx.fill()
      this.data.ctx.stroke()
      this.data.history[this.data.history.length - 1].push(x, y, r);
    }
  },
  upEvent: function (e) {
    this.data.moveFlag = false;
  },
  clear: function () {
    this.data.history = [];
    this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
  },
  historyBack: function () {
    this.data.history.pop();
    this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
    for (var i = 0; i < this.data.history.length; i++) {
      var h = this.data.history[i];
      for (var j = 0; j < h.length; j += 3) {
        this.data.ctx.beginPath();
        this.data.ctx.arc(h[j], h[j + 1], h[j + 2], 0, 2 * Math.PI, true);
        this.data.ctx.fill();
      }
    }
  },
})

首先对于canvas的一些属性,原web是用一个类(function)来模拟,而在微信小程序端,我们把它都放到data里面去,然后再将方法里对这些数据的引用也修改一下。同时小程序中并没有document对象,所有用到document对象的地方替换成小程序中相同的获取方式。

完成后的样子

这只是一个测试的Demo,因此十分丑陋大家可以根据自己的需要再完善:

sign-weixin.gif

微信端源代码:

wxml:

// sign.wxml

<!--pages/sign/sign.wxml-->
<view id="body">
<view>手写毛笔字效果-手机版</view>
<canvas id="canvasId" type="2d" canvas-id="canvasId" bindtouchstart="downEvent" bindtouchmove="moveEvent" bindtouchend="upEvent"></canvas>
<i-button bind:click="clear">全部清除</i-button>
<i-button bind:click="historyBack">清除最后一笔</i-button>
</view> 

js:

// sign.js

// pages/sign/sign.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    canvas: null,
    ctx: null,
    moveFlag: false,
    upof: {},
    radius: 0,
    has: [],
    startof: null,
    lineMax: 10,
    lineMin: 4,
    linePressure: 1.2,
    smoothness: 30,
    history: []
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {
    wx.createSelectorQuery()
      .select('#canvasId')
      .fields({
        node: true,
        size: true,
      })
      .exec(this.init.bind(this))
  },
  init: function(res){
    const canvas = res[0].node
    const ctx = canvas.getContext('2d');

    this.data.canvas = canvas;
    this.data.ctx = ctx;

    const dpr = wx.getSystemInfoSync().pixelRatio
    canvas.width = res[0].width * dpr
    canvas.height = res[0].height * dpr
    ctx.scale(dpr, dpr)

    // ctx.fillRect(0, 0, 100, 100)
  },
  downEvent: function(e) {
    this.data.moveFlag = true;
    this.data.has = [];
    this.data.upof = this.getXY(e);
    this.data.startOf = this.data.upof;
  },
  getXY: function(e) {
    var et = e.touches ? e.touches[0] : e;
    var x = et.x;
    var y = et.y;
    return {
      x: x,
      y: y,
    }
  },
  distance: function(a, b) {
    var x = b.x - a.x,
      y = b.y - a.y;
    return Math.sqrt(x * x + y * y);
  },
  moveEvent: function(e) {
    if (!this.data.moveFlag){
      return;
    }
    // e.preventDefault();
    var of = this.getXY(e);
    var up = this.data.upof;
    var ur = this.data.radius;
    this.data.has.unshift({
      time: new Date().getTime(),
      dis: this.distance(up, of )
    });
    var dis = 0;
    var time = 0;
    for (var n = 0; n < this.data.has.length - 1; n++) {
      dis += this.data.has[n].dis;
      time += this.data.has[n].time - this.data.has[n + 1].time;
      if (dis > this.data.smoothness)
        break;
    }
    var or = Math.min(time / dis * this.data.linePressure + this.data.lineMin, this.data.lineMax) / 2;
    this.data.radius = or;
    this.data.upof = of ;
    if (dis < 7)
      return;
    if (this.data.startOf) {
      up = this.data.startOf;
      ur = or;
      this.data.startOf = null;
      this.data.history.push([]);
    }
    var len = Math.ceil(this.distance(up, of ) / 2);
    for (var i = 0; i < len; i++) {
      var x = up.x + ( of .x - up.x) / len * i;
      var y = up.y + ( of .y - up.y) / len * i;
      var r = ur + (or - ur) / len * i;
      this.data.ctx.beginPath();
      this.data.ctx.arc(x, y, r, 0, 2 * Math.PI, true);
      this.data.ctx.fillStyle = '#1aad19'
      this.data.ctx.strokeStyle = 'rgba(1,1,1,0)'
      this.data.ctx.fill()
      this.data.ctx.stroke()
      this.data.history[this.data.history.length - 1].push(x, y, r);
    }
  },
  upEvent: function (e) {
    this.data.moveFlag = false;
  },
  clear: function () {
    this.data.history = [];
    this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
  },
  historyBack: function () {
    this.data.history.pop();
    this.data.ctx.clearRect(0, 0, this.data.canvas.width, this.data.canvas.height);
    for (var i = 0; i < this.data.history.length; i++) {
      var h = this.data.history[i];
      for (var j = 0; j < h.length; j += 3) {
        this.data.ctx.beginPath();
        this.data.ctx.arc(h[j], h[j + 1], h[j + 2], 0, 2 * Math.PI, true);
        this.data.ctx.fill();
      }
    }
  },
})

wxss:

/* pages/sign/sign.wxss */

#canvasId {
  width: 100%;
  height: 500px;
  background-color: #ffd;
}

#body {
  margin: 0;
  padding: 0;
  text-align: center;
  background-color: #936;
}

注意有坑:

  1. canvas标签在微信当中必须要有canvas-id属性,否则这个标签会被自动隐藏。

  2. 注意调试基础库,如果版本过低,不支持canvas标签还没啥显著提示。

  3. 打开真机调试无法调试canvas,但是预览中可以生鲜。调试时它不会生效。这并不仅仅是我自己的问题,论坛里同样有人在问,不知道此时是否已经解决。

上一篇下一篇

猜你喜欢

热点阅读