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

news2024/11/23 3:02:31

文章目录

  • 环境
  • 实现
  • 效果
  • 代码

环境

安装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>



效果

在这里插入图片描述

代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/781677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Mycat2】关于序列功能的一个 Bug

创建序列前 Mycat 的 sequences/ 目录情况&#xff1a; 创建一个 MySQL 生成方式的序列。 /* mycat:setSequence{"name":"sharding_db_not_tb111","clazz":"io.mycat.plug.sequence.SequenceMySQLGenerator"} */;因为没有官方文档支…

字符函数和字符串函数下篇(详解)

❤️ 作者简介 &#xff1a;RO-BERRY 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识&#xff0c;对纯音乐有独特的喜爱 &#x1f4d7; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;如果你也感兴趣的话欢迎关注博主&#xff0c;期待更新 字符函数和字符串函数2 1…

DevOps自动化平台开发之 Shell脚本执行的封装

基础知识 基于如下技术栈开发DevOps平台 Spring Boot Shell Ansible Git Gitlab Docker K8S Vue 1、spring boot starter的封装使用 2、Shell脚本的编写 3、Ansible 脚本的编写 4、Docker 的使用与封装设计 本篇介绍如何使用Java封装Linux命令和Shell脚本的使用 将其设计成…

【Datawhale夏令营】任务一学习笔记

目录 一&#xff1a;anaconda的环境配置 二&#xff1a;赛题任务解读 2.1 任务要求 2.2 数据集介绍 2.3 评估指标 三&#xff1a;机器学习之 LightGBM 一&#xff1a;anaconda的环境配置 下载Anaconda&#xff1a;访问Anaconda官方网站&#xff08;https://www.anaconda.…

SQL篇-04_SQL进阶挑战-01_增删改操作

插入记录 SQL110 插入记录&#xff08;一&#xff09; 描述 牛客后台会记录每个用户的试卷作答记录到exam_record表&#xff0c;现在有两个用户的作答记录详情如下&#xff1a;用户1001在2021年9月1日晚上10点11分12秒开始作答试卷9001&#xff0c;并在50分钟后提交&#xff…

【算法基础:数学知识】4.3 欧拉函数

文章目录 欧拉函数定义性质 例题列表873. 欧拉函数&#xff08;使用质因数分解求一个数的欧拉函数&#xff09;原理讲解&#xff08;公式推导&#xff09;⭐解法代码 874. 筛法求欧拉函数&#xff08;求 1 ~ n 中所有数字的欧拉函数&#xff09;⭐ 欧拉函数 https://oi-wiki.o…

安装及配置zabbix_agent代理端(监控FTP服务器)

监控agent的linux主机我们在之前的文章里已经做好了 现在直接安装ftp服务即可 [rootagent ~]# yum install -y vsftpd[rootagent ~]# systemctl start vsftpd #启动ftp服务[rootagent ~]# systemctl enable vsftpd #设置ftp服务开机自启 Created symlink fro…

codec2play流程总结

Codec2.0(C2)是android系统为vendor提供的用于实现video/audio/filter模块的的HAL层接口API&#xff0c;vendor可用这个API实现他们自己的HAL层&#xff0c;Codec2.0是用于替换现有的OMX-IL。 数据流程 C2LinearBlock创建share ptr类型block&#xff0c;fetchLinearBlock对blo…

【每日一题Day274】LC42接雨水 | 单调栈

接雨水【LC42】[面试常见] 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 按列求贡献&#xff1a;枚举 首先确定按行计算雨水&#xff0c;还是按列确定雨水 按行计算&#xff1a; 按列计算&#xff1…

SpringBoot之jackson之复杂XML和Object互转、泛型、传参/接参

引入依赖 <!-- lombok插件 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- jackson xml 转换工具 --><dependency><…

自然语言处理实战项目13-基于GRU模型与NER的关键词抽取模型训练全流程

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目13-基于GRU模型与NER的关键词抽取模型训练全流程。本文主要介绍关键词抽取样例数据、GRU模型模型构建与训练、命名实体识别(NER)、模型评估与应用&#xff0c;项目的目标是通过训练一个GRU模型…

crmeb部署

安装宝塔 教程 安装所需要的软件 php mysql5.7 redis fileinfo nginx 安装crmeb 重启mysql 前台http://192.168.216.128/ 后台http://192.168.216.128/admin admin admin888登录 访问前台

GOF 代理模式

1.需求 &#xff08;1&#xff09;&#xff1a;在程序中&#xff0c;对象A和对象B无法直接交互时。 &#xff08;2&#xff09;&#xff1a;在程序中&#xff0c;功能需要增强时。 &#xff08;3&#xff09;&#xff1a;在程序中&#xff0c;目标需要被保护时 代理模式中有一…

使用html和css技巧提升网站加载速度

使用html和css技巧提升网站加载速度 加载时间每增加一秒&#xff08;0-5 秒之间&#xff09;&#xff0c;网站转化率平均就会下降 4.42%。页面加载时间的前五秒对转化率的影响最大。 通过更改html和css文件可以提高网站的页面加载速度,本文现在就来介绍一下怎么实现。 延迟加载…

51单片机--AT24C02数据存储

文章目录 存储器的介绍AT24C02I2C总线I2C时序结构AT24C02数据帧AT24C02数据存储实例 存储器的介绍 存储器是计算机系统中的一种重要设备&#xff0c;用于存储程序和数据&#xff0c;它可以通过电子、磁性介质等技术来记录和保持数据。在这里&#xff0c;主要介绍的是随机存储器…

Java SPI机制:扩展Java应用的灵活性与可插拔性

文章目录 引言1. Java SPI机制简介2. Java SPI的工作原理2.1. 定义服务接口2.2. 编写服务提供者2.3. 创建SPI配置文件2.4. 使用Service Loader加载服务2.5. 客户端代码调用服务 3. 实例演示HelloEnService .javaHelloZhServiceImpl .javaMETA-INF/services/com.gpj.spi.HelloSe…

Blazor前后端框架Known-V1.2.6

V1.2.6 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazor…

kubesphere安装中间件

kubesphere安装mysql 创建configMap [client] default-character-setutf8mb4[mysql] default-character-setutf8mb4[mysqld] init_connectSET collation_connection utf8mb4_unicode_ci init_connectSET NAMES utf8mb4 character-set-serverutf8mb4 collation-serverutf8mb4_…

Nuxt 菜鸟入门学习笔记一:介绍与安装

文章目录 介绍 Introduction自动化和惯例服务器端渲染服务器引擎生产就绪模块化架构 安装 Installation准备安装 Nuxt官网地址&#xff1a; https://nuxt.com/ 介绍 Introduction Nuxt 是一个免费的开源框架&#xff0c;以直观和可扩展的方式使用 Vue.js 创建类型安全、高性能…