如何自动下载画布图像?

Posted

技术标签:

【中文标题】如何自动下载画布图像?【英文标题】:How can I automatically download canvas images? 【发布时间】:2020-07-03 05:48:37 【问题描述】:

我正在开发一个小型卡片创建器。它背后的想法是从 CSV 中获取数据,相应地从文件夹中获取一些图像,将它们与相关文本(名称、权力、奖金等)一起放入画布中,然后将其保存为图像(如果文件名会很棒= 卡名.jpg)。但我实际上坚持让下载正常工作。主要问题是我尝试的每个解决方案都给我一个空白的 jpeg,没有我填充的画布的痕迹。第二个问题是我不希望下载事件被按钮触发。只需选择 CSV,上传它,让代码为每张卡片创建一个画布,然后将每张卡片下载到同一个文件夹中,无需任何确认。有可能吗?

CSV 结构很简单,只有很多行具有相同的模式: 部落名称,卡牌名称,1,1,1,卡牌强度,卡牌奖励,全名

Papaparse 库由我从他们的网站获得的两个文件处理,因为我没有找到任何 CDN。

我是初学者,所以答案越简单越好。重要的一点是将每个 img 保存为同一文件夹中的 jpg。 (例如:C:\Users\MyName\Desktop\CardCollectionFolder)

代码只需要运行一次,只是为了在我自己的计算机上离线和创建 jpeg 格式的大量卡片。因此,内部没有安全问题。

这是我的代码(HTML/CSS + JS/JQuery)

$( document ).ready(function() 

  function loadImages(sources, callback) 
      var images = ;
      var loadedImages = 0;
      var numImages = 0;
      // get num of sources
      for(var src in sources) 
        numImages++;
      
      for(var src in sources) 
        images[src] = new Image();
        images[src].onload = function() 
          if(++loadedImages >= numImages) 
            callback(images);
          
        ;
        images[src].src = sources[src];
      

    

    function creaCartaFinale(results, i)
        

      var canvas = document.getElementById("myCanvas"+i);
      var context = canvas.getContext('2d');
      
        var clan=(results.data[i][0]);
        const nome =(results.data[i][1]); 
        var rarita =(results.data[i][2]);
        var stelle =(results.data[i][3]);
        var potenza =(results.data[i][4]);
        var danno =(results.data[i][5]);
        const potere =(results.data[i][6]);
        const bonus=(results.data[i][7]);
        var nomeintero=(results.data[i][8]);
    
        imgStelle = "Immagini/LivelloCarte/livello"+ stelle +".png";
        imgClan= "Immagini/IconeClan/"+ clan +"_42.png";
        if (rarita=="Non comune")
            imgRarita = "Immagini/RaritaCarta/raritanoncomune.png";
         else 
            imgRarita = "Immagini/RaritaCarta/rarita"+rarita+".png";
        
    
        immagine ="Immagini/ImmaginiChar/"+clan+"_"+nomeintero+"_N"+stelle+"_HD_673.png"
    
        var sources = 
            image1: 'Immagini/SfondoCarte/base.png',
            image2:'Immagini/SfondoCarte/riquadro.png',
            image3: immagine,
            image4:'Immagini/SfondoCarte/poteri.png',
            image5: imgRarita,
            image6: imgStelle,
            image7: 'Immagini/SfondoCarte/riquadrobis.png',
            image8: imgClan,
            image9: 'Immagini/Numeri/'+potenza+'.png',
            image10:'Immagini/Numeri/'+danno+'.png',
          ;
    
        
          loadImages(sources, function(images) 
            context.drawImage(images.image1, 0, 0,  2220 , 3240);  //SFONDO *   x - y
            context.drawImage(images.image2, 80, 80,  2040 ,1900);//RIQUADRO
            context.drawImage(images.image3, 0, 0,  2220 ,3150);  //IMMAGINE *
            context.drawImage(images.image4, 80, 2000,  2050 ,1150);  //LAYOUT POTERI *
            context.drawImage(images.image5, 520, 80,  1610 ,200);   //RARITA *
            context.drawImage(images.image6, 600, 2000,  1240 ,260);  //STELLE *
            context.drawImage(images.image7, 100, 70,  360 ,360);  //RIQUADRO CLAN *
            context.drawImage(images.image8, 120, 80,  320 ,320);  //CLAN *
            context.drawImage(images.image9, 170, 2350,  130 ,170);  //FORZA *
            context.drawImage(images.image10, 170, 2950,  130 ,170);  //DANNO *
    
            //STAMPA NOME
            context.font = "200px Calibri";
            var gradient = context.createLinearGradient (0,0, canvas.width,0)
            gradient.addColorStop("0", "white")
            context.fillStyle = gradient;
            context.fillText(nome, 600, 250);
    
    
            //STAMPA POTERI E BONUS
            context.font = "105px Calibri";
            if (potere.length > 34)
              var ultimoIndice = potere.length -1;
              var frase30 = potere.slice(0,33);
              var n = frase30.lastIndexOf(" ");
              var primaFrase = potere.slice(0,n)
              var secondaFrase = potere.slice(n,ultimoIndice)
              context.fillText(primaFrase, 750, 2500);
              context.fillText(secondaFrase, 750, 2620);
    
            
            else
               context.fillText(potere, 750, 2500);
            
            if (bonus.length > 34)
              var ultimoIndice = bonus.length -1;
              var frase30 = bonus.slice(0,33);
              var n = frase30.lastIndexOf(" ");
              var primaFrase = bonus.slice(0,n)
              var secondaFrase = bonus.slice(n,ultimoIndice)
              context.fillText(primaFrase, 750, 2850);
              context.fillText(secondaFrase, 750, 2970);
            
            else
              context.fillText(bonus,750, 2850);
            
    
          );
    


  document.getElementById('txtFileUpload').addEventListener('change', upload, false);
  
  function upload(evt) 
      var file = evt.target.files[0];
      var reader = new FileReader();
      reader.readAsText(file);
      reader.onload = function (event) 
          var csvData = event.target.result;
          Papa.parse(csvData, 
              complete: function (results) 
                  console.log(results.data);
                  var numeroCarte=(results.data.length);
                  for(var i=0; i<numeroCarte; i++)
                    $( ".cartaSingola" ).prepend( "<canvas class='cardCanvas' id='myCanvas"+i+"' width='2500' height='3500'></canvas>" );
                    creaCartaFinale(results,i);
                    download_image(i);
                  
              
          );
      ;
  

  //THIS FUNCTION DOES NOT WORK, JUST GIVES BACK A BLANK FILE
  function download_image(i)
    var canvas = document.getElementById("myCanvas"+i);
    image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
    var link = document.createElement('a');
    link.download = "my-image.png";
    link.href = image;
    link.click();
  
      

);
/* .contenitoreCarte
    height:88mm;
    width:63mm;
 */

.cartabase
    margin:15px;
    height:88mm;
    width:63mm;


.nomeCarta
    position: relative;
    top: -352px;
    right: -102px;
    width:45mm;
    height:6mm;
    text-align: center;
    z-index:1;
    


.nomeCarta p
    text-align: center;
    width: 100%;
    left: -32px;
    position: relative;


.potereCarta
    position: relative;
    top: -352px;
    right: -102px;
    width:45mm;
    height:6mm;
    text-align: center;

.potereCarta p
    text-align: center;
    
    position: relative;
    bottom: -194px;
    width: 82%;
    left: -6px;
    word-wrap: break-word

.bonusCarta
    position: relative;
    top: -352px;
    right: -102px;
    width:45mm;
    height:6mm;
    text-align: center;

.bonusCarta p
    text-align: center;
    width: 82%;
    left: -6px;
    bottom: -195px;
    position: relative;
    word-wrap: break-word


.forzaCarta
    position: relative;
    top: -352px;
    right: -102px;
    width:45mm;
    height:6mm;
    text-align: center;

.forzaCarta p
    text-align: center;
    width: 100%;
    left: -148px;
    bottom: -119px;
    position: relative;


.dannoCarta
    position: relative;
    top: -352px;
    right: -100px;
    width:45mm;
    height:6mm;
    text-align: center;

.dannoCarta p
    text-align: center;
    width: 100%;
    left: -146px;
    bottom: -139px;
    position: relative;


.livelloCarta
    /* width: 32mm; */
    width: 42mm; 
    height: 7mm;
    right: -36px;
    top: -166px;
    position: relative;


.clanCarta
    height: 9mm;
    width: 9mm;
    right: -15px;
    top: -278px;
    position: relative;
    z-index: 1;


.raritaCarta
    height: 6mm;
    width: 45mm;
    right: -35px;
    top: -296px;
    position: relative;


.baseCarta
    height: 88mm;
    width: 63mm;
    right: -7px;
    top: -312px;
    position: relative;
    z-index: -99;

 .riquadroCarta
    height: 50mm;
    width: 56mm;
    right: -14px;
    top: -473px;
    position: relative;


.poteriCarta
    height: 32mm;
    width: 58mm;
    right: -12px;
    top: -470px;
    position: relative;
    z-index: -99;


.immaginiCarta
    height: 92mm;
    width: 68mm;
    right: -3px;
    top: -642px;
    position: relative;
    z-index: 1;



.cardCanvas
    position: relative;
    display: block;
<html>

  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />

    <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"type="text/css" />

    <link rel="stylesheet" href="Pagina.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="Pagina.js"></script> 
    <script src="papaparse.js"></script> 
    <script src="papaparse.min.js"></script> 

    <title>Card maker</title>                             
  
  </head>


  <body>

    <input type="file" name="File Upload" id="txtFileUpload" accept=".csv" />

    <div class="cartaSingola">
       
    </div>

  </body>

</html>

【问题讨论】:

您不能使用 javascript 编写文件(这将是一场彻底的安全噩梦) 非常肯定它会,但这意味着在我自己的电脑上离线运行一次,以创建一些有趣的卡片。由于代码是用 JS 编写的,并且在保存 jpeg 之前可以正常工作,所以我没有看到任何问题。 请查看***.com/questions/17311645/… 我认为您希望保持 MIME 类型不变。 【参考方案1】:

考虑以下几点:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

// draw cloud
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.bezierCurveTo(430, 40, 370, 30, 340, 50);
context.bezierCurveTo(320, 5, 250, 20, 250, 50);
context.bezierCurveTo(200, 5, 150, 20, 170, 80);
context.closePath();
context.lineWidth = 5;
context.fillStyle = '#8ED6FF';
context.fill();
context.strokeStyle = '#0000ff';
context.stroke();

function download_image(trg) 
  var canvas = document.getElementById(trg);
  var dataUrl = canvas.toDataURL("image/png");
  var link = document.createElement('a');
  link.download = "my-image.png";
  link.href = dataUrl.replace("image/png", "image/octet-stream");
  console.log(link);
  link.click();


download_image("myCanvas");
&lt;canvas id="myCanvas" width="578" height="200" style="display:none;"&gt;&lt;/canvas&gt;

无法用您的代码为您测试。

【讨论】:

不起作用,我上传了 CSV,图像已创建,但代码到达函数后,它会返回错误“找不到文件” @GianlucaFontana 经过一番研究后更新。 ***.com/questions/8126623/… 本帖的第二种解决方案是自动下载,但同样的问题是空白jpg。 我试图将回调的位置交换到该函数。如果我在 loadimages 调用中调用函数,嵌套在函数 creaCartaFinale 中,我会收到空​​白图像或消息“可能无法导出受污染的画布” @GianlucaFontana 无法复制。当我在答案中运行脚本时,我会下载并保存为 PNG 而不是 JPG。然后我可以在下载后正确打开它。你需要做一个最小的、可重现的例子:***.com/help/minimal-reproducible-example

以上是关于如何自动下载画布图像?的主要内容,如果未能解决你的问题,请参考以下文章

Fabric Js - 是不是可以将图像对象自动缩放到画布上?

使用画布自动调整背景图像大小

如何在 Robot Framework 中比较两个图像

Javascript Canvas迭代地将图像自动保存到文件夹

如何从数据库 INSIDE 数据表中下载画布图像

绘制后如何下载不同的html画布?