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

Javascript 我应该如何使用jcrop在客户端裁剪图像并上传它?,javascript,jquery,css,image,jcrop,Javascript,Jquery,Css,Image,Jcrop,我正在开发一个组件,其中有文件上载HTML控件,使用该控件选择图像后,图像将呈现在HTML5画布元素上 下面是JSFIDLE及其示例代码: id=target是jcrop图像目标 id=photo是一个文件上载控件 id=preview是画布元素 id=clear\u选择是一个按钮,用于清除画布 使用的第三方JS库: <script src="./js/jquery.min.js"></script> <script src="./js/jquery.Jcrop.j

我正在开发一个组件,其中有文件上载HTML控件,使用该控件选择图像后,图像将呈现在HTML5画布元素上

下面是JSFIDLE及其示例代码:

id=target是jcrop图像目标
id=photo是一个文件上载控件
id=preview是画布元素
id=clear\u选择是一个按钮,用于清除画布

使用的第三方JS库:

<script src="./js/jquery.min.js"></script>
<script src="./js/jquery.Jcrop.js"></script>
<script src="./js/jquery.color.js"></script>
在jcrop面板上显示上载的图像

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);     
});
我使用以下代码在画布上裁剪和渲染图像

    function make_base() {
        console.log("make_base called");
        var base_image = new Image();
        base_image.src = '';
        base_image.onload = function () {
            context.drawImage(base_image, 0, 0);
        }
    }

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

    make_base();
    function updatePreview(c) {
        console.log("called");
        if(parseInt(c.w) > 0) {
            // Show image preview
            var imageObj = $("#target")[0];
            var canvas = $("#preview")[0];
            var context = canvas.getContext("2d");
            context.drawImage(imageObj, c.x, c.y, c.w, c.h, 0, 0, canvas.width, canvas.height);
        }
    };
以下是我在上述设置中面临的一系列问题:

  • updatePreview函数在选择时未被调用,因此画布未被渲染
  • 裁剪选择框不可拖动(我正在使用引导CSS,我怀疑这是由于缺少/不匹配的依赖项)
  • Canvas是
    HTML5
    元素,这意味着最终用户必须拥有与
    HTML5
    兼容的浏览器,我正在开发一个拥有数百万用户的应用程序。强制用户使用最新的浏览器不是一个可行的选择。这里的回退机制应该是什么
    以下是基本的html 5代码:

    此代码裁剪图像,显示预览,并将输入元素的值设置为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);
    
    //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);
    
    将base64图像字符串作为post变量提交具有其服务器post大小限制,并且base64编码使裁剪图像文件的大小更大(~33%),而裁剪图像的原始数据将更大,这使得上载时间更长

    要设置邮件大小限制,请执行以下操作:

    请记住,增加的帖子大小限制可能会被滥用,例如DoS攻击

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

    然后,您可以通过以下方式在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);
    
    //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);
    
    更新:

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

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

    我将发布一个更新,当我让它工作时,使用下面的回退代码

    更新2:

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

    唯一的限制是使用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);
    }
    
    canvas元素还没有回退代码,我正在研究它。 旧浏览器回退中的帖子大小限制是我本人放弃支持旧浏览器的原因之一。 更新3:

    我为IE8中的canvas元素推荐的后备方案是:

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


    记住它需要闪光灯。有一个画布回退(explorercanvas),它不需要flash,但不支持保存裁剪图像所需的函数toDataURL()。

    Seahorsepip的答案非常棒。我在非回退答案上做了很多改进

    我建议不要做那个奇怪的隐藏png的事情,当一个图像对象工作得很好的时候(只要我们不支持回退)

    尽管如此,您还是最好在最后将数据从画布中取出,并仅在最后将其放在该字段中

    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]);
      }
    }
    
    但是,如果您想要更多的函数,而不仅仅是裁剪,那么如果我们将jcrop附加到插入的画布(刷新时用jcrop销毁)。我们可以轻松地使用画布执行任何操作,然后再次验证图像(),并将更新后的图像显示到位

    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();
    }
    
    画布将添加到div视图中

     <div id="views"></div>
    

    目前在我的手机上,我将在一两个小时后回家:/train旅行是一种可怕的缓慢体验……此外,客户端剪切图像上传是一件棘手的事情,需要我做一些工作,在第一瞬间我将其设置为base64字符串,但由于其长度,在提交时遇到了问题。在这之后,我添加了代码,将base64字符串变成一个图像文件blob,然后我使用一些html5代码将其添加到表单中(因此,这也需要一个回退)。我在项目中放弃了所有旧的浏览器,因为我一直遇到这些限制,并且为每个限制做回退花费了太多的时间和工作。回来,给我一分钟时间来制作ajs fiddle。好的,我将尝试你发布的内容,并让你知道结果。不再适用于firefox或edge。将context.draw替换为:“if(coords.w!==0&&coords.h!==0){context.drawImage(imageObj,coords.x,coords.y,coords.w,coords.h,0,0,canvas.width,canvas.height);}”主要尝试jfiddle,应该功能齐全,将上传保存到jfiddle。两件事1)我使用上传控制器上传图像,
    引导模式
    没有显示图像,然后我单击
    裁剪按钮
    现在它显示图像。现在,如果我关闭模式并再次上传图像,它将显示一个旧图像。图像将从画布上分割,并保存画布。正在上载的图像是从修改后的画布构建的。您需要通过将数据发送回页面并在那里重建画布,从引导模式更新图像,或者直接从引导模式提交修改后的图像。添加到答案中。希望它能有所帮助,尽管这是文件所在的位置,而且超出了它的范围
    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();
    }
    
     <div id="views"></div>
    
        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();
        }