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 = " ";
        }
        if("\n" == item ){
          item = "
"
; } 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 = " "; } if("\n" == item ){ item = "
"
; } 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 = " "; } if("\n" == item ){ item = "
"
; } 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>

效果

代码

你可能感兴趣的:(Vue,vue.js,前端,javascript)