jCrop 在更改图像时使用正确的坐标调整大小

Posted

技术标签:

【中文标题】jCrop 在更改图像时使用正确的坐标调整大小【英文标题】:jCrop resize with correct coords when changing image 【发布时间】:2016-05-19 19:35:48 【问题描述】:

希望这不会被标记为重复,因为 SO 上的其他 q/as 都没有帮助我解决这个问题,我想我需要更具体的帮助。

我的网站上有一个个人资料页面,允许用户更改他们的个人资料图片而无需重新加载页面(通过 AJAX / jQuery)。

这一切都很好。用户打开“更改个人资料图片”模式,选择要上传的文件并按“裁剪此图像”。按下此按钮时,它会使用发送文件和 formData(我将文件数据附加到其中)的典型方式将文件上传到网站。

它通过以下 jQuery 被发送到后端:

// Upload the image for cropping (Crop this Image!)
$("#image-upload").click(function()

    // File data
    var fileData = $("#image-select").prop("files")[0];

    // Set up a form
    var formData = new FormData();

    // Append the file to the new form for submission
    formData.append("file", fileData);

    // Send the file to be uploaded
    $.ajax(

        // Set the params
        cache: false,
        contentType: false,
        processData: false,

        // Page & file information
        url: "index.php?action=uploadimage",
        dataType: "text",
        type: "POST",

        // The data to send
        data: formData,

        // On success...
        success: function(data)

            // If no image was returned
            // "not-image" is returned from the PHP script if we return it in case of an error
            if(data == "not-image")
                alert("That's not an image, please upload an image file.");
                return false;
            

            // Else, load the image on to the page so we don't need to reload
            $(profileImage).attr("src", data);

            // If the API is already set, then we should apply a new image
            if(jCropAPI)
                jCropAPI.setImage(data + "?" + new Date().getTime());
            

            // Initialise jCrop
            setJCrop();

            //$("#image-profile").show();
            $("#send-coords").show();
        
    )
);

setJcrop 执行以下操作

function setJCrop()

    // Get width / height of the image
    var width = profileImage.width();
    var height = profileImage.height();

    // Var containing the source image
    var imgSource = profileImage.attr("src");

    // New image object to work on
    var image = new Image();
    image.src = imgSource;

    // The SOURCE (ORIGINAL) width / height
    var origWidth = image.width;
    var origHeight = image.height;

    // Set up the option to jCrop it
    $(profileImage).Jcrop(
        onSelect: setCoords,
        onChange: setCoords,
        setSelect: [0, 0, 51, 51],
        aspectRatio: 1,             // This locks it to a square image, so it fits the site better
        boxWidth: width,
        boxHeight: height,          // Fixes the size permanently so that we can load new images
    , function()jCropAPI = this);

    setOthers(width, height, origWidth, origHeight);

一旦后端,它会执行以下操作:

public function uploadImage($file)

    // See if there is already an error
    if(0 < $file["file"]["error"])
        return $file["file"]["error"] . " (error)";
    else

        // Set up the image
        $image = $file["file"];
        $imageSizes = getimagesize($image["tmp_name"]);

        // If there are no image sizes, return the not-image error
        if(!$imageSizes)
            return "not-image";
        

        // SIZE LIMIT HERE SOON (TBI)

        // Set a name for the image
        $username = $_SESSION["user"]->getUsername();
        $fileName = "images/profile/$username-profile-original.jpg";

        // Move the image which is guaranteed a unique name (unless it is due to overwrite), to the profile pictures folder
        move_uploaded_file($image["tmp_name"], $fileName);

        // Return the new filename
        return $fileName;
    

然后,用户使用选择器在图像上选择他们的区域并按下“更改个人资料图片”,执行以下操作

// Send the Coords and upload the new image
$("#send-coords").click(function()

    $.ajax(
        type: "POST",
        url: "index.php?action=uploadprofilepicture",
        data: 
            coordString: $("#coords").text() + $("#coords2").text(),
            imgSrc: $("#image-profile").attr("src")
        ,
        success: function(data)
            if(data == "no-word")
                alert("Can not work with this image type, please try with another image");
            else

                // Append a date to make sure it reloads the image without using a cached version
                var dateNow = new Date();
                var newImageLink = data + "?" + dateNow.getTime();
                $("#profile-picture").attr("src", newImageLink);

                // Hide the modal
                $("#profile-picture-modal").modal("hide");
            
        
    );
)

后端是:

public function uploadProfilePicture($coordString, $imgSrc)

    // Target dimensions
    $tarWidth = $tarHeight = 150;

    // Split the coords in to an array (sent by a string that was created by JS)
    $coordsArray = explode(",", $coordString);

    //Set them all from the array
    $x =            $coordsArray[0];
    $y =            $coordsArray[1];
    $width =        $coordsArray[2];
    $height =       $coordsArray[3];
    $newWidth =     $coordsArray[4];
    $newHeight =    $coordsArray[5];
    $origWidth =    $coordsArray[6];
    $origHeight =   $coordsArray[7];

    // Validate the image and decide which image type to create the original resource from
    $imgDetails = getimagesize($imgSrc);
    $imgMime = $imgDetails["mime"];

    switch($imgMime)
        case "image/jpeg":
            $originalImage = imagecreatefromjpeg($imgSrc);
            break;
        case "image/png":
            $originalImage = imagecreatefrompng($imgSrc);
            break;
        default:
            return "no-work";
    

    // Target image resource
    $imgTarget = imagecreatetruecolor($tarWidth, $tarHeight);
    $img = imagecreatetruecolor($newWidth, $newHeight);

    // Resize the original image to work with our coords
    imagecopyresampled($img, $originalImage, 0, 0, 0, 0,
        $newWidth, $newHeight, $origWidth, $origHeight);

    // Now copy the CROPPED image in to the TARGET resource
    imagecopyresampled(
        $imgTarget,     // Target resource
        $img,           // Target image
        0, 0,           // X / Y Coords of the target image; this will always be 0, 0 as we do not want any black nothingness
        $x, $y,         // X / Y Coords (top left) of the target area
        $tarWidth,
        $tarHeight,       // width / height of the target
        $width,
        $height         // Width / height of the source image crop
    );

    $username = $_SESSION["user"]->getUsername();
    $newPath = "images/profile/$username-profile-cropped.jpg";

    // Create that shit!
    imagejpeg($imgTarget, $newPath);

    // Return the path
    return $newPath;

所以基本上这会返回新文件的路径,该文件会更改为用户的个人资料图片(每次都相同)并实时上传,并在? 之后附加时间以正确刷新图像(无缓存)。

这一切都很好,但是如果用户选择另一张图片来上传,在已经上传一张之后,坐标就会变得一团糟(例如,它们从 50 变为 250)并且最终会裁剪图像的完全不同的部分,留下大部分是黑色的虚无。

对于这个问题中的大量代码感到非常抱歉,但我很感谢以前可能解决过这个问题的人提供的任何帮助。

有些代码可能看起来不合适,但这只是我试图调试它。

谢谢,再次对这个问题的规模感到抱歉。

-编辑-

我的 setCoords()setOthers() 函数如下所示:

//Set the coords with this method, that is called every time the user makes / changes a selection on the crop panel
function setCoords(c)
    $("#coords").text(c.x + "," + c.y + "," + c.w + "," + c.h + ",");


//This one adds the other parts to the second div; they will be concatenated in to the POST string
function setOthers(width, height, origWidth, origHeight)
    $("#coords2").text(width + "," + height + "," + origWidth + "," + origHeight);

【问题讨论】:

你的setCoords函数是什么样的? 为你编辑了问题:) 【参考方案1】:

我现在已经解决了这个问题。

对我来说,问题是当使用 setJCrop(); - 它没有重新加载图像。原因是上传并加载到JCrop窗口的图像每次都具有相同的名称(用户名作为前缀,然后是profile-cropped.jpg)。

所以为了解决这个问题,我使用了 setImage 方法来加载全尺寸图像。

我通过设置 boxWidth / boxHeight 参数解决了这个问题,但它们只是给我留下了每次加载新图像时坐标不正确的问题。

事实证明,它每次都从缓存中加载图像,即使我在 jQuery 中使用 new Image();

为了解决这个问题,我现在使用了 destroy();在 jCropAPI 上,然后每次都重新初始化它,不使用 setImage();

我在图像本身的 CSS 中设置了最大宽度,这阻止了它被锁定到特定宽度。

下一个问题是,每次我第二次加载图像时,它都会将旧图像的宽度/高度留在那里,这使得图像看起来完全歪斜和错误。

为了解决这个问题,我将使用 jCrop 的图像的宽度和高度重置为 ""$(profileImage).css("width", ""); $(profileImage).css("height", "");,然后从新上传的图像重新设置图像的源。

但我仍然遇到在图像上使用相同名称的问题,然后导致它每次都从缓存中加载。

我对此的解决方案是在数据库中添加一个“头像”列,并且每次都将图像名称保存在数据库中。该图像被命名为$username-$time.jpg$username-$time.jpg-cropped.jpg,其中$username 是用户的用户名(derp),$time 就是PHP 中的time();

这意味着每次我上传一张图片时,它都有一个新名称,因此当对该图片进行任何调用时,都不会缓存它。

imageName + ".jpg?" + new Date.getTime(); 这样的附加对某些事情有用,但是当发送图像名称后端时,它不能正常工作,决定何时附加/不附加它是一件痛苦的事情,然后有一件事要求它是追加强制重新加载,但是当追加时它不能正常工作,所以我不得不重新工作。

所以关键:(TL;DR)

如果您正在加载新图像,请不要使用与 jCrop 相同的图像名称;上传具有不同名称的图像,然后引用该图像。缓存问题很痛苦,如果不每次都使用一个新名称,您将无法真正正确地解决它们,因为这样可以确保绝对不会再出现问题(只要名称始终是唯一的)。

然后,当您初始化 jCrop 时,如果有前一个,则预先销毁前一个。在图像上使用max-width 而不是width 以阻止其锁定宽度,如果您将新图像加载到相同的&lt;img&gt;&lt;div&gt; 中,请重新设置图像的宽度/高度

希望这对某人有所帮助!

【讨论】:

【参考方案2】:

我使用了 jcrop,我认为这发生在我身上。当有新图像时,您必须“重置”jcrop。试试这样的:

function resetJCrop()

    if (jCropAPI) 
        jCropAPI.disable();
        jCropAPI.release();
        jCropAPI.destroy();
    


$("#image-upload").click(function()

    success: function(data)
    ...

    resetJCrop(); // RESETTING HERE

    // If the API is already set, then we should apply a new image
    if(jCropAPI)
        jCropAPI.setImage(data + "?" + new Date().getTime());
    

    // Initialise jCrop
    setJCrop();

    ...
    
);

我不记得在我的特定情况下为什么必须使用 disable() AND release() AND destroy() 的详细信息。可能你只能使用其中之一。试试看,看看是否适合你!

【讨论】:

不幸的是我仍然有同样的问题:(

以上是关于jCrop 在更改图像时使用正确的坐标调整大小的主要内容,如果未能解决你的问题,请参考以下文章

如果调整图像大小,Jcrop 问题 - Jquery

调整窗口大小时如何使用鼠标单击获取图像坐标?

调整主图像大小后,jcrop 演示不再有效(使用图片更新)

尝试使用 Jcrop 和使用 scala Scrimage lib 调整服务器端图像大小

jcrop 未在现场正确调整框尺寸

Jcrop 图像干预 Laravel 5