我应该如何使用jcrop在客户端裁剪图像并上传它?

Gov*_*are 12 javascript css jquery image jcrop

我想要做的是使用html fileupload控件选择图像,使用jcrop裁剪选择,在客户端裁剪图像并上传图像.这里是小提琴链接https://jsfiddle.net/govi20/spmc7ymp/

Id用于代码: jcrop图像的

目标
图片 fileupload控件
预览画布预览
clear_selection按钮清除所选区域

设置jcrop.

<script src="./js/jquery.min.js"></script>
<script src="./js/jquery.Jcrop.js"></script>
<script src="./js/jquery.color.js"></script>
Run Code Online (Sandbox Code Playgroud)

清除选择.

<script type="text/javascript">

jQuery(function($){

var api;

$('#target').Jcrop({
  // start off with jcrop-light class
  bgOpacity: 0.5,
  keySupport: false,
  bgColor: 'black',
  minSize:[240,320],
  maxSize:[480,640],
  onChange : updatePreview,
  onSelect : updatePreview, 
  height:160,
  width:120,
  addClass: 'jcrop-normal'
},function(){
  api = this;
  api.setSelect([0,0,240,320]);
  api.setOptions({ bgFade: true });
  api.ui.selection.addClass('jcrop-selection');
  });

});
Run Code Online (Sandbox Code Playgroud)

在jcrop中显示上传的文件

jQuery('#clear_selection').click(function(){
  $('#target').Jcrop({    

      setSelect: [0,0,0,0],
    });
});
Run Code Online (Sandbox Code Playgroud)

裁剪图像并将其分配给画布(从stackoverflow上的一个线程获取此图像)

function readURL(input) {

    if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
            $('#target').attr('src', e.target.result);
            setProperties();       
        }
        reader.readAsDataURL(input.files[0]);
    }
}

function setProperties(){
   $('#target').Jcrop({         
              setSelect: [0,0,240,320]
        }); 
}
$("#photograph").change(function(){
    readURL(this);     
});
Run Code Online (Sandbox Code Playgroud)

我面临的问题:

  1. updatePreview函数未在选择时调用,因此没有可见的画布.
  2. 裁剪选择不可拖动(我使用bootstrap css).
  3. Canvas是HTML5元素,这意味着客户端应该具有html5兼容的浏览器,所以我该如何删除这个条件.

sea*_*pip 18

这是基本的html 5代码:

https://jsfiddle.net/zm7e0jev/

此代码裁剪图像,显示预览并将输入元素的值设置为base64编码的裁剪图像.

您可以通过以下方式在php中获取图像文件:

//File destination
$destination = "/folder/cropped_image.png";
//Get convertable base64 image string
$image_base64 = $_POST["png"];
$image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
$image_base64 = str_replace(" ", "+", $image_base64);
//Convert base64 string to image data
$image = base64_decode($image_base64);
//Save image to final destination
file_put_contents($destination, $image);
Run Code Online (Sandbox Code Playgroud)

提交base64图像字符串作为post变量具有服务器发布大小限制和base64编码使得裁剪后的图像文件大小更大(~33%),然后裁剪图像的原始数据将使上载花费更长时间.

设置帖子大小限制:帖子请求的大小限制是多少?

请记住,例如,DoS攻击可能会滥用增加的邮件大小限制.

相反,我建议将base64裁剪图像转换为数据blob,然后将其作为文件提交到表单中:

https://jsfiddle.net/g3ysk6sf/

然后你可以通过以下方式在php中获取图像文件:

//File destination
$destination = "/folder/cropped_image.png";
//Get uploaded image file it's temporary name
$image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
//Move temporary file to final destination
move_uploaded_file($image_tmp_name, $destination);
Run Code Online (Sandbox Code Playgroud)

更新:

FormData()仅在IE10中得到部分支持,在旧版本的IE中不受支持

因此,我建议将base64字符串作为后备发送,但这会导致较大图像出现问题,因此需要检查文件大小并在图像高于特定大小时显示错误弹出窗口.

当我使用它时,我将使用下面的回退代码发布更新.

更新2:

我为IE10及以下版本添加了一个后备:

https://jsfiddle.net/oupxo3pu/

唯一的限制是使用IE10及以下时可以提交的图像大小,如果图像大小太大,js代码将引发错误.每个服务器之间的post值的最大大小是不同的,js代码有一个变量来设置最大大小.

下面的PHP代码适用于上述后备:

//File destination
$destination = "/folder/cropped_image.png";
if($_POST["png"]) {//IE10 and below
    //Get convertable base64 image string
    $image_base64 = $_POST["png"];
    $image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
    $image_base64 = str_replace(" ", "+", $image_base64);
    //Convert base64 string to image data
    $image = base64_decode($image_base64);
    //Save image to final destination
    file_put_contents($destination, $image);
} else if($_FILES["cropped_image"]) {//IE11+ and modern browsers
    //Get uploaded image file it's temporary name
    $image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
    //Move temporary file to final destination
    move_uploaded_file($image_tmp_name, $destination);
}
Run Code Online (Sandbox Code Playgroud)
还没有canvas元素的回退代码,我正在研究它.
旧版浏览器后备版中的帖子大小限制是我自己放弃对旧浏览器的支持的原因之一.

更新3:

我推荐的IE8中canvas元素的后备:

http://flashcanvas.net/

它支持裁剪代码所需的所有画布函数.

请记住,它需要闪存.有一个画布后备(explorercanvas)不需要闪存,但它不支持我们需要保存裁剪图像的函数toDataURL().


Tat*_*ize 18

Seahorsepip的答案太棒了.我在非后备答案上做了很多改进.

http://jsfiddle.net/w1Lh4w2t/

我建议不要做那个奇怪的隐藏的png事情,当Image对象也能正常工作时(只要我们不支持回退).

var jcrop_api;
var canvas;
var context;
var image;
var prefsize;
Run Code Online (Sandbox Code Playgroud)

虽然即便如此,你最好还是在最后从画布中获取数据,并将其放在最后的那个字段中.

function loadImage(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
      image = new Image();
      image.src = e.target.result;
      validateImage();
    }
    reader.readAsDataURL(input.files[0]);
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果你想要更多的功能而不仅仅是裁剪,如果我们将jcrop附加到插入的画布(我们在刷新时使用jcrop销毁).我们可以轻松地使用画布执行任何操作,然后再次验证validateImage()并使更新后的图像可见.

function validateImage() {
  if (canvas != null) {
    image = new Image();
    image.src = canvas.toDataURL('image/png');
  }
  if (jcrop_api != null) {
    jcrop_api.destroy();
  }
  $("#views").empty();
  $("#views").append("<canvas id=\"canvas\">");
  canvas = $("#canvas")[0];
  context = canvas.getContext("2d");
  canvas.width = image.width;
  canvas.height = image.height;
  context.drawImage(image, 0, 0);
  $("#canvas").Jcrop({
    onSelect: selectcanvas,
    onRelease: clearcanvas,
    boxWidth: crop_max_width,
    boxHeight: crop_max_height
  }, function() {
    jcrop_api = this;
  });
  clearcanvas();
}
Run Code Online (Sandbox Code Playgroud)

然后在提交时,我们提交任何待处理的操作,例如applyCrop()或applyScale(),如果我们需要那些东西,则将数据添加到隐藏字段中以用于后备内容.然后我们有一个系统,我们可以轻松地以任何方式修改画布,然后当我们提交画布时,数据被正确发送.

function applyCrop() {
  canvas.width = prefsize.w;
  canvas.height = prefsize.h;
  context.drawImage(image, prefsize.x, prefsize.y, prefsize.w, prefsize.h, 0, 0, canvas.width, canvas.height);
  validateImage();
}
Run Code Online (Sandbox Code Playgroud)

画布将添加到div视图中.

 <div id="views"></div>
Run Code Online (Sandbox Code Playgroud)

要在PHP(drupal)中捕获附件,我使用了类似的东西:

    function makeFileManaged() {
        if (!isset($_FILES['croppedfile']))
            return NULL;
        $path = $_FILES['croppedfile']['tmp_name'];
        if (!file_exists($path))
            return NULL;
        $result_filename = $_FILES['croppedfile']['name'];
        $uri = file_unmanaged_move($path, 'private://' . $result_filename, FILE_EXISTS_RENAME);
        if ($uri == FALSE)
            return NULL;
        $file = File::Create([
                    'uri' => $uri,
        ]);
        $file->save();
        return $file->id();
    }
Run Code Online (Sandbox Code Playgroud)