【无标题】前端电子签名的canvas画板

页面引入vue-esign

<template>
  <div class="esigns"> 
   <div class="rightText" @click="handleReset">重新书写</div> 
    <div style="width: 100%; margin: auto 0">
      <cvue-esign
        ref="esign"
        class="cav"
        :isCrop="isCrop"
        :lineWidth="lineWidth"
        :lineColor="lineColor"
        :bgColor.sync="bgColor"
        :height="cavHeight"
      ></cvue-esign>
    </div>
  </div>
</template>

<script>
// 下载依赖  npm i vue-esign --save
export default {
  data() {
    return {
      lineWidth: 6,
      lineColor: "#000000",
      bgColor: "#F9FAF9",
      resultImg: "",
      isCrop: false,
      cavHeight: 1280,   //  画板高度
    };
  },
  methods: {
    handleReset() {
      // 清除
      this.$refs.esign.reset();
    },
    handleGenerate() {
      _this.$refs.esign   // 电子签名  需要转换成base64
    },
  },
};
</script>

<style scoped>
body {
  position: fixed;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  background-color: rgb(51, 51, 51);
  overflow: hidden;
}
#app {
  width: 560px;
  height: 320px;
}
@media screen and (orientation: portrait) {
  #app {
    position: absolute;
    width: 100vh;
    height: 100vw;
    top: 0;
    left: 100vw;
    -webkit-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
    transform: rotate(90deg);
    transform-origin: 0% 0%;
  }
}
@media screen and (orientation: landscape) {
  #app {
    position: absolute;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    -webkit-transform: rotate(-90deg);
    -moz-transform: rotate(-90deg);
    -ms-transform: rotate(-90deg);
    transform: rotate(-90deg);
    transform-origin: 0% 0%;
  }
}
.esigns {
  height: 100%;
  /*background-color: #ffffff;*/
}
.tip {
  text-align: center;
  line-height: 60px;
  height: 60px;
  font-size: 16px;
  background-color: #fff;
  /* color: cadetblue; */
  font-weight: 500;
  display: flex;
}
.btn {
  /* display: flex;
  justify-content: space-around;
  margin-top: 10px; */
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
}
.cav {
  width: 90%;
  margin: auto;
  border: aliceblue;
}
.foot-btn {
  width: 45%;
  background: -webkit-gradient(
    linear,
    right top,
    left top,
    from(#ff3d00),
    to(#ff8c00)
  );
  background: -o-linear-gradient(right, #ff3d00 0%, #ff8c00 100%);
  background: linear-gradient(270deg, #ff3d00 0%, #ff8c00 100%);
  font-size: 0.36rem;
  color: #fff;
  height: 1rem;
  line-height: 1rem;
}
.foot-btn-reset {
  width: 45%;
  /*background: -webkit-gradient(linear, right top, left top, from(#FF3D00), to(#ff8c00));*/
  /*background: -o-linear-gradient(right, #FF3D00 0%, #ff8c00 100%);*/
  /*background: linear-gradient(270deg, #FF3D00 0%, #ff8c00 100%);*/
  font-size: 0.36rem;
  color: #666666;
  height: 1rem;
  line-height: 1rem;
  background-color: #ffffff;
}
.leftText{
  margin-left: .4rem;
  color: #1d1d1d;
}
</style>

vue-esign的页面书写

<template>
  <canvas
    ref="canvas"
    @mousedown="mouseDown"
    @mousemove="mouseMove"
    @mouseup="mouseUp"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend="touchEnd"
  ></canvas>
</template>

<script>
export default {
  name:'vue-esign',
  props: {
    width: {
      type: Number,
      default: 800,
    },
    height: {
      type: Number,
      default: 300,
    },
    lineWidth: {
      type: Number,
      default: 4,
    },
    lineColor: {
      type: String,
      default: "#000000",
    },
    bgColor: {
      type: String,
      default: "",
    },
    isCrop: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      hasDrew: false,
      resultImg: "",
      points: [],
      canvasTxt: null,
      startX: 0,
      startY: 0,
      isDrawing: false,
      sratio: 1,
    };
  },
  computed: {
    ratio() {
      return this.height / this.width;
    },
    stageInfo() {
      return this.$refs.canvas.getBoundingClientRect();
    },
    myBg() {
      return this.bgColor ? this.bgColor : "rgba(255, 255, 255)";
    },
  },
  watch: {
    myBg: function (newVal) {
      this.$refs.canvas.style.background = newVal;
    },
  },
  beforeMount() {
    window.addEventListener("resize", this.$_resizeHandler);
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.$_resizeHandler);
  },
  mounted() {
    const canvas = this.$refs.canvas;
    canvas.height = this.height;
    canvas.width = this.width;
    canvas.style.background = this.myBg;
    this.$_resizeHandler();
    // 在画板以外松开鼠标后冻结画笔
    document.onmouseup = () => {
      this.isDrawing = false;
    };
  },
  methods: {
    $_resizeHandler() {
      const canvas = this.$refs.canvas;
      canvas.style.width = this.width + "px";
      const realw = parseFloat(window.getComputedStyle(canvas).width);
      canvas.style.height = this.ratio * realw + "px";
      this.canvasTxt = canvas.getContext("2d");
      this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio);
      this.sratio = realw / this.width;
      this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio);
    },
    // pc
    mouseDown(e) {
      e = e || event;
      e.preventDefault();
      this.isDrawing = true;
      this.hasDrew = true;
      let obj = {
        x: e.offsetX,
        y: e.offsetY,
      };
      this.drawStart(obj);
    },
    mouseMove(e) {
      e = e || event;
      e.preventDefault();
      if (this.isDrawing) {
        let obj = {
          x: e.offsetX,
          y: e.offsetY,
        };
        this.drawMove(obj);
      }
    },
    mouseUp(e) {
      e = e || event;
      e.preventDefault();
      let obj = {
        x: e.offsetX,
        y: e.offsetY,
      };
      this.drawEnd(obj);
      this.isDrawing = false;
    },
    // mobile
    touchStart(e) {
      e = e || event;
      e.preventDefault();
      this.hasDrew = true;
      if (e.touches.length === 1) {
        let obj = {
          x:
            e.targetTouches[0].clientX -
            this.$refs.canvas.getBoundingClientRect().left,
          y:
            e.targetTouches[0].clientY -
            this.$refs.canvas.getBoundingClientRect().top,
        };
        this.drawStart(obj);
      }
    },
    touchMove(e) {
      e = e || event;
      e.preventDefault();
      if (e.touches.length === 1) {
        let obj = {
          x:
            e.targetTouches[0].clientX -
            this.$refs.canvas.getBoundingClientRect().left,
          y:
            e.targetTouches[0].clientY -
            this.$refs.canvas.getBoundingClientRect().top,
        };
        this.drawMove(obj);
      }
    },
    touchEnd(e) {
      e = e || event;
      e.preventDefault();
      if (e.touches.length === 1) {
        let obj = {
          x:
            e.targetTouches[0].clientX -
            this.$refs.canvas.getBoundingClientRect().left,
          y:
            e.targetTouches[0].clientY -
            this.$refs.canvas.getBoundingClientRect().top,
        };
        this.drawEnd(obj);
      }
    },
    // 绘制
    drawStart(obj) {
      this.startX = obj.x;
      this.startY = obj.y;
      this.canvasTxt.beginPath();
      this.canvasTxt.moveTo(this.startX, this.startY);
      this.canvasTxt.lineTo(obj.x, obj.y);
      this.canvasTxt.lineCap = "round";
      this.canvasTxt.lineJoin = "round";
      this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
      this.canvasTxt.stroke();
      this.canvasTxt.closePath();
      this.points.push(obj);
    },
    drawMove(obj) {
      this.canvasTxt.beginPath();
      this.canvasTxt.moveTo(this.startX, this.startY);
      this.canvasTxt.lineTo(obj.x, obj.y);
      this.canvasTxt.strokeStyle = this.lineColor;
      this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
      this.canvasTxt.lineCap = "round";
      this.canvasTxt.lineJoin = "round";
      this.canvasTxt.stroke();
      this.canvasTxt.closePath();
      this.startY = obj.y;
      this.startX = obj.x;
      this.points.push(obj);
    },
    drawEnd(obj) {
      this.canvasTxt.beginPath();
      this.canvasTxt.moveTo(this.startX, this.startY);
      this.canvasTxt.lineTo(obj.x, obj.y);
      this.canvasTxt.lineCap = "round";
      this.canvasTxt.lineJoin = "round";
      this.canvasTxt.stroke();
      this.canvasTxt.closePath();
      this.points.push(obj);
      this.points.push({ x: -1, y: -1 });
    },
    // 操作
    generate() {
      const pm = new Promise((resolve, reject) => {
        if (!this.hasDrew) {
          reject(`Warning: Not Signned!`);
          return;
        }
        var resImgData = this.canvasTxt.getImageData(
          0,
          0,
          this.$refs.canvas.width,
          this.$refs.canvas.height
        );
        this.canvasTxt.globalCompositeOperation = "destination-over";
        this.canvasTxt.fillStyle = this.myBg;
        this.canvasTxt.fillRect(
          0,
          0,
          this.$refs.canvas.width,
          this.$refs.canvas.height
        );
        this.resultImg = this.$refs.canvas.toDataURL();
        var resultImg = this.resultImg;
        this.canvasTxt.clearRect(
          0,
          0,
          this.$refs.canvas.width,
          this.$refs.canvas.height
        );
        this.canvasTxt.putImageData(resImgData, 0, 0);
        this.canvasTxt.globalCompositeOperation = "source-over";
        if (this.isCrop) {
          const crop_area = this.getCropArea(resImgData.data);
          var crop_canvas = document.createElement("canvas");
          const crop_ctx = crop_canvas.getContext("2d");
          crop_canvas.width = crop_area[2] - crop_area[0];
          crop_canvas.height = crop_area[3] - crop_area[1];
          const crop_imgData = this.canvasTxt.getImageData(...crop_area);
          crop_ctx.globalCompositeOperation = "destination-over";
          crop_ctx.putImageData(crop_imgData, 0, 0);
          crop_ctx.fillStyle = this.myBg;
          crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
          resultImg = crop_canvas.toDataURL();
          crop_canvas = null;
        }
        resolve(resultImg);
      });
      return pm;
    },
    reset() {
      this.canvasTxt.clearRect(
        0,
        0,
        this.$refs.canvas.width,
        this.$refs.canvas.height
      );
      this.$emit("update:bgColor", "");
      this.$refs.canvas.style.background = this.bgColor
        ? this.bgColor
        : "rgba(255, 255, 255)";
      this.points = [];
      this.hasDrew = false;
      this.resultImg = "";
    },
    getCropArea(imgData) {
      var topX = this.$refs.canvas.width;
      var btmX = 0;
      var topY = this.$refs.canvas.height;
      var btnY = 0;
      for (var i = 0; i < this.$refs.canvas.width; i++) {
        for (var j = 0; j < this.$refs.canvas.height; j++) {
          var pos = (i + this.$refs.canvas.width * j) * 4;
          if (
            imgData[pos] > 0 ||
            imgData[pos + 1] > 0 ||
            imgData[pos + 2] ||
            imgData[pos + 3] > 0
          ) {
            btnY = Math.max(j, btnY);
            btmX = Math.max(i, btmX);
            topY = Math.min(j, topY);
            topX = Math.min(i, topX);
          }
        }
      }
      topX++;
      btmX++;
      topY++;
      btnY++;
      const data = [topX, topY, btmX, btnY];
      return data;
    },
  },
};
</script>

<style scoped>
canvas {
  max-width: 100%;
  display: block;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值