Adding cropper function to dynamically created image - javascript

I have input with url, and 'preview' button.
When I click 'preview' it inserts image from this url, before 'submit' button.
$('#preview').click(function() {
var image_url = $('#event_remote_flyer_url').val();
var image_tag = $('<img class="preview">');
image_tag.attr('src',image_url)
$('input[name="commit"]').before(image_tag);
});
Now I want to add cropper plugin for this image. (https://github.com/fengyuanchen/cropper)
Docs say I should use it like this:
$('img.preview').cropper({
aspectRatio: 16 / 9,
crop: function(e) {
// Output the result data for cropping image.
console.log(e.x);
console.log(e.y);
console.log(e.width);
console.log(e.height);
console.log(e.rotate);
console.log(e.scaleX);
console.log(e.scaleY);
}
});
It doesn't work. As I understand javascript is not waiting with this cropper function and runs it when img.preview is not inserted yet. How can I fix this?

You need to attach it when creating img.preview elements and your understanding is spot on.
$('#preview').click(function() {
var image_url = $('#event_remote_flyer_url').val();
var image_tag = $('<img class="preview">');
image_tag.attr('src', image_url)
//Atach Cropper
image_tag.cropper({
aspectRatio: 16 / 9,
crop: function(e) {}
});
//Append
$('input[name="commit"]').before(image_tag);
});

Related

How can I use cropme.js to allow users upload their profile image?

This is a knowledge sharing Q&A.
Cropme is a nice JS add-on for cropping and rotating an image using visual sliders. The author provided good documentation, but building a working implementation is not as simple as it should be.
The question I want to answer is this:
I want to allow my website-users to upload their profile image. That image must be exactly 240x292 pixels. The users should be able zoom and rotate their image, then crop it to that specific size and upload it to my website. How can I do all that with cropme?
These are the requires steps:
Show an empty placeholder for the image we want the user to load.
By clicking the "Get Image" button, the user can select an image from its local files.
The selected file is loaded into memory, and presented for editing using 'cropme'. The user can use visual sliders to rotate and zoom in/out
After clicking "Crop", the user is presented with the cropped image, and can decide to save the image or to cancel.
After clicking "Save", the cropped image is uploaded to a PHP server, the modal window is closed, and the placeholder image is replaced with the link to the just-uploaded image.
So how can we do this?
A fully working demo is presented here:
https://codepen.io/ishahak/pen/XWjVzLr
I will explain some of the details, step by step.
Note: usually in my code when you see obj[0], it is simply a conversion from jQuery object into a simple JS object.
1. Showing a placeholder for the image.
We can create a real SVG image on the fly using this code:
getImagePlaceholder: function(width, height, text) {
//based on https://cloudfour.com/thinks/simple-svg-placeholder/
var svg = '\
<svg xmlns="http://www.w3.org/2000/svg" width="{w}" \
height="{h}" viewBox="0 0 {w} {h}">\
<rect fill="#ddd" width="{w}" height="{h}"/>\
<text fill="rgba(0,0,0,0.5)" font-family="sans-serif"\
font-size="30" dy="10.5" font-weight="bold"\
x="50%" y="50%" text-anchor="middle">{t}</text>\
</svg>';
var cleaned = svg
.replace(/{w}/g, width)
.replace(/{h}/g, height)
.replace('{t}', text)
.replace(/[\t\n\r]/gim, '') // Strip newlines and tabs
.replace(/\s\s+/g, ' ') // Condense multiple spaces
.replace(/'/gim, '\\i'); // Normalize quotes
var encoded = encodeURIComponent(cleaned)
.replace(/\(/g, '%28') // Encode brackets
.replace(/\)/g, '%29');
return 'data:image/svg+xml;charset=UTF-8,' + encoded;
}
2. By clicking the "Get Image" button, the user can select an image from its local files.
This process involves an input element of type "file" which has no visible appearance (we set it with the 'd-none' class), and a button element which 'clicks' it to open a dialog:
<button id="btnGetImage" class="btn btn-primary">Get Image</button>
<input class="d-none" type="file" id="fileUpload" accept="image/*" />
And the relevant code:
$('#btnGetImage').on('click', function(){
//force 'change' event even if repeating same file:
$('#fileUpload').prop("value", "");
$('#fileUpload').click();
});
$('#fileUpload').on('change', function(){
CiM.read_file_from_input(/*input elem*/this, function() {
console.log('image src fully loaded');
$('#imgModal-dialog').modal('show');
});
});
When a file is selected, the 'change' event is firing, leading us to read the file into memory.
3. The selected file is loaded into memory, and presented for editing using 'cropme'. The user can use visual sliders to rotate and zoom in/out
Our read_file_from_input mentioned above is implemented like this:
imgHolder: null,
imgHolderCallback: null,
read_file_from_input: function(input, callback) {
if (input.files && input.files[0]) {
imgHolderCallback = callback;
var reader = new FileReader();
if (!CiM.imgHolder) {
CiM.imgHolder = new Image();
CiM.imgHolder.onload = function () {
if (imgHolderCallback) {
imgHolderCallback();
}
}
}
reader.onload = function (e) {
console.log('image data loaded!');
CiM.imgHolder.src = e.target.result; //listen to img:load...
}
reader.readAsDataURL(input.files[0]);
}
else {
console.warn('failed to read file');
}
}
When the FileReader is ready, we set the src for our internal image holder, and wait for the 'load' event, which signals that the img element is ready with the new content.
We listen to that 'load' event, and when triggered we show the modal. A modal in Bootstrap has several events. We listen to the one which signals that the modal is shown, meaning that the width and set and we can plan our Cropme dimensions based on it.
update_options_for_width: function(w) {
var o = CiM.opt, //shortcut
vp_ratio = o.my_final_size.w / o.my_final_size.h,
h, new_vp_w, new_vp_h;
w = Math.floor(w * 0.9);
h = Math.floor(w / o.my_win_ratio);
o.container.width = w;
o.container.height = h;
new_vp_h = 0.6 * h;
new_vp_w = new_vp_h * vp_ratio;
// if we adapted to the height, but it's too wide:
if (new_vp_w > 0.6 * w) {
new_vp_w = 0.6 * w;
new_vp_h = new_vp_w / vp_ratio;
}
new_vp_w = Math.floor(new_vp_w);
new_vp_h = Math.floor(new_vp_h);
o.viewport.height = new_vp_h;
o.viewport.width = new_vp_w;
}
We wait for the size of the modal to be set because cropme must be set with specific viewport dimensions. At the end of our shown.bs.modal handler, we create our Cropme instance.
4. After clicking "Crop", the user is presented with the cropped image, and can decide to save the image or to cancel.
Here is the save-button handler:
$('#imgModal-btnSave').on('click', function(){
uploadImage(croppedImg[0], function(path_to_saved) {
savedImg[0].src = path_to_saved;
$('#imgModal-dialog').modal('hide');
});
});
The uploadImage function goes like this:
uploadImage: function(img, callback){
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture (needed??)
imgCanvas.width = img.width;
imgCanvas.height = img.height;
// Draw image into canvas element
imgContext.drawImage(img, 0, 0, img.width, img.height);
var dataURL = imgCanvas.toDataURL();
$.ajax({
type: "POST",
url: "save-img.php", // see code at the bottom
data: {
imgBase64: dataURL
}
}).done(function(resp) {
if (resp.startsWith('nok')) {
console.warn('got save error:', resp);
} else {
if (callback) callback(resp);
}
});
}
It is matched with a simple PHP script which appears at the end of the HTML in the codepen. I think this answer went too long, so I'll finish here.
Good luck - have fun :)

I'm sure the code is correct but not working? Javascript, Croppie JS, PHP, jQuery

I'm having some serious trouble with this piece of Croppie.JS Code.
It runs on the Croppie.JS demo. Calling the croppie plugin through a variable is working and reporting an object inside the parentheses.
However when I try to link it up to the rotation button, nothing rotates.
I can rotate it using the orientation setting in the bind function however this only seems to be accessible if I also refresh the whole page at the same time?
I'm hoping someone can help me out here, I'm in quite a mess.
I'm just looking to make it so that when you upload a picture, you can rotate it before you submit it without refreshing the page every time (because refreshing the page was also rotating the image?!)
//Code:
<button class="vanilla-rotate" data-deg="-90">Rotate</button>
var croppieBox = $('#main-cropper').croppie({
viewport: { width: 300, height: 300 },
boundary: { width: 320, height: 320 },
enableOrientation: true,
update: function (data) {
var coords = $('#main-cropper').croppie('get');
//alert ( JSON.stringify(coords, null, 4) );
//for data use
var pointA = coords.points[0];
var pointB = coords.points[1];
var pointC = coords.points[2];
var pointD = coords.points[3];
var zoom = coords.zoom;
var croppieGet = $('#main-cropper').croppie('result', {
type: "canvas",
format: <?php echo $file_ext;?>
}).then(function (img) {
document.getElementById("javascript_image_base64").value = img;
});
}
//Close the first part of Croppie
});
croppieBox.croppie('bind', {
url: <?php echo "'".$croppy_image_path."'"; ?>,
});
//.vanilla-rotate is the HTML button
$('.vanilla-rotate').on('click', function(ev) {
//vanilla is the new croppie instance, croppieBox
var croppieBox = $('#main-cropper');
//This line seems to be causing the problem???
$croppieBox.croppie('rotate', parseInt($(this).data('deg')));
});
// get cropped image data
// get zoom data
// get crop points data
//send it to php
</script>
Thanks to Gulshan and ADyson the code is now working. Thank you both, your help is much appreciated. You can see what you helped to build here at https://www.thejobswhale.com
If you are happy, I'll add you both to the credits list.
//Updated, working code is
<script>
//Check when window is resized. If it crosses the boundary for mobile,
//remove the zoomer from the croppie plugin.
//If it crosses it the other way, add the zoomer back in.
var croppieBox = $('#main-cropper').croppie({
viewport: { width: 300, height: 300 },
boundary: { width: 320, height: 320 },
enableOrientation: true,
update: function (data) {
var coords = $('#main-cropper').croppie('get');
//alert ( JSON.stringify(coords, null, 4) );
//for data use
var pointA = coords.points[0];
var pointB = coords.points[1];
var pointC = coords.points[2];
var pointD = coords.points[3];
var zoom = coords.zoom;
var croppieGet = $('#main-cropper').croppie('result', {
type: "canvas",
format: '<?php echo $file_ext;?>'
}).then(function (img) {
document.getElementById("javascript_image_base64").value = img;
});
}
//Close the first part of Croppie
});
croppieBox.croppie('bind', {
url: <?php echo "'".$croppy_image_path."'"; ?>,
});
//.vanilla-rotate is the HTML button
$('.vanilla-rotate').on('click', function(ev) {
//vanilla is the new croppie instance, croppieBox
var croppieBox = $('#main-cropper');
croppieBox.croppie('rotate', parseInt($(this).data('deg')));
});
// get cropped image data
// get zoom data
// get crop points data
//send it to php
</script>

How to get image dimension in Dropzone JS with setting createImageThumbnails to false?

With Dropzone.js, I am trying to get image height and width with createImageThumbnails = false. To be more precise, I do not need thumbnails to be created while dropping images because the process becomes slow specially when I drop many images at once. I just need to scan height and width of all images that are dropped and save them into a database. But the problem is, when I turn off thumbnail creation, dropzone does not provide image height and width. As per documentation, image height and width are provided after the thumbnail event is triggered. So, quite the opposite to what I need. So as a solution, I would like to be able to get image height and width info as soon as images are dropped into dropzone and there should be no delay for creating thumbnail. Please advise if there is a way out of it.
HTML
<div id="dropzone">
<form class="dropzone" id = "upload-widget" action = "/demo">
</form>
</div>
JS
jQuery(document).ready(function($)
{
var images = Array();
var item = [];
Dropzone.options.uploadWidget = {
autoProcessQueue: false,
acceptedFiles: 'image/*',
previewTemplate: '<div class="dz-filename"><span data-dz-name></span></div>',
createImageThumbnails: false,
init: function() {
this.on("addedfile", function(file)
{
height = file.height;
width = file.width;
item = {Name : file.name, Size:file.size, Height:file.height, Width:file.width};
images.push(item);
});
}
};
});
You could try working with the accept function, along with FileReader(), and the Image() constructor.
Dropzone.options.uploadWidget = {
autoProcessQueue: false,
acceptedFiles: 'image/*',
previewTemplate: '<div class="dz-filename"><span data-dz-name></span></div>',
createImageThumbnails: false,
accept: function(file, done) {
// FileReader() asynchronously reads the contents of files (or raw data buffers) stored on the user's computer.
var reader = new FileReader();
reader.onload = (function(entry) {
// The Image() constructor creates a new HTMLImageElement instance.
var image = new Image();
image.src = entry.target.result;
image.onload = function() {
console.log(this.width);
console.log(this.height);
};
});
reader.readAsDataURL(file);
done();
}
}

cropper.jscant initialize blob image <img>tag by blueimp

So what i want :
select an image with input type file,
downscale with blueimp,
crop that image with cropper.js
so the blueimp part is working fine, the image gets resized to maxWidth property and is appended as <img> tag to "#imagearea" then i want to initialize cropper.js based on that tag, like in the docs, heres my code
document.getElementById('file-input').onchange = function (e) {
var loadingImage = loadImage(
e.target.files[0],
function (img) {
$(img).attr("style","max-width: 100%;")
$('#imagearea').append(img)
// now i see the image and when i inspect dom its a <img> tag
// so lets initialize the cropper
$('#imagearea').find("img").cropper({
aspectRatio: 16 / 9,
crop: function(e) {
}
});
},
{maxWidth: 1280}
);
};
but when initializing the cropper,
first i get an 404 error like
GET blob:http://foo.bar/64c77709-29f7-44ba-8772-49517e7976e5 404 (Not Found)
and then
Uncaught RangeError: Offset is outside the bounds of the DataView
at DataView.getUint8 ()
at m (6eaf333.js:7051)
at e.value (6eaf333.js:7051)
at XMLHttpRequest.n.onload (6eaf333.js:7051)
Please use "noRevoke " option.
document.getElementById('file-input').onchange = function (e) {
var loadingImage = loadImage(
e.target.files[0],
function (img) {
$(img).attr("style","max-width: 100%;")
$('#imagearea').append(img)
// now i see the image and when i inspect dom its a <img> tag
// so lets initialize the cropper
$('#imagearea').find("img").cropper({
aspectRatio: 16 / 9,
crop: function(e) {
}
});
},
{maxWidth: 1280,noRevoke: true}
);
};

Ajax Cropped Image to PHP

I am trying to create a jQuery image cropper. A user will upload an image and an image preview of where they want to crop will be shown. The cropping size should be exactly 600 x 400. So if the user tries to upload an image that's 2000px wide by 2000px tall, then they will only be able to get a 600 x 400 selection of that image. After the user has selected their crop of that image, then they will be able to upload it. The cropped image should be relayed in Ajax and sent to PHP, but how do I create the Ajax for it? How can I send the cropped coordinates and the image dimensions through Ajax to PHP? Afterwards, how should the PHP look like? This plugin (cropbox) works on getting src's of images, but it's not working with input type="file" for some reason. Here's the jsfiddle, and here's what I've tried:
HTML
<input type="file" id="cropimage">
jQuery
$(function() {
var file = $("#cropimage").files[0];
if (file.type.match(/image\/.*/)) {
var reader = new FileReader();
reader.onload = function() {
var $img = $('<img />').attr({
src: reader.result
});
}
$img.on('load', function() {
$img.cropbox({
width: 600,
height: 400
}).on('cropbox', function(event, results, img) {
})
})
}
})
Attach change listener over file-input
Set reader.readAsDataURL(file);
Append $img in DOM
$(function() {
$("#cropimage").on('change', function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function() {
var $img = $('<img />').attr({
src: reader.result
});
$img.on('load', function() {
$img.cropbox({
width: 600,
height: 400
}).on('cropbox', function(event, results, img) {});
});
$('body').append($img);
};
reader.readAsDataURL(file);
});
});
Fiddle Demo

Categories