在上传之前在浏览器中裁剪图像

63

我发现许多库,如Jcrop等,实际上并未进行裁剪,而只是创建了一个图像裁剪用户界面。它还依赖于服务器来执行实际的裁剪。

我如何使用一些 HTML5特性 在不使用任何服务器端代码的情况下,使图像裁剪在客户端进行呢?

如果可以,是否有一些示例或提示可以提供?


3
可能存在重复问题:1. JavaScript 客户端裁剪图片(与您的问题完全相同;然而,顶部答案说这是不可能的,但这是错误的)。2. JavaScript 中的图像复制和裁剪(用例略有不同,但顶部答案正好符合您的需求)。 - apsillers
https://github.com/foliotek/croppie - Tom M
4个回答

45

可以完成这个操作。这基于HTML5中新的锚标签“download”属性。 流程如下:

  1. 加载图像
  2. 使用指定的裁剪边界将图像绘制到画布上
  3. 从画布获取图像数据,并将其设置为DOM中锚标签的“href”属性
  4. 为该元素添加下载属性(download="desired-file-name")

就是这样。用户只需点击您的“下载链接”,即可将图像下载到他的电脑上。

我有机会时会回来展示演示。

这里是我承诺的实时演示,它使用了JSFiddle logo并裁剪了每个边距的5px。 代码如下:

var img = new Image();
img.onload = function(){
    var cropMarginWidth = 5,
        canvas = $('<canvas/>')
                    .attr({
                         width: img.width - 2 * cropMarginWidth,
                         height: img.height - 2 * cropMarginWidth
                     })
                    .hide()
                    .appendTo('body'),
        ctx = canvas.get(0).getContext('2d'),
        a = $('<a download="cropped-image" title="click to download the image" />'),
        cropCoords = {
            topLeft : {
                x : cropMarginWidth,
                y : cropMarginWidth
            },
            bottomRight :{
                x : img.width - cropMarginWidth,
                y : img.height - cropMarginWidth
            }
        };

    ctx.drawImage(img, cropCoords.topLeft.x, cropCoords.topLeft.y, cropCoords.bottomRight.x, cropCoords.bottomRight.y, 0, 0, img.width, img.height);
    var base64ImageData = canvas.get(0).toDataURL();

    a
        .attr('href', base64ImageData)
        .text('cropped image')
        .appendTo('body');

    a
        .clone()
        .attr('href', img.src)
        .text('original image')
        .attr('download','original-image')
        .appendTo('body');

    canvas.remove();
}
img.src = 'some-image-src';

忘了提一点:当然还有一个缺点 :(。 由于同源策略也适用于图像,如果您想通过画布方法toDataUrl访问图像数据, 因此,仍需要一个服务器端代理,将您的图像作为托管在您域上的图像提供。

虽然我无法提供此功能的实时演示(出于安全原因),但以下是解决同源策略的 PHP 示例代码:

文件 proxy.php

$imgData = getimagesize($_GET['img']);
header("Content-type: " . $imgData['mime']);
echo file_get_contents($_GET['img']);

这种方式,不再直接从其源加载外部图像:

img.src = 'http://example.com/imagefile.png';

您可以通过代理加载它:

img.src = 'proxy.php?img=' + encodeURIComponent('http://example.com/imagefile.png');

这里有一段用于将图像数据(base64)保存为实际图像的PHP示例代码:

文件 save-image.php

$data = preg_replace('/data:image\/(png|jpg|jpeg|gif|bmp);base64/','',$_POST['data']);
$data = base64_decode($data);
$img = imagecreatefromstring($data);

$path = 'path-to-saved-images/';
// generate random name
$name  = substr(md5(time()),10);
$ext = 'png';
$imageName = $path.$name.'.'.$ext;

// write the image to disk
imagepng($img,  $imageName);
imagedestroy($img);
// return the image path
echo $imageName;

然后你所要做的就是将图像数据发布到此文件中,它将保存该图像并返回现有图像的文件名。

当然,所有这些可能感觉有点复杂,但我想展示给你,你想实现的是可能的。


+1 很高兴能有一个演示。我也会尝试去做。但是奇怪的是网上还没有这样的例子。 - Lorraine Bernard
我已经做了,谢谢。您认为使用https://github.com/tapmodo/Jcrop与您的示例相结合以便将图像上传到服务器是合理/可行的吗?根据您的“更新II”,似乎我不能这样做。我是对的吗? - Lorraine Bernard
可以做到。如果您有图像数据,只需将其发布到服务器端页面,该页面将其写入图像文件即可。唯一棘手的部分(甚至不那么棘手)是使用代理来代理外部图像。例如:不要使用“http://domain.com/image.png”,而要使用“proxy.php?img=http://domain.com/image.png”。我不知道是否可以为此制作演示文稿,但您应该知道这是完全可行的 :) - gion_13
你的例子出现了错误:SecurityError: The operation is insecure. (img.onload()) - Huseyin Yagli

7
Pixastic库可以实现您想要的功能。但是,它只能在支持canvas的浏览器上运行。对于那些老旧的浏览器,您需要做以下两件事之一:
  1. 提供服务器端备用方案;或者
  2. 告诉用户很抱歉,但他需要使用更现代的浏览器。
当然,选项#2并不太友好。但是,如果您的意图是提供纯客户端工具和/或您无法支持后备裁剪器(例如,您正在编写浏览器扩展或离线Chrome应用程序,或者您无法负担提供图像操作库的良好托管提供者),那么限制您的用户群体为现代浏览器可能是公平的。 编辑:如果您不想学习Pixastic,我已经在jsFiddle上添加了一个非常简单的裁剪器这里。应该可以修改、集成并使用Jcrop中的drawCroppedImage函数。

嗨@apsillers,感谢您的回答。您确定这个演示(http://www.pixastic.com/lib/docs/actions/crop/)正在裁剪并调整图像大小吗?我的意思是,在裁剪操作之后,我无法下载已裁剪的图像,但我只能下载原始图像。 - Lorraine Bernard
1
裁剪后的图像被绘制到画布上。您可以使用 toDataURL 从画布中获取base64编码的图像数据。如果在裁剪后在URL栏中输入 javascript:window.open($("#demoimage")[0].toDataURL()),则会看到裁剪后的图像本身。 - apsillers
(请注意:如果您将此代码粘贴到地址栏中,您的浏览器可能会剥离掉开头的javascript:部分;请确保将其添加回去。) - apsillers
+1 谢谢,它有效 :). 无论如何,我想使用一个在 Github 上可用的 JS 插件和 Pixastic 不行 :(。我是对的吗? - Lorraine Bernard
2
你是否在寻找 https://github.com/jseidelin/pixastic? - apsillers
2
不幸的是,看起来Pixastic项目已经停止了。 - Cfreak

3

#change-avatar-file 是一个文件上传输入框 #change-avatar-file 是一个图片标签(jcrop的目标) “key” 是 FR.onloadend 事件 https://developer.mozilla.org/en-US/docs/Web/API/FileReader

$('#change-avatar-file').change(function(){
        var currentImg;
        if ( this.files && this.files[0] ) {
            var FR= new FileReader();
            FR.onload = function(e) {
                $('#avatar-change-img').attr( "src", e.target.result );
                currentImg = e.target.result;
            };
            FR.readAsDataURL( this.files[0] );
            FR.onloadend = function(e){
                //console.log( $('#avatar-change-img').attr( "src"));
                var jcrop_api;

                $('#avatar-change-img').Jcrop({
                    bgFade:     true,
                    bgOpacity: .2,
                    setSelect: [ 60, 70, 540, 330 ]
                },function(){
                    jcrop_api = this;
                });
            }
        }
    });

-1
如果您仍然使用JCrop,您只需要这些PHP函数来裁剪文件:
$img_src = imagecreatefromjpeg($src);
$img_dest = imagecreatetruecolor($new_w,$new_h);
imagecopyresampled($img_dest,$img_src,0,0,$x,$y,$new_w,$new_h,$w,$h);
imagejpeg($img_dest,$dest);

客户端:

jQuery(function($){

    $('#target').Jcrop({
    onChange:   showCoords,
    onSelect:   showCoords,
    onRelease:  clearCoords
    });

});

var x,y,w,h; //these variables are necessary to crop
function showCoords(c)
{
    x = c.x;
    y = c.y;
    w = c.w;
    h = c.h;
};
function clearCoords()
{
    x=y=w=h=0;
}

+1 不错的例子。你觉得我可以使用 HTML5 canvas 来完成 PHP 代码所做的同样工作吗? - Lorraine Bernard
我只知道并非所有浏览器都支持HTML5,如果你在服务器端完成工作,它就无法被黑客攻击。 - user669677
假设我想仅向支持“HTML5画布”的浏览器提供服务。我该怎么做?有任何示例吗? - Lorraine Bernard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接