Please see this code . In the following code user can upload images , they can move, resize, rotate uploaded images etc .
$(function() {
var inputLocalFont = $("#user_file");
inputLocalFont.change(previewImages);
function previewImages() {
var fileList = this.files;
var anyWindow = window.URL || window.webkitURL;
for (var i = 0; i < fileList.length; i++) {
var objectUrl = anyWindow.createObjectURL(fileList[i]);
var $newDiv = $("<div>", {
class: "img-div"
});
var $newImg = $("<img>", {
src: objectUrl,
class: "newly-added"
}).appendTo($newDiv);
$(".new-multiple").append($newDiv);
$newDiv.draggable();
$newDiv.rotatable();
$newDiv.resizable({
aspectRatio: true,
handles: "ne, nw, e, se, sw, w"
});
$newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
window.URL.revokeObjectURL(fileList[i]);
}
$(".newly-added").on("click", function(e) {
$(".newly-added").removeClass("img-selected");
$(this).addClass("img-selected");
e.stopPropagation()
});
$(document).on("click", function(e) {
if ($(e.target).is(".newly-added") === false) {
$(".newly-added").removeClass("img-selected");
}
});
}
$(".user_submit").on("click",function(e){
e.preventDefault();
html2canvas($('.new-multiple'), {
allowTaint: true,
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
});
});
.new-multiple {
width: 400px !important;
height: 400px !important;
background: white;
border: 2px solid red;
overflow: hidden;
}
.img-div {
width: 200px;
height: 200px;
}
.newly-added {
width: 100%;
height: 100%;
}
.img-selected {
box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
border: 2px solid rgb(145, 44, 94);
}
/*
.ui-resizable-handle.ui-resizable-se.ui-icon.ui-icon-gripsmall-diagonal-se {
background-color: white;
border: 1px solid tomato;
}
*/
.ui-resizable-handle {
border: 0;
border-radius: 50%;
background-color: #00CCff;
width: 14px;
height: 14px;
}
.ui-resizable-nw {
top: -7px;
left: -7px;
}
.ui-resizable-ne {
top: -7px;
right: -7px;
}
.ui-resizable-e {
top: calc(50% - 7px);
right: -7px;
}
.ui-resizable-w {
top: calc(50% - 7px);
left: -7px;
}
.ui-resizable-sw {
bottom: -7px;
left: -7px;
}
.ui-resizable-se {
right: -7px;
bottom: -7px;
}
.ui-resizable-se.ui-icon {
display: none;
}
.ui-rotatable-handle {
background-size: 14px;
background-repeat: no-repeat;
background-position: center;
border: 0;
border-radius: 50%;
background-color: #00CCff;
margin-left: calc(50% - 9px);
bottom: -5px;
width: 18px;
height: 18px;
}
<link href=".12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src=".12.4.js"></script>
<script src=".12.1/jquery-ui.js"></script>
<link href="/[email protected]/jquery.ui.rotatable.css" rel="stylesheet"/>
<script src="/[email protected]/jquery.ui.rotatable.min.js"></script>
<script src=".js"></script>
<form method="post" action="">
<input name="user_file[]" id="user_file" style="position: relative;overflow: hidden" multiple="" type="file">
<div class="new-multiple"></div>
<input type="submit" name="submit" class="user_submit" value="submit" />
</form>
Please see this code . In the following code user can upload images , they can move, resize, rotate uploaded images etc .
$(function() {
var inputLocalFont = $("#user_file");
inputLocalFont.change(previewImages);
function previewImages() {
var fileList = this.files;
var anyWindow = window.URL || window.webkitURL;
for (var i = 0; i < fileList.length; i++) {
var objectUrl = anyWindow.createObjectURL(fileList[i]);
var $newDiv = $("<div>", {
class: "img-div"
});
var $newImg = $("<img>", {
src: objectUrl,
class: "newly-added"
}).appendTo($newDiv);
$(".new-multiple").append($newDiv);
$newDiv.draggable();
$newDiv.rotatable();
$newDiv.resizable({
aspectRatio: true,
handles: "ne, nw, e, se, sw, w"
});
$newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
window.URL.revokeObjectURL(fileList[i]);
}
$(".newly-added").on("click", function(e) {
$(".newly-added").removeClass("img-selected");
$(this).addClass("img-selected");
e.stopPropagation()
});
$(document).on("click", function(e) {
if ($(e.target).is(".newly-added") === false) {
$(".newly-added").removeClass("img-selected");
}
});
}
$(".user_submit").on("click",function(e){
e.preventDefault();
html2canvas($('.new-multiple'), {
allowTaint: true,
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
});
});
.new-multiple {
width: 400px !important;
height: 400px !important;
background: white;
border: 2px solid red;
overflow: hidden;
}
.img-div {
width: 200px;
height: 200px;
}
.newly-added {
width: 100%;
height: 100%;
}
.img-selected {
box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
border: 2px solid rgb(145, 44, 94);
}
/*
.ui-resizable-handle.ui-resizable-se.ui-icon.ui-icon-gripsmall-diagonal-se {
background-color: white;
border: 1px solid tomato;
}
*/
.ui-resizable-handle {
border: 0;
border-radius: 50%;
background-color: #00CCff;
width: 14px;
height: 14px;
}
.ui-resizable-nw {
top: -7px;
left: -7px;
}
.ui-resizable-ne {
top: -7px;
right: -7px;
}
.ui-resizable-e {
top: calc(50% - 7px);
right: -7px;
}
.ui-resizable-w {
top: calc(50% - 7px);
left: -7px;
}
.ui-resizable-sw {
bottom: -7px;
left: -7px;
}
.ui-resizable-se {
right: -7px;
bottom: -7px;
}
.ui-resizable-se.ui-icon {
display: none;
}
.ui-rotatable-handle {
background-size: 14px;
background-repeat: no-repeat;
background-position: center;
border: 0;
border-radius: 50%;
background-color: #00CCff;
margin-left: calc(50% - 9px);
bottom: -5px;
width: 18px;
height: 18px;
}
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://cdn.jsdelivr.net/gh/godswearhats/[email protected]/jquery.ui.rotatable.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/godswearhats/[email protected]/jquery.ui.rotatable.min.js"></script>
<script src="https://html2canvas.hertzen.com/build/html2canvas.js"></script>
<form method="post" action="">
<input name="user_file[]" id="user_file" style="position: relative;overflow: hidden" multiple="" type="file">
<div class="new-multiple"></div>
<input type="submit" name="submit" class="user_submit" value="submit" />
</form>
https://jsfiddle.net/s99kxydw/15/
After uploading images , and rotate , move then the user will press submit button . That time we need to generate the screenshot of the window, so that both user and we can understand that what are the changes user done . This is our requirement .
How we can generate this screenshot ?
We found one solution . That is html2canvas
https://html2canvas.hertzen.com/
But the problem is that html2canvas not support the css transform
property
As the result the rotation is not coming in the screenshot . How we can over ride this . Please check the code .
is there is any other method without using html2canvas ?
Share Improve this question edited Oct 16, 2017 at 6:37 Abilash Erikson asked Oct 10, 2017 at 7:49 Abilash EriksonAbilash Erikson 3515 gold badges30 silver badges61 bronze badges 2- 3 you need to use a lib for generate image and report the size the rotation to the flow to generate the same. You could use lib GD2 for PHP or Imagemagic ... – user8556290 Commented Oct 10, 2017 at 7:59
- 1 As @headmax mentions, you need to pass the details of the changes to your image handler, along with the image, and allow that handler to manipulate the image into a new image based on the new details. I suspect there may be some scripts out there to do this. html2canvas may not be the best method, yet it can do the job. – Twisty Commented Oct 10, 2017 at 19:14
5 Answers
Reset to default 6I understand what your problem is as I have done a similar thing with html2canvas. The problem with it. is that it can't save everything so it may not be entirely accurate, for example it cannot do css text clipping. This is what worked for me (I had it downloading the image but you can just as easily save it check out this link for how to do that ):
html2canvas($('.classOfElementToSave'), {
allowTaint: true,
onrendered: function(canvas) {
var dataURL = canvas.toDataURL();
$.ajax({
type: "POST",
url: "script.php",
data: {
imgBase64: dataURL
}
}).done(function(o) {
console.log('saved');
// If you want the file to be visible in the browser
// simply return the url previously saved
});
}
});
Then in your script.php or file (or whatever you file is called):
$img = $_POST['data'];
$img = str_replace('data:image/png;base64,', '', $img); //Because saved as a data image
$img = str_replace(' ', '+', $img);
$fileData = base64_decode($img);
//saving the image to server
$fileName = 'image.png';
file_put_contents($fileName, $fileData);
html2canvas does not support most css properties (except the basic ones), one of which is transform
as you may have already know and there is also no workaround (using html2canvas) for that unfortunately.
However, you can use a JavaScript canvas library called FabricJS which seems to be the most suitable to serve your purpose, such as manipulating (move, resize, rotate etc.) user uploaded image(s).
The best part of using this library is that, you won't need to use html2canvas or any other additional libraries to take the screenshot. You can directly save the canvas (take screenshot) as image, since FabricJS is a canvas library by nature.
Here is a basic example demonstrating that :
var canvas = new fabric.Canvas('myCanvas', {
backgroundColor: 'white'
});
function renderImage(e) {
var imgUrl = URL.createObjectURL(e.target.files[0]);
fabric.Image.fromURL(imgUrl, function(img) {
// set default props
img.set({
width: 150,
height: 150,
top: 75,
left: 75,
transparentCorners: false
});
canvas.add(img);
canvas.renderAll();
});
}
function saveOnPC() {
var link = document.createElement('a');
link.href = canvas.toDataURL();
link.download = 'myImage.png';
link.click();
}
function saveOnServer() {
$.post('https://your-site-name.com/save-image.php', {
data: canvas.toDataURL()
}, function() {
console.log('Image saved on server!');
});
/* use the following PHP code for 'save-image.php' on server-side
<? php
$img = $_POST['data'];
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$fileData = base64_decode($img);
$fileName = 'myImage.png';
file_put_contents($fileName, $fileData);
*/
}
canvas{border:1px solid red}input{margin-bottom:6px}button{margin:10px 3px 0 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" onchange="renderImage(event)">
<canvas id="myCanvas" width="300" height="300"></canvas>
<button onclick="saveOnPC()">Save on PC</button>
<button onclick="saveOnServer()">Save on Server</button>
To learn more about the FabricJS library refer to it's Official Documentation.
This will only be a half answer. As I commented, the other half will rely on what type of script you use to generate the new image.
This half shows how you can capture all the various details. Assuming your Red box is your viewport for the new image, this would collect the details as you make changes so that you can pass them along to the script that will construct the image.
I assumed the option to capture the following details:
- File Name
- File Size
- Dimensions
- Position
- Rotation
You can, if you want, drop the file name and let the user assign a name value.
HTML
<form method="post" action="">
<button id="browse-btn">Browse Images</button>
<input name="user_file[]" id="user_file" style="display: none; position: relative;overflow: hidden" multiple="" type="file" />
<div class="new-multiple"></div>
<button id="submit-btn" type="submit">Submit</button>
<div class="meta-details">
<ul>
<li>
<label>Name:</label>
<span></span>
</li>
<li>
<label>Size:</label>
<span></span>
</li>
<li>
<label>Width:</label>
<span></span>
</li>
<li>
<label>Height:</label>
<span></span>
</li>
<li>
<label>Top:</label>
<span></span>
</li>
<li>
<label>Left:</label>
<span></span>
</li>
<li>
<label>Rotation:</label>
<span></span>
</li>
</ul>
</div>
</form>
CSS
form button {
margin: 3px;
}
.new-multiple {
width: 400px !important;
height: 400px !important;
background: white;
border: 2px solid #faa;
border-radius: 3px;
overflow: hidden;
}
.img-div {
width: 200px;
height: 200px;
}
.newly-added {
width: 100%;
height: 100%;
}
.img-selected {
box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
border: 2px solid rgb(145, 44, 94);
}
.ui-resizable-handle {
border: 0;
border-radius: 50%;
background-color: #00CCff;
width: 14px;
height: 14px;
}
.ui-resizable-nw {
top: -7px;
left: -7px;
}
.ui-resizable-ne {
top: -7px;
right: -7px;
}
.ui-resizable-e {
top: calc(50% - 7px);
right: -7px;
}
.ui-resizable-w {
top: calc(50% - 7px);
left: -7px;
}
.ui-resizable-sw {
bottom: -7px;
left: -7px;
}
.ui-resizable-se {
right: -7px;
bottom: -7px;
}
.ui-resizable-se.ui-icon {
display: none;
}
.ui-rotatable-handle {
background-size: 14px;
background-repeat: no-repeat;
background-position: center;
border: 0;
border-radius: 50%;
background-color: #00CCff;
margin-left: calc(50% - 9px);
bottom: -5px;
width: 18px;
height: 18px;
}
.meta-details ul {
padding: 0;
margin: 0;
list-style: none;
font-family: Arial, sans-serif;
font-size: 9px;
}
.meta-details ul li label {
display: inline-block;
width: 45px;
}
JavaScript
$(function() {
var inputLocalFont = $("#user_file");
inputLocalFont.change(previewImages);
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while (Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1) + ' ' + units[u];
}
function logMeta(d) {
var $m = $(".meta-details ul li span");
$m.eq(0).html(d.name);
$m.eq(1).html(humanFileSize(d.size));
$m.eq(2).html(d.width + " px");
$m.eq(3).html(d.height + " px");
$m.eq(4).html(d.top + " px");
$m.eq(5).html(d.left + " px");
$m.eq(6).html(d.rotateDeg + " °");
}
function previewImages() {
var fileList = this.files;
var fileMeta = [];
$.each(fileList, function(key, val) {
fileMeta[key] = {
name: val.name,
size: val.size,
modified: val.lastModified
};
});
var anyWindow = window.URL || window.webkitURL;
for (var i = 0; i < fileList.length; i++) {
var $list = fileList[i];
var $meta = fileMeta[i];
var objectUrl = anyWindow.createObjectURL(fileList[i]);
var $newDiv = $("<div>", {
class: "img-div"
});
var $newImg = $("<img>", {
src: objectUrl,
class: "newly-added"
}).appendTo($newDiv);
$meta['width'] = $newImg.width();
$meta['height'] = $newImg.height();
$meta['rotateDeg'] = 0.000;
$meta['top'] = $newImg.position().top;
$meta['left'] = $newImg.position().left;
$(".new-multiple").append($newDiv);
$newDiv.draggable({
drag: function(e, ui) {
$meta['top'] = ui.position.top;
$meta['left'] = ui.position.left;
logMeta($meta);
$newImg.data("meta", $meta);
}
});
$newDiv.rotatable({
rotate: function(e, ui) {
$meta['rotateDeg'] = Math.round(ui.angle.degrees * 10000) / 10000;
$meta['rotateRad'] = ui.angle.current;
logMeta($meta);
$newImg.data("meta", $meta);
}
});
$newDiv.resizable({
aspectRatio: true,
handles: "ne, nw, e, se, sw, w",
resize: function(e, ui) {
$meta['width'] = ui.size.width;
$meta['height'] = ui.size.height;
logMeta($meta);
$newImg.data("meta", $meta);
}
});
$newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
window.URL.revokeObjectURL(fileList[i]);
console.log($meta);
logMeta($meta);
$newImg.data("meta", $meta);
}
$(".newly-added").on("click", function(e) {
$(".newly-added").removeClass("img-selected");
$(this).addClass("img-selected");
e.stopPropagation();
});
$(document).on("click", function(e) {
if ($(e.target).is(".newly-added") === false) {
$(".newly-added").removeClass("img-selected");
}
});
}
$("button").button();
$("#browse-btn").click(function(e) {
e.preventDefault();
$("#user_file").trigger("click");
});
$("#browse-btn").click(function(e) {
e.preventDefault();
$(this).parent().submit();
});
$("form").submit(function(e) {
e.preventDefault();
console.log("Prepared Meta Data:");
$(".newly-added").each(function() {
console.log($(this).data("meta"));
});
// AJAX Post Code will be entered here
});
});
First a ref to @mpen here from converting file size in bytes to human-readable string for the file size conversion function.
You can see we create an array to store the corresponding details that associate with the file(s). This gets updated as the item is dragged, resized, or rotated. You can submit these details along with the original image when you submit the form. So at least your User Interface is built now.
Your next step will be to see how you want to build and save the image from these details. So start looking at Image Processing for PHP. See which you want to use and start on that back-end script.
Following @ValfarDeveloper you can assign the value of the .src
of the <img>
to a data URI
instead of a Blob URL
and set the current HTML of ".new-multiple"
at a <foreignObject>
element within an <svg>
string.
$(function() {
var inputLocalFont = $("#user_file");
inputLocalFont.change(previewImages);
async function previewImages() {
var fileList = this.files;
var anyWindow = window.URL || window.webkitURL;
for (var i = 0; i < fileList.length; i++) {
var objectUrl = await new Promise(resolve => {
var reader = new FileReader;
reader.onload = e => resolve(reader.result);
reader.readAsDataURL(fileList[i]);
});
var $newDiv = $("<div>", {
class: "img-div"
});
var $newImg = $("<img>", {
src: objectUrl,
class: "newly-added"
}).appendTo($newDiv);
$(".new-multiple").append($newDiv);
$newDiv.draggable();
$newDiv.rotatable();
$newDiv.resizable({
aspectRatio: true,
handles: "ne, nw, e, se, sw, w"
});
$newDiv.find(".ui-icon").removeClass("ui-icon ui-icon-gripsmall-diagonal-se");
}
$(".newly-added").on("click", function(e) {
$(".newly-added").removeClass("img-selected");
$(this).addClass("img-selected");
e.stopPropagation()
});
$(document).on("click", function(e) {
if ($(e.target).is(".newly-added") === false) {
$(".newly-added").removeClass("img-selected");
}
});
}
$(".user_submit").on("click",function(e){
e.preventDefault();
let html = $(".new-multiple").html();
let svg = `<?xml version="1.0" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="400px" viewBox="0 0 400 300">
<foreignObject width="400px" height="300px"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<html xmlns="http://www.w3.org/1999/xhtml">
${html}
</html>
</foreignObject>
</svg>`;
$("body").append(svg);
});
});
.new-multiple {
width: 400px !important;
height: 400px !important;
background: white;
border: 2px solid red;
overflow: hidden;
}
.img-div {
width: 200px;
height: 200px;
}
.newly-added {
width: 100%;
height: 100%;
}
.img-selected {
box-shadow: 1px 2px 6px 6px rgb(206, 206, 206);
border: 2px solid rgb(145, 44, 94);
}
/*
.ui-resizable-handle.ui-resizable-se.ui-icon.ui-icon-gripsmall-diagonal-se {
background-color: white;
border: 1px solid tomato;
}
*/
.ui-resizable-handle {
border: 0;
border-radius: 50%;
background-color: #00CCff;
width: 14px;
height: 14px;
}
.ui-resizable-nw {
top: -7px;
left: -7px;
}
.ui-resizable-ne {
top: -7px;
right: -7px;
}
.ui-resizable-e {
top: calc(50% - 7px);
right: -7px;
}
.ui-resizable-w {
top: calc(50% - 7px);
left: -7px;
}
.ui-resizable-sw {
bottom: -7px;
left: -7px;
}
.ui-resizable-se {
right: -7px;
bottom: -7px;
}
.ui-resizable-se.ui-icon {
display: none;
}
.ui-rotatable-handle {
background-size: 14px;
background-repeat: no-repeat;
background-position: center;
border: 0;
border-radius: 50%;
background-color: #00CCff;
margin-left: calc(50% - 9px);
bottom: -5px;
width: 18px;
height: 18px;
}
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://cdn.jsdelivr.net/gh/godswearhats/[email protected]/jquery.ui.rotatable.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/godswearhats/[email protected]/jquery.ui.rotatable.min.js"></script>
<script src="https://html2canvas.hertzen.com/build/html2canvas.js"></script>
<form method="post" action="">
<input name="user_file[]" id="user_file" style="position: relative;overflow: hidden" multiple="" type="file">
<div class="new-multiple"></div>
<input type="submit" name="submit" class="user_submit" value="submit" />
</form>
Have you tried rasterizeHTML ?
I'll quote it:
For security reasons rendering HTML into a canvas is severly limited... However it is possible by embedding the HTML into an SVG image as a and then drawing the resulting image via ctx.drawImage().
You can found the project on Github, there they explain how use it:
https://github.com/cburgmer/rasterizeHTML.js