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

时间:2021-11-19 21:20:29

Many libraries I have found, like Jcrop, do not actually do the cropping, it only creates an image cropping UI. It then depends on the server doing the actual cropping.

我发现许多库,比如j农作物,实际上并不做裁剪,它只创建一个图像裁剪UI。然后它取决于服务器执行实际裁剪。

How can I make the image cropping client-side by using some HTML5 feature without using any server-side code.

如何在不使用任何服务器端代码的情况下使用HTML5特性使图像裁剪到客户端。

If yes, are there some examples or hints?

如果有,有没有例子或提示?

4 个解决方案

#1


38  

Yes, it can be done.
It is based on the new html5 "download" attribute of anchor tags.
The flow should be something like this :

是的,这是可以做到的。它基于新的html5“下载”属性的锚标。流程应该是这样的:

  1. load the image
  2. 加载图片
  3. draw the image into a canvas with the crop boundaries specified
  4. 用指定的裁剪边界将图像绘制到画布中
  5. get the image data from the canvas and make it a href attribute for an anchor tag in the dom
  6. 从画布中获取图像数据,并将其作为dom中锚标记的href属性
  7. add the download attribute (download="desired-file-name") to that a element That's it. all the user has to do is click your "download link" and the image will be downloaded to his pc.
  8. 将下载属性(download=“desired-file-name”)添加到它的元素中。用户只需点击“下载链接”,图像就会被下载到他的电脑上。

I'll come back with a demo when I get the chance.

我一有机会就回来演示。

Update
Here's the live demo as I promised. It takes the jsfiddle logo and crops 5px of each margin.
The code looks like this :

更新这里是我承诺的现场演示。它使用jsfiddle标识,并在每个边距上添加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';

Update II
Forgot to mention : of course there is a downside :(.
Because of the same-origin policy that is applied to images too, if you want to access an image's data (through the canvas method toDataUrl).
So you would still need a server-side proxy that would serve your image as if it were hosted on your domain.

更新II忘记提及:当然有一个缺点:(。因为同样适用于图像的同源策略,如果您想访问图像的数据(通过canvas方法toDataUrl)。因此,您仍然需要一个服务器端代理,它将服务于您的映像,就好像它是托管在您的域上一样。

Update III Although I can't provide a live demo for this (for security reasons), here is a php sample code that solves the same-origin policy :

更新III虽然我不能为此提供实时演示(出于安全原因),但这里有一个php示例代码,可以解决同源策略:

file proxy.php :

文件代理。php:

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

This way, instead of loading the external image direct from it's origin :

这样,就不用直接从外部图像的原点加载了:

img.src = 'http://some-domain.com/imagefile.png';

You can load it through your proxy :

您可以通过代理加载:

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

And here's a sample php code for saving the image data (base64) into an actual image :

下面是一个示例php代码,用于将图像数据(base64)保存到实际图像中:

file save-image.php :

文件保存图像。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;

All you have to do then is post the image data to this file and it will save the image to disc and return you the existing image filename.

然后,您所要做的就是将图像数据发布到这个文件,它将把图像保存到磁盘,并返回现有的图像文件名。

Of course all this might feel a bit complicated, but I wanted to show you that what you're trying to achieve is possible.

当然,这一切可能会让你觉得有点复杂,但我想告诉你,你想要实现的目标是可能的。

#2


7  

The Pixastic library does exactly what you want. However, it will only work on browsers that have canvas support. For those older browsers, you'll either need to:

像素库做的正是你想要的。但是,它只适用于支持画布的浏览器。对于那些较老的浏览器,您需要:

  1. supply a server-side fallback, or
  2. 提供服务器端回退,或。
  3. tell the user that you're very sorry, but he'll need to get a more modern browser.
  4. 告诉用户你很抱歉,但是他需要一个更现代的浏览器。

Of course, option #2 isn't very user-friendly. However, if your intent is to provide a pure client-only tool and/or you can't support a fallback back-end cropper (e.g. maybe you're writing a browser extension or offline Chrome app, or maybe you can't afford a decent hosting provider that provides image manipulation libraries), then it's probably fair to limit your user base to modern browsers.

当然,选项2不太友好。然而,如果你的目的是提供一个纯仅客户端的工具和/或你不能支持回退的后端农作物(例如:也许你正在编写一个浏览器Chrome扩展或离线应用程序,或者你买不起像样的托管提供商提供图像处理库),那么它可能是公平的现代浏览器限制你的用户群。

EDIT: If you don't want to learn Pixastic, I have added a very simple cropper on jsFiddle here. It should be possible to modify and integrate and use the drawCroppedImage function with Jcrop.

编辑:如果你不想学像素化,我在jsFiddle添加了一个非常简单的cropper。应该有可能修改、集成和使用与j农作物有关的绘图功能。

#3


3  

#change-avatar-file is a file input #change-avatar-file is a img tag (the target of jcrop) The "key" is FR.onloadend Event https://developer.mozilla.org/en-US/docs/Web/API/FileReader

#change-avatar-file是一个文件输入#change-avatar-file是一个img标签(j农作物的目标)“key”是FR.onloadend事件https://.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;
                });
            }
        }
    });

#4


-1  

If you will still use JCrop, you will need only this php functions to crop the file:

如果您仍然使用j农作物,您将只需要这个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);

client side:

客户端:

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


38  

Yes, it can be done.
It is based on the new html5 "download" attribute of anchor tags.
The flow should be something like this :

是的,这是可以做到的。它基于新的html5“下载”属性的锚标。流程应该是这样的:

  1. load the image
  2. 加载图片
  3. draw the image into a canvas with the crop boundaries specified
  4. 用指定的裁剪边界将图像绘制到画布中
  5. get the image data from the canvas and make it a href attribute for an anchor tag in the dom
  6. 从画布中获取图像数据,并将其作为dom中锚标记的href属性
  7. add the download attribute (download="desired-file-name") to that a element That's it. all the user has to do is click your "download link" and the image will be downloaded to his pc.
  8. 将下载属性(download=“desired-file-name”)添加到它的元素中。用户只需点击“下载链接”,图像就会被下载到他的电脑上。

I'll come back with a demo when I get the chance.

我一有机会就回来演示。

Update
Here's the live demo as I promised. It takes the jsfiddle logo and crops 5px of each margin.
The code looks like this :

更新这里是我承诺的现场演示。它使用jsfiddle标识,并在每个边距上添加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';

Update II
Forgot to mention : of course there is a downside :(.
Because of the same-origin policy that is applied to images too, if you want to access an image's data (through the canvas method toDataUrl).
So you would still need a server-side proxy that would serve your image as if it were hosted on your domain.

更新II忘记提及:当然有一个缺点:(。因为同样适用于图像的同源策略,如果您想访问图像的数据(通过canvas方法toDataUrl)。因此,您仍然需要一个服务器端代理,它将服务于您的映像,就好像它是托管在您的域上一样。

Update III Although I can't provide a live demo for this (for security reasons), here is a php sample code that solves the same-origin policy :

更新III虽然我不能为此提供实时演示(出于安全原因),但这里有一个php示例代码,可以解决同源策略:

file proxy.php :

文件代理。php:

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

This way, instead of loading the external image direct from it's origin :

这样,就不用直接从外部图像的原点加载了:

img.src = 'http://some-domain.com/imagefile.png';

You can load it through your proxy :

您可以通过代理加载:

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

And here's a sample php code for saving the image data (base64) into an actual image :

下面是一个示例php代码,用于将图像数据(base64)保存到实际图像中:

file save-image.php :

文件保存图像。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;

All you have to do then is post the image data to this file and it will save the image to disc and return you the existing image filename.

然后,您所要做的就是将图像数据发布到这个文件,它将把图像保存到磁盘,并返回现有的图像文件名。

Of course all this might feel a bit complicated, but I wanted to show you that what you're trying to achieve is possible.

当然,这一切可能会让你觉得有点复杂,但我想告诉你,你想要实现的目标是可能的。

#2


7  

The Pixastic library does exactly what you want. However, it will only work on browsers that have canvas support. For those older browsers, you'll either need to:

像素库做的正是你想要的。但是,它只适用于支持画布的浏览器。对于那些较老的浏览器,您需要:

  1. supply a server-side fallback, or
  2. 提供服务器端回退,或。
  3. tell the user that you're very sorry, but he'll need to get a more modern browser.
  4. 告诉用户你很抱歉,但是他需要一个更现代的浏览器。

Of course, option #2 isn't very user-friendly. However, if your intent is to provide a pure client-only tool and/or you can't support a fallback back-end cropper (e.g. maybe you're writing a browser extension or offline Chrome app, or maybe you can't afford a decent hosting provider that provides image manipulation libraries), then it's probably fair to limit your user base to modern browsers.

当然,选项2不太友好。然而,如果你的目的是提供一个纯仅客户端的工具和/或你不能支持回退的后端农作物(例如:也许你正在编写一个浏览器Chrome扩展或离线应用程序,或者你买不起像样的托管提供商提供图像处理库),那么它可能是公平的现代浏览器限制你的用户群。

EDIT: If you don't want to learn Pixastic, I have added a very simple cropper on jsFiddle here. It should be possible to modify and integrate and use the drawCroppedImage function with Jcrop.

编辑:如果你不想学像素化,我在jsFiddle添加了一个非常简单的cropper。应该有可能修改、集成和使用与j农作物有关的绘图功能。

#3


3  

#change-avatar-file is a file input #change-avatar-file is a img tag (the target of jcrop) The "key" is FR.onloadend Event https://developer.mozilla.org/en-US/docs/Web/API/FileReader

#change-avatar-file是一个文件输入#change-avatar-file是一个img标签(j农作物的目标)“key”是FR.onloadend事件https://.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;
                });
            }
        }
    });

#4


-1  

If you will still use JCrop, you will need only this php functions to crop the file:

如果您仍然使用j农作物,您将只需要这个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);

client side:

客户端:

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