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

How to take screenshot of the div that contain user Uploaded images in frontend?

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

$(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/

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

How we can generate this screenshot ?

我们找到了一种解决方案。即html2canvashttps://html2canvas.hertzen.com/

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

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

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

我明白你的问题是什么,因为我用 html2canvas 做过类似的事情。它的问题。是它不能保存所有内容,所以它可能不完全准确,例如它不能进行 css 文本剪辑。这对我有用(我用它下载了图片,但你也可以很容易地保存它,看看这个 link 如何做到这一点):

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);

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

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

我假定了捕获以下详细信息的选项:

  • 文件名
  • 文件大小
  • 维度
  • 位置
  • 旋转

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

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 here from converting file size in bytes to human-readable string 文件大小转换功能。

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

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

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

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

使用此库的最佳部分是,您无需使用 html2canvas 或任何其他附加库来截取屏幕截图。您可以直接将 canvas (截屏) 保存为图像,因为 FabricJS 本质上是一个 canvas 库。

这是一个 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);
 
   */
}
canvas{border:1px solid red}input{margin-bottom:6px}button{margin: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" width="300" height="300"></canvas>
<button onclick="saveOnPC()">Save on PC</button>
<button onclick="saveOnServer()">Save on Server</button>

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

您尝试过 rasterizeHTML 吗?

我会引用它:

For security reasons rendering HTML into a canvas is severly limited... However it is possible by embedding the HTML into an SVG image as a and then drawing the resulting image via ctx.drawImage().

您可以在 Github 上找到该项目,他们在那里解释了如何使用它:

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

在@ValfarDeveloper 之后,您可以将 <img>.src 的值分配给 data URI 而不是 Blob URL 并设置当前的 HTML 的 ".new-multiple"<svg> 字符串中的 <foreignObject> 元素处。

$(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" width="400px" height="400px" viewBox="0 0 400 300">
    <foreignObject width="400px" height="300px" 
     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>