如何在前端截取包含用户上传图像的 div?

Posted

技术标签:

【中文标题】如何在前端截取包含用户上传图像的 div?【英文标题】:How to take screenshot of the div that contain user Uploaded images in frontend? 【发布时间】:2018-03-21 12:56:57 【问题描述】:

请查看此代码。在以下代码中,用户可以上传图片,他们可以移动、调整大小、旋转上传的图片等。

$(function() 
  var inputLocalFont = $("#user_file");
  inputLocalFont.change(previewImages);

  function previewImages() 
    var fileList = this.files;
    var anyWindow = window.URL || window.webkitURL;

    for (var i = 0; i < fileList.length; i++) 
      var objectUrl = anyWindow.createObjectURL(fileList[i]);
      var $newDiv = $("<div>", 
        class: "img-div"
      );
      var $newImg = $("<img>", 
        src: objectUrl,
        class: "newly-added"
      ).appendTo($newDiv);
      $(".new-multiple").append($newDiv);
      $newDiv.draggable();
      $newDiv.rotatable();
      $newDiv.resizable(
        aspectRatio: true,
        handles: "ne, nw, e, se, sw, w"
      );
      $newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
      window.URL.revokeObjectURL(fileList[i]);
    
    $(".newly-added").on("click", function(e) 
      $(".newly-added").removeClass("img-selected");
      $(this).addClass("img-selected");
      e.stopPropagation()
    );

    $(document).on("click", function(e) 
      if ($(e.target).is(".newly-added") === false) 
        $(".newly-added").removeClass("img-selected");
      
    );
  
   $(".user_submit").on("click",function(e)
   e.preventDefault();
   html2canvas($('.new-multiple'), 
     allowTaint: true,
	  onrendered: function(canvas) 
		document.body.appendChild(canvas);
		
);
  
  
  );

);
.new-multiple 
  width: 400px !important;
  height: 400px !important;
  background: white;
  border: 2px solid red;
  overflow: hidden;


.img-div 
  width: 200px;
  height: 200px;


.newly-added 
  width: 100%;
  height: 100%;


.img-selected 
  box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
  border: 2px solid rgb(145, 44, 94);



/*
.ui-resizable-handle.ui-resizable-se.ui-icon.ui-icon-gripsmall-diagonal-se 
  background-color: white;
  border: 1px solid tomato;

*/

.ui-resizable-handle 
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  width: 14px;
  height: 14px;


.ui-resizable-nw 
  top: -7px;
  left: -7px;


.ui-resizable-ne 
  top: -7px;
  right: -7px;


.ui-resizable-e 
  top: calc(50% - 7px);
  right: -7px;


.ui-resizable-w 
  top: calc(50% - 7px);
  left: -7px;


.ui-resizable-sw 
  bottom: -7px;
  left: -7px;


.ui-resizable-se 
  right: -7px;
  bottom: -7px;


.ui-resizable-se.ui-icon 
  display: none;


.ui-rotatable-handle 
  background-size: 14px;
  background-repeat: no-repeat;
  background-position: center;
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  margin-left: calc(50% - 9px);
  bottom: -5px;
  width: 18px;
  height: 18px;
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://cdn.jsdelivr.net/gh/godswearhats/jquery-ui-rotatable@1.1/jquery.ui.rotatable.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/godswearhats/jquery-ui-rotatable@1.1/jquery.ui.rotatable.min.js"></script>
 <script src="https://html2canvas.hertzen.com/build/html2canvas.js"></script>

<form method="post" action="">


<input name="user_file[]" id="user_file" style="position: relative;overflow: hidden" multiple="" type="file">

<div class="new-multiple"></div>
<input type="submit" name="submit" class="user_submit" value="submit" />
</form>

https://jsfiddle.net/s99kxydw/15/

上传图片后,旋转,移动,然后用户将按下提交按钮。那个时候我们需要生成窗口的截图,这样用户和我们都可以了解用户做了什么改变。这是我们的要求。

我们如何生成这个截图?

我们找到了一种解决方案。那是 html2canvas https://html2canvas.hertzen.com/

但问题是 html2canvas 不支持 css transform 属性

因此旋转没有出现在屏幕截图中。我们怎么能克服这个。请检查代码。

有没有其他不使用html2canvas的方法?

【问题讨论】:

您需要使用一个库来生成图像并将旋转的大小报告给流以生成相同的图像。您可以将 lib GD2 用于 php 或 Imagemagic ... 正如@headmax 提到的,您需要将更改的详细信息与图像一起传递给图像处理程序,并允许该处理程序根据新细节将图像处理成新图像。我怀疑可能有一些脚本可以做到这一点。 html2canvas 可能不是最好的方法,但它可以完成这项工作。 【参考方案1】:

我了解您的问题,因为我对 html2canvas 做了类似的事情。它的问题。是它无法保存所有内容,因此它可能不完全准确,例如它无法进行 css 文本剪辑。这对我有用(我已经下载了图像,但您可以轻松保存它,查看此链接了解如何执行此操作):

html2canvas($('.classOfElementToSave'), 
      allowTaint: true,
      onrendered: function(canvas) 
        var dataURL = canvas.toDataURL();
        $.ajax(
          type: "POST",
          url: "script.php",
          data:  
             imgBase64: dataURL
          
        ).done(function(o) 
          console.log('saved'); 
          // If you want the file to be visible in the browser 
          // simply return the url previously saved
    );
      
  );

然后在您的 script.php 或文件(或任何您的文件)中:

$img = $_POST['data'];
$img = str_replace('data:image/png;base64,', '', $img); //Because saved as a data image
$img = str_replace(' ', '+', $img);
$fileData = base64_decode($img);
//saving the image to server
$fileName = 'image.png';
file_put_contents($fileName, $fileData);

【讨论】:

嗨我试过这个,而不是 $img = $_POST['data'];我用 $img = $_POST['dataURL'];它正在生成屏幕截图并保存在服务器中。但问题是旋转的图像在屏幕截图中没有旋转 试试这样的:var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); var rectWidth = 150; var rectHeight = 75; // translate context to center of canvas context.translate(canvas.width / 2, canvas.height / 2); // rotate 45 degrees clockwise context.rotate(Math.PI / 4); context.fillStyle = 'blue'; context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight); github.com/niklasvh/html2canvas/issues/209 这也可能有帮助:github.com/niklasvh/html2canvas/issues/220【参考方案2】:

这只是一半的答案。正如我所评论的,另一半将取决于您使用什么类型的脚本来生成新图像。

这半部分展示了如何捕捉所有不同的细节。假设您的红色框是新图像的视口,这将在您进行更改时收集详细信息,以便您可以将它们传递给将构建图像的脚本。

我假设选择捕获以下详细信息:

文件名 文件大小 尺寸 位置 旋转

如果需要,您可以删除文件名并让用户指定名称值。

HTML

<form method="post" action="">
  <button id="browse-btn">Browse Images</button>
  <input name="user_file[]" id="user_file" style="display: none; position: relative;overflow: hidden" multiple="" type="file" />
  <div class="new-multiple"></div>
  <button id="submit-btn" type="submit">Submit</button>
  <div class="meta-details">
    <ul>
      <li>
        <label>Name:</label>
        <span></span>
      </li>
      <li>
        <label>Size:</label>
        <span></span>
      </li>
      <li>
        <label>Width:</label>
        <span></span>
      </li>
      <li>
        <label>Height:</label>
        <span></span>
      </li>
      <li>
        <label>Top:</label>
        <span></span>
      </li>
      <li>
        <label>Left:</label>
        <span></span>
      </li>
      <li>
        <label>Rotation:</label>
        <span></span>
      </li>
    </ul>
  </div>
</form>

CSS

form button 
  margin: 3px;


.new-multiple 
  width: 400px !important;
  height: 400px !important;
  background: white;
  border: 2px solid #faa;
  border-radius: 3px;
  overflow: hidden;


.img-div 
  width: 200px;
  height: 200px;


.newly-added 
  width: 100%;
  height: 100%;


.img-selected 
  box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
  border: 2px solid rgb(145, 44, 94);


.ui-resizable-handle 
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  width: 14px;
  height: 14px;


.ui-resizable-nw 
  top: -7px;
  left: -7px;


.ui-resizable-ne 
  top: -7px;
  right: -7px;


.ui-resizable-e 
  top: calc(50% - 7px);
  right: -7px;


.ui-resizable-w 
  top: calc(50% - 7px);
  left: -7px;


.ui-resizable-sw 
  bottom: -7px;
  left: -7px;


.ui-resizable-se 
  right: -7px;
  bottom: -7px;


.ui-resizable-se.ui-icon 
  display: none;


.ui-rotatable-handle 
  background-size: 14px;
  background-repeat: no-repeat;
  background-position: center;
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  margin-left: calc(50% - 9px);
  bottom: -5px;
  width: 18px;
  height: 18px;


.meta-details ul 
  padding: 0;
  margin: 0;
  list-style: none;
  font-family: Arial, sans-serif;
  font-size: 9px;


.meta-details ul li label 
  display: inline-block;
  width: 45px;

JavaScript

$(function() 
  var inputLocalFont = $("#user_file");
  inputLocalFont.change(previewImages);

  function humanFileSize(bytes, si) 
    var thresh = si ? 1000 : 1024;
    if (Math.abs(bytes) < thresh) 
      return bytes + ' B';
    
    var units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
    var u = -1;
    do 
      bytes /= thresh;
      ++u;
     while (Math.abs(bytes) >= thresh && u < units.length - 1);
    return bytes.toFixed(1) + ' ' + units[u];
  

  function logMeta(d) 
    var $m = $(".meta-details ul li span");
    $m.eq(0).html(d.name);
    $m.eq(1).html(humanFileSize(d.size));
    $m.eq(2).html(d.width + " px");
    $m.eq(3).html(d.height + " px");
    $m.eq(4).html(d.top + " px");
    $m.eq(5).html(d.left + " px");
    $m.eq(6).html(d.rotateDeg + " &deg;");
  

  function previewImages() 
    var fileList = this.files;
    var fileMeta = [];
    $.each(fileList, function(key, val) 
      fileMeta[key] = 
        name: val.name,
        size: val.size,
        modified: val.lastModified
      ;
    );
    var anyWindow = window.URL || window.webkitURL;

    for (var i = 0; i < fileList.length; i++) 
      var $list = fileList[i];
      var $meta = fileMeta[i];
      var objectUrl = anyWindow.createObjectURL(fileList[i]);
      var $newDiv = $("<div>", 
        class: "img-div"
      );
      var $newImg = $("<img>", 
        src: objectUrl,
        class: "newly-added"
      ).appendTo($newDiv);
      $meta['width'] = $newImg.width();
      $meta['height'] = $newImg.height();
      $meta['rotateDeg'] = 0.000;
      $meta['top'] = $newImg.position().top;
      $meta['left'] = $newImg.position().left;
      $(".new-multiple").append($newDiv);
      $newDiv.draggable(
        drag: function(e, ui) 
          $meta['top'] = ui.position.top;
          $meta['left'] = ui.position.left;
          logMeta($meta);
          $newImg.data("meta", $meta);
        
      );
      $newDiv.rotatable(
        rotate: function(e, ui) 
          $meta['rotateDeg'] = Math.round(ui.angle.degrees * 10000) / 10000;
          $meta['rotateRad'] = ui.angle.current;
          logMeta($meta);
          $newImg.data("meta", $meta);
        
      );
      $newDiv.resizable(
        aspectRatio: true,
        handles: "ne, nw, e, se, sw, w",
        resize: function(e, ui) 
          $meta['width'] = ui.size.width;
          $meta['height'] = ui.size.height;
          logMeta($meta);
          $newImg.data("meta", $meta);
        
      );
      $newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
      window.URL.revokeObjectURL(fileList[i]);
      console.log($meta);
      logMeta($meta);
      $newImg.data("meta", $meta);
    
    $(".newly-added").on("click", function(e) 
      $(".newly-added").removeClass("img-selected");
      $(this).addClass("img-selected");
      e.stopPropagation();
    );

    $(document).on("click", function(e) 
      if ($(e.target).is(".newly-added") === false) 
        $(".newly-added").removeClass("img-selected");
      
    );
  
  $("button").button();
  $("#browse-btn").click(function(e) 
    e.preventDefault();
    $("#user_file").trigger("click");
  );
  $("#browse-btn").click(function(e) 
    e.preventDefault();
    $(this).parent().submit();
  );
  $("form").submit(function(e) 
    e.preventDefault();
    console.log("Prepared Meta Data:");
    $(".newly-added").each(function() 
      console.log($(this).data("meta"));
    );
    // AJAX Post Code will be entered here
  );
);

首先引用 @mpen 这里来自 converting file size in bytes to human-readable string 的文件大小转换功能。

您可以看到我们创建了一个数组来存储与文件关联的相应详细信息。这会随着项目被拖动、调整大小或旋转而更新。您可以在提交表单时将这些详细信息与原始图像一起提交。所以至少你的用户界面现在已经构建好了。

您的下一步将是查看您希望如何根据这些详细信息构建和保存图像。所以开始查看Image Processing 的 PHP。查看您要使用哪个并从该后端脚本开始。

【讨论】:

但是朋友,这段代码与我的代码不同。请提供一个使用与我们的小提琴相同的 html 结构的示例。 同样在 html2canvas 中,问题是它不支持-> 变换:旋转。有什么方法可以在不使用 ->transform: rotate 的情况下旋转 div @abilasher HTML 更改很小,如果您愿意,可以忽略。正如我在帖子中提到的,您需要使用更复杂的图像处理器进行调查。 要添加到 Twisty 的答案,您可以使用这个 PHP 库来选择性地呈现您想要的内容:github.com/mikehaertl/phpwkhtmltopdf。它基于 webkit,可以渲染所有内容,包括你的 CSS 转换。因此,1) 按照 Twisty 的建议捕获转换,2) 读取提交脚本中的转换并重现图像 3) 使用 wkhtmlto\Image 捕获结果。【参考方案3】:

html2canvas 是否 支持大多数 css 属性(基本属性除外),其中之一是 transform,您可能已经知道,也有 > 不幸的是,解决方法(使用 html2canvas)

但是,您可以使用一个名为 FabricJSjavascript 画布库,它似乎最适合您的目的,例如操作 (移动、调整大小、旋转等) 用户上传的图像(s).

使用这个库最好的部分是,您不需要使用 html2canvas 或任何其他额外的库来截取屏幕截图。您可以直接将画布 (截图) 保存为图像,因为 FabricJS 本质上是一个画布库。

这是一个basic example 证明:

var canvas = new fabric.Canvas('myCanvas', 
   backgroundColor: 'white'
);

function renderImage(e) 
   var imgUrl = URL.createObjectURL(e.target.files[0]);
   fabric.Image.fromURL(imgUrl, function(img) 
      // set default props
      img.set(
         width: 150,
         height: 150,
         top: 75,
         left: 75,
         transparentCorners: false
      );
      canvas.add(img);
      canvas.renderAll();
   );


function saveOnPC() 
   var link = document.createElement('a');
   link.href = canvas.toDataURL();
   link.download = 'myImage.png';
   link.click();


function saveOnServer() 
   $.post('https://your-site-name.com/save-image.php', 
      data: canvas.toDataURL()
   , function() 
      console.log('Image saved on server!');
   );

   /* use the following PHP code for 'save-image.php' on server-side

   <? php
   $img = $_POST['data'];
   $img = str_replace('data:image/png;base64,', '', $img);
   $img = str_replace(' ', '+', $img);
   $fileData = base64_decode($img);
   $fileName = 'myImage.png';
   file_put_contents($fileName, $fileData);
	
   */
canvasborder:1px solid redinputmargin-bottom:6pxbuttonmargin:10px 3px 0 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<input type="file" onchange="renderImage(event)">
<canvas id="myCanvas"  ></canvas>
<button onclick="saveOnPC()">Save on PC</button>
<button onclick="saveOnServer()">Save on Server</button>

要了解有关 FabricJS 库的更多信息,请参阅它Official Documentation。

【讨论】:

谢谢。但重要的一点是我没有使用 。我只是在使用 divs 。我会检查fabric js是否也支持div。 hm.. 我知道,但您不应该将 div 用于此类任务(图像处理)。一个更好和更有效的方法是使用 canvas,fabric.js 是专门为这种目的而制作的。研究它,你会发现..顺便说一句,fabric.js 不支持 div,正如我所说的它是一个画布库。 是的,朋友。你说的是对的。但不幸的是我们不能使用 canvas 。这就是我们使用 html2 canvas 的原因。我们想要一个针对当前代码结构的解决方案 嗯.. 但是使用画布有什么问题?你只需要用 fabric.js 来实现你正在使用 jQuery 做的事情。这应该不会太难。 对不起朋友。这项任务几乎完成了许多功能。现在只有待处理的部分是截图。这就是为什么我们不能改变。这包含许多代码。【参考方案4】:

您尝试过 rasterizeHTML 吗?

我会引用它:

出于安全原因,将 HTML 渲染到画布中受到严重限制...但是可以通过将 HTML 作为 a 嵌入到 SVG 图像中,然后通过 ctx.drawImage() 绘制结果图像。

你可以在 Github 上找到这个项目,他们解释了如何使用它:

https://github.com/cburgmer/rasterizeHTML.js

【讨论】:

【参考方案5】:

在@ValfarDeveloper 之后,您可以将&lt;img&gt;.src 的值分配给data URI 而不是Blob URL,并将".new-multiple" 的当前HTML 设置为@ 内的&lt;foreignObject&gt; 元素987654329@ 字符串。

$(function() 
  var inputLocalFont = $("#user_file");
  inputLocalFont.change(previewImages);

  async function previewImages() 
    var fileList = this.files;
    var anyWindow = window.URL || window.webkitURL;

    for (var i = 0; i < fileList.length; i++) 
      var objectUrl = await new Promise(resolve => 
        var reader = new FileReader;
        reader.onload = e => resolve(reader.result);
        reader.readAsDataURL(fileList[i]);
      );
      var $newDiv = $("<div>", 
        class: "img-div"
      );
      var $newImg = $("<img>", 
        src: objectUrl,
        class: "newly-added"
      ).appendTo($newDiv);
      $(".new-multiple").append($newDiv);
      $newDiv.draggable();
      $newDiv.rotatable();
      $newDiv.resizable(
        aspectRatio: true,
        handles: "ne, nw, e, se, sw, w"
      );
      $newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
    
    $(".newly-added").on("click", function(e) 
      $(".newly-added").removeClass("img-selected");
      $(this).addClass("img-selected");
      e.stopPropagation()
    );

    $(document).on("click", function(e) 
      if ($(e.target).is(".newly-added") === false) 
        $(".newly-added").removeClass("img-selected");
      
    );
  
   $(".user_submit").on("click",function(e)
   e.preventDefault();
   let html = $(".new-multiple").html();
   let svg = `<?xml version="1.0" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg"   viewBox="0 0 400 300">
    <foreignObject   
     requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
      <html xmlns="http://www.w3.org/1999/xhtml">
      $html
      </html>
      </foreignObject>
</svg>`;
  
    $("body").append(svg);
  
  );

);
.new-multiple 
  width: 400px !important;
  height: 400px !important;
  background: white;
  border: 2px solid red;
  overflow: hidden;


.img-div 
  width: 200px;
  height: 200px;


.newly-added 
  width: 100%;
  height: 100%;


.img-selected 
  box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
  border: 2px solid rgb(145, 44, 94);



/*
.ui-resizable-handle.ui-resizable-se.ui-icon.ui-icon-gripsmall-diagonal-se 
  background-color: white;
  border: 1px solid tomato;

*/

.ui-resizable-handle 
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  width: 14px;
  height: 14px;


.ui-resizable-nw 
  top: -7px;
  left: -7px;


.ui-resizable-ne 
  top: -7px;
  right: -7px;


.ui-resizable-e 
  top: calc(50% - 7px);
  right: -7px;


.ui-resizable-w 
  top: calc(50% - 7px);
  left: -7px;


.ui-resizable-sw 
  bottom: -7px;
  left: -7px;


.ui-resizable-se 
  right: -7px;
  bottom: -7px;


.ui-resizable-se.ui-icon 
  display: none;


.ui-rotatable-handle 
  background-size: 14px;
  background-repeat: no-repeat;
  background-position: center;
  border: 0;
  border-radius: 50%;
  background-color: #00CCff;
  margin-left: calc(50% - 9px);
  bottom: -5px;
  width: 18px;
  height: 18px;
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://cdn.jsdelivr.net/gh/godswearhats/jquery-ui-rotatable@1.1/jquery.ui.rotatable.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/godswearhats/jquery-ui-rotatable@1.1/jquery.ui.rotatable.min.js"></script>
 <script src="https://html2canvas.hertzen.com/build/html2canvas.js"></script>

<form method="post" action="">


<input name="user_file[]" id="user_file" style="position: relative;overflow: hidden" multiple="" type="file">

<div class="new-multiple"></div>
<input type="submit" name="submit" class="user_submit" value="submit" />
</form>

【讨论】:

真的谢谢你的朋友。这就是我要找的。非常感谢 由于某种原因,当我将此代码复制到 jsfiddle 时,旋转未显示。你能检查一下这个jsfiddle.net/mkyshemq .rotatable() 未定义,见console 好的。但是为什么某些图像部分没有出现在屏幕截图中。可能是我的代码错误,请您更新小提琴。 谢谢你的朋友。请看截图awesomescreenshot.com/image/2908405/…

以上是关于如何在前端截取包含用户上传图像的 div?的主要内容,如果未能解决你的问题,请参考以下文章

当包含它的 DIV 类的大小正确时,如何纠正不成比例地拉伸的图像?

如何在上传前调整图像大小并进行预览

如何使用 jQuery 客户端上传图像并将其添加到 div?

如何使用 C# 和 ASP.NET 从网页中截取 div?

如何在 Bootstrap 3 div 中放置图像?

如何检测用户何时在浏览器当前选项卡视口中放置图像?