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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12728188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 11:07:28  来源:igfitidea点击:

Cropping images in the browser BEFORE the upload

javascripthtmlhtml5-canvas

提问by Lorraine Bernard

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.

我发现的许多库,比如 Jcrop,实际上并没有进行裁剪,它只创建了一个图像裁剪 UI。然后它取决于进行实际裁剪的服务器。

How can I make the image cropping client-sideby using some HTML5 featurewithout using any server-side code.

如何在不使用任何服务器端代码的情况下使用某些HTML5 功能客户端进行图像裁剪。

If yes, are there some examples or hints?

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

回答by gion_13

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. draw the image into a canvas with the crop boundaries specified
  3. get the image data from the canvas and make it a hrefattribute for an anchor tag in the dom
  4. add the download attribute (download="desired-file-name") to that aelement That's it. all the user has to do is click your "download link" and the image will be downloaded to his pc.
  1. 加载图像
  2. 将图像绘制到指定裁剪边界的画布中
  3. 从画布中获取图像数据,并使其成为hrefdom 中锚点标记的属性
  4. 将下载属性 ( download="desired-file-name")添加到该a元素 就是这样。用户所要做的就是单击您的“下载链接”,图像就会下载到他的电脑上。

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

当我有机会时,我会带着演示回来。

Update
Here's the live demoas I promised. It takes the jsfiddle logoand 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
忘记提及:当然有一个缺点:(。
因为同样适用于图像的同源策略,如果你想访问图像的数据(通过画布方法toDataUrl)。
所以你仍然需要一个服务器端代理可以为您的图像提供服务,就好像它托管在您的域上一样。

Update IIIAlthough 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:

文件 proxy.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 :

这是用于将图像数据(base64)保存到实际图像的示例 php 代码:

file save-image.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;

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.

当然,所有这些可能感觉有点复杂,但我想向您展示您想要实现的目标是可能的。

回答by apsillers

The Pixasticlibrary 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:

Pixastic库不正是你想要的。但是,它仅适用于支持画布的浏览器。对于那些较旧的浏览器,您需要:

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

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 drawCroppedImagefunction with Jcrop.

编辑:如果你不想学Pixastic,我已经添加的jsfiddle一个非常简单的农作物在这里。应该可以修改、集成和使用drawCroppedImageJcrop的功能。

回答by user1925970

#change-avatar-fileis a file input #change-avatar-fileis 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 标签(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;
                });
            }
        }
    });

回答by user1925970

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

如果你仍然使用 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);

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