vue实现仿手写稿样式,可导出成png图片

环境

安装html2canvas,用于将指定标签下的全部子节点转换为图片

 npm install html2canvas

实现

<template>
  <div class="handwrite">
    <div id="left" class="left">
      <div id="backImg" class="backImg" :style="{ backgroundImage: `url(${defaultImgUrl})` }">
        <div id="textContent" class="textContent">

        </div>
      </div>
    </div>
    <div class="right">
      <el-input type="textarea" :autosize="{minRows: 20, maxRows: 20 }" resize="none" v-model="text"></el-input>
      <div class="setting">
        <el-row>
          <el-col :span="12"> <label>颜色: </label> <input type="number" v-model="R" min="0" max="255"> <input type="number" v-model="G" min="0" max="255"> <input type="number" v-model="B" min="0" max="255"> </el-col>
          <el-col :span="12"> <label>默认背景: </label>
            <el-radio-group v-model="defaultImgUrl">
              <el-radio :label="item.value" v-for="(item,index) in defaultImgOptions" :key="index">{{item.label}}</el-radio>
            </el-radio-group>
          </el-col>
        </el-row>
        <br>
        <el-row>
          <el-col :span="12"> <label>字体: </label>
            <!--            <input id="fontFile" type="file" name="fontFile" accept=".ttf,.tff" @change="" />-->
            <el-radio-group v-model="fontType">
              <el-radio :label="item.value" v-for="(item,index) in fontOptions" :key="index">{{item.label}}</el-radio>
            </el-radio-group>
          </el-col>
          <el-col :span="12"> <label>背景图: </label> <input id="imgFile" type="file" name="imgFile" accept="image/png, image/jpeg, image/jpg" @change="selectImg" /> </el-col>
        </el-row>
        <br>
        <el-row>
          <el-col :span="8"> <label>字体水平间距: </label> <input type="number" v-model="level" min="-10" max="50"></el-col>
          <el-col :span="8"> <label>字体竖直间距: </label> <input type="number" v-model="vertical" min="10" max="50"> </el-col>
          <el-col :span="8"> <label>字体大小: </label> <input type="number" v-model="size" min="-10" max="50"> </el-col>
        </el-row>
        <br>
        <el-row>
          <el-col :span="6"> <label>左间距: </label> <input type="number" v-model="leftPitch" min="0" max="30"></el-col>
          <el-col :span="6"> <label>右间距: </label> <input type="number" v-model="rightPitch" min="0" max="30"> </el-col>
          <el-col :span="6"> <label>上间距: </label> <input type="number" v-model="topPitch" min="0" max="30"> </el-col>
          <el-col :span="6"> <label>下间距: </label> <input type="number" v-model="bottomPitch" min="0" max="30"> </el-col>
        </el-row>
        <br>
        <el-row>
          <el-col :span="6"> <label>文字旋转角度: </label> <input type="number" v-model="rotaAngle" min="-90" max="90"></el-col>
          <el-col :span="6"> <label>背景旋转角度: </label> <input type="number" v-model="backRotaAngle" min="-90" max="90"></el-col>
        </el-row>
        <br>
        <el-row>
          <el-button @click="exportImg" style="margin-left: 80%">导出图片</el-button>
        </el-row>
      </div>
    </div>

  </div>

</template>

<script >
import html2canvas from "html2canvas";

export default {
  name: "handwrite",
  data() {
    return {
      text: "",

      R: 0,
      G: 0,
      B: 0,
      defaultImgUrl: require('@/assets/handwrite/a4.jpg'),
      backImgUrl: "",
      level: 0,
      vertical: 10,
      size: 15,

      leftPitch: 0,
      rightPitch: 0,
      topPitch: 0,
      bottomPitch: 0,

      rotaAngle: 0,
      backRotaAngle: 0,

      defaultImgOptions: [
        {
          label: "A4",
          value: require('@/assets/handwrite/a4.jpg'),
        },
        {
          label: "A4-横线(红)",
          value: require('@/assets/handwrite/a4-line.jpg'),
        },
        {
          label: "A4-横线(黑)",
          value: require('@/assets/handwrite/a4-line-black.jpg'),
        },
        {
          label: "牛皮纸",
          value: require('@/assets/handwrite/niupi.jpeg'),
        },
        {
          label: "信签纸",
          value: require('@/assets/handwrite/xz.jpeg'),
        },
      ],

      fontType: "caoshu",
      fontOptions: [
        {
          label: "字体1",
          value: "XingKai",
        },
        {
          label: "字体2",
          value: "caoshu",
        },
        {
          label: "字体3",
          value: "XingShu",
        },
      ]
    }
  },
  computed: {},
  mounted() {

  },
  watch: {
    //文本
    text(){
      var textArry = this.text.split("") || [];
      if(this.isEmpty(textArry) || textArry.length <= 0){
        return ;
      }
      let textContent = document.getElementById("textContent");
      while(textContent.hasChildNodes()) //当div下还存在子节点时 循环继续
      {
        textContent.removeChild(textContent.firstChild);
      }
      let number = 0;
      textArry.forEach((item) => {
        var span = document.createElement("span");
        if(" " == item || ' ' == item ){
          item = "&emsp;";
        }
        if("\n" == item ){
          item = "<br>";
        }
        span.innerHTML = item;
        if(this.size >=15 ){
          span.style.fontSize = Math.floor(Math.random() * (this.size - (this.size-3) + 1)) + (this.size-3)+"px";
        }
        span.style.transform = "rotate("+(Math.floor(Math.random() * (5 - (-5) + 1)) + (-5))+"deg)";
        if(number%5 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%3 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%9 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        span.style.fontFamily = this.fontType;
        number = number +1;
        textContent.appendChild(span);
      });
    },
    //RGB颜色
    R(){
      let textContent = document.getElementById("textContent");
      let rgb = "rgb("+this.R+","+this.G+","+this.B+")";
      textContent.style.color = this.colorHex(rgb);
    },
    //RGB颜色
    G(){
      let textContent = document.getElementById("textContent");
      let rgb = "rgb("+this.R+","+this.G+","+this.B+")";
      textContent.style.color = this.colorHex(rgb);
    },
    //RGB颜色
    B(){
      let textContent = document.getElementById("textContent");
      let rgb = "rgb("+this.R+","+this.G+","+this.B+")";
      textContent.style.color = this.colorHex(rgb);
    },
    //文字水平间距
    level(){
      let textContent = document.getElementById("textContent");
      textContent.style.letterSpacing = this.level+"px";
    },
    //文字竖直间距
    vertical(){
      let textContent = document.getElementById("textContent");
      textContent.style.lineHeight = this.vertical+"px";
    },
    //文字大小
    size(){
      let textContent = document.getElementById("textContent");
      textContent.style.fontSize = this.size+"px";

      var textArry = this.text.split("") || [];
      if(this.isEmpty(textArry) || textArry.length <= 0){
        return ;
      }
      while(textContent.hasChildNodes()) //当div下还存在子节点时 循环继续
      {
        textContent.removeChild(textContent.firstChild);
      }
      let number = 0;
      textArry.forEach((item) => {
        var span = document.createElement("span");
        if(" " == item || ' ' == item ){
          item = "&emsp;";
        }
        if("\n" == item ){
          item = "<br>";
        }
        span.innerHTML = item;
        if(this.size >=15 ){
          span.style.fontSize = Math.floor(Math.random() * (this.size - (this.size-3) + 1)) + (this.size-3)+"px";
        }
        span.style.transform = "rotate("+(Math.floor(Math.random() * (5 - (-5) + 1)) + (-5))+"deg)";
        if(number%5 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%3 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%9 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        span.style.fontFamily = this.fontType;
        number = number +1;
        textContent.appendChild(span);
      });
    },
    //字体
    fontType(){
      var textArry = this.text.split("") || [];
      if(this.isEmpty(textArry) || textArry.length <= 0){
        return ;
      }
      let textContent = document.getElementById("textContent");
      while(textContent.hasChildNodes()) //当div下还存在子节点时 循环继续
      {
        textContent.removeChild(textContent.firstChild);
      }
      let number = 0;
      textArry.forEach((item) => {
        var span = document.createElement("span");
        if(" " == item || ' ' == item ){
          item = "&emsp;";
        }
        if("\n" == item ){
          item = "<br>";
        }
        span.innerHTML = item;
        if(this.size >=15 ){
          span.style.fontSize = Math.floor(Math.random() * (this.size - (this.size-3) + 1)) + (this.size-3)+"px";
        }
        span.style.transform = "rotate("+(Math.floor(Math.random() * (5 - (-5) + 1)) + (-5))+"deg)";
        if(number%5 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%3 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        if(number%9 == 0){
          span.style.marginLeft = (Math.floor(Math.random() * (3 - (0) + 1)) + (0)) + "px";
        }
        span.style.fontFamily = this.fontType;
        number = number +1;
        textContent.appendChild(span);
      });
    },
    //左边距
    leftPitch(){
      let textContent = document.getElementById("textContent");
      textContent.style.paddingLeft = this.leftPitch+"px";
    },
    //右边距
    rightPitch(){
      let textContent = document.getElementById("textContent");
      textContent.style.paddingRight = this.rightPitch+"px";
    },
    //上边距
    topPitch(){
      let textContent = document.getElementById("textContent");
      textContent.style.paddingTop = this.topPitch+"px";
    },
    //下边距
    bottomPitch(){
      let textContent = document.getElementById("textContent");
      textContent.style.paddingBottom = this.bottomPitch+"px";
    },
    //字体旋转角
    rotaAngle(){
      let textContent = document.getElementById("textContent");
      textContent.style.transform = "rotate("+this.rotaAngle+"deg)";
    },
    //背景旋转角
    backRotaAngle(){
      let backImg = document.getElementById("backImg");
      backImg.style.transform = "rotate("+this.backRotaAngle+"deg)";
    }
  },
  methods: {
    //选择背景图
    selectImg(){
      //生成的临时url
      debugger
      const url = URL.createObjectURL(document.querySelector('#imgFile').files[0]);
      this.defaultImgUrl = url;
    },
    //导出图片
    exportImg(){
      html2canvas(document.querySelector("#backImg")
        ,{
          //高度和宽度 背景色
          // width: 75,
          // height: 75,
          // backgroundColor: "#00ff00",
          //使用 scale 属性可以修改渲染时的放大倍数(默认为 1),将其调大可以解决低分辨率设备下生成的图片模糊问题。
          scale: 2,
          //指定渲染的 Canvas 如果页面上原先就有个 canvas 元素,我们希望可以将图片绘制在它上面,可以使用 canvas 属性设置。
          // canvas: document.querySelector("#myCanvas")
        }
      ).then(canvas => {
        // 将canvas转换成img的src流
        // var imgUrl = canvas.toDataURL("image/png");
        // console.log("base64编码数据:", imgUrl);
        // 此方法可以设置截图质量(0-1)
        // var imgUrl = canvas.toDataURL("image/png", 1);
        // console.log("base64编码数据:", imgUrl);
        //将canvas内容保存为文件并下载
        canvas.toBlob(function(blob) {
          saveAs(blob, "手写稿.png");
        });
      });

    },
    /**
     *
     * @param 校验是否为空
     */
    isEmpty(obj) {
      if (obj == undefined || obj == null || obj == '' || obj == "" ) {
        return true
      }
      return false
    },

    /**
     * RGB转换为16进制
     * @returns {string}
     */
    colorHex(color) {
      // RGB颜色值的正则
      var reg = /^(rgb|RGB)/;
      if (reg.test(color)) {
        var strHex = "#";
        // 把RGB的3个数值变成数组
        var colorArr = color.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",");
        // 转成16进制
        for (var i = 0; i < colorArr.length; i++) {
          var hex = Number(colorArr[i]).toString(16);
          if (hex === "0") {
            hex += hex;
          }
          strHex += hex;
        }
        return strHex;
      } else {
        return String(color);
      }
    },

    /**
     * 16进制转换为RGB
     * @returns {string}
     */
    colorRgb(color) {
      // 16进制颜色值的正则
      var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
      // 把颜色值变成小写
      var color = color.toLowerCase();
      if (reg.test(color)) {
        // 如果只有三位的值,需变成六位,如:#fff => #ffffff
        if (color.length === 4) {
          var colorNew = "#";
          for (var i = 1; i < 4; i += 1) {
            colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
          }
          color = colorNew;
        }
        // 处理六位的颜色值,转为RGB
        var colorChange = [];
        for (var i = 1; i < 7; i += 2) {
          colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
        }
        return "RGB(" + colorChange.join(",") + ")";
      } else {
        return color;
      }
    }

  }
}
</script>


<style scoped lang="scss">
.handwrite{
  width: 100%;
  height: 100%;


  .left{
    background-color: #c01111;
    float: left;
    margin-left: 3%;
    width: 45%;
    height: 98vh;
    padding: 5px;
    margin-bottom: 10px;

    .backImg{
      width: 100%;
      height: 100%;
      -moz-background-size:100% 100%;
      background-size:100% 100%;
    }

    .textContent {
      margin-top: 0%;
    }
  }

  .right{
    background-color: #1ab394;
    float: left;
    margin-left: 3%;
    width: 45%;
    height: 98vh;
    padding: 5px;
    margin-bottom: 10px;

    .setting{
      margin-top: 5px;
    }
  }
}


</style>



效果

在这里插入图片描述

代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值