<link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="container">
<div class="row">
<h2>Create your snippet's HTML, CSS and Javascript in the editor tabs</h2>
</div>
</div>
<div class="container">
<div class="col-4-md">
<div class="image-uploader" data-base-height="250" data-base-width="250">
<div class="image">
<label>Click or drag a file to change your image</label>
<img />
</div>
<input id="uploader" type="file" />
<div class="zoom" style="display:none;">
<div class="plus"></div>
<div class="minus"></div>
<div class="close"></div>
</div>
</div>
</div>
<div class="col-4-md">
<div class="image-uploader" data-base-height="150" data-base-width="150">
<div class="image">
<label>Click or drag a file to change your image</label>
<img />
</div>
<input id="uploader" type="file" />
<div class="zoom" style="display:none;">
<div class="plus"></div>
<div class="minus"></div>
<div class="close"></div>
</div>
</div>
</div>
<div class="col-4-md">
<div class="image-uploader" data-base-height="150" data-base-width="350">
<div class="image">
<label>Click or drag a file to change your image</label>
<img />
</div>
<input id="uploader" type="file" />
<div class="zoom" style="display:none;">
<div class="plus"></div>
<div class="minus"></div>
<div class="close"></div>
</div>
</div></div>
</div>
* {
box-sizing: border-box;
}
html, body {
font-family:'Open sans', 'sans serif';
font-size: 14px;
color: #444;
margin: 1em;
height: 100%;
}
.container {
margin-left: 100px;
}
.image-uploader {
position: relative;
}
.image-uploader:not(.filled) {
opacity: 0.7;
cursor: pointer;
background: #fff;
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.1) inset;
}
.image-uploader:not(.filled):hover {
opacity: 1;
}
.image-uploader:not(.filled):hover .image {
border: 3px dashed #bbb!important;
}
.image-uploader.filled {
cursor: move;
}
.image-uploader .image {
overflow: hidden;
border: 1px solid #aaa;
}
.image-uploader .image {
width: 150px;
height: 150px;
}
.image-uploader.filled label {
display: none;
}
.image-uploader label {
display: block;
color: #888;
font-size: 0.9em;
margin: 1em;
position: absolute;
top: 0;
left: 0;
}
.image-uploader img {
border: none;
}
.image-uploader #uploader {
display: none;
}
.image-uploader .zoom {
position: absolute;
top: 0;
left: -35px;
}
.image-uploader .zoom .plus {
background: url(http://s.cdpn.io/24822/zoom-in.png) center no-repeat;
}
.image-uploader .zoom .minus {
background: url(http://s.cdpn.io/24822/zoom-out.png) center no-repeat;
}
.image-uploader .zoom .close {
background: url(http://s.cdpn.io/24822/close-widget.png) center no-repeat;
}
.image-uploader .zoom .plus, .image-uploader .zoom .minus, .image-uploader .zoom .close {
cursor: pointer;
opacity: 0.7;
padding: 2px;
font-size: 20px;
text-align: center;
width: 30px;
height: 30px;
}
.image-uploader .zoom .plus:hover, .image-uploader .zoom .minus:hover, .image-uploader .zoom .close:hover {
opacity: 1;
}
.droppable .image {
border: 2px dashed #ccc!important;
}
// Required for drag and drop file access
jQuery.event.props.push('dataTransfer');
// IIFE to prevent globals
(function () {
var s;
var UserImage = {
settings: [],
uploaded: [],
init: function (settings) {
UserImage.settings = settings;
s = settings;
UserImage.bindUIActions();
},
bindUIActions: function () {
var timer;
for (i = 0; i < s.length; i++) {
s[i].each(function (index) {
$(this)
.data('width', $(this).data('base-width'))
.data('height', $(this).data('base-height'))
.data('zoom-factor', 0);
$(this).css({
'width': $(this).data('base-width'),
'height': $(this).data('base-height')
});
$('.image', $(this)).css({
'width': $(this).data('base-width'),
'height': $(this).data('base-height')
});
});
s[i].on("dragover", function (event) {
clearTimeout(timer);
UserImage.showDroppableArea($(event.currentTarget));
// Required for drop to work
return false;
});
s[i].on('dragleave', function (event) {
// Flicker protection
timer = setTimeout(function () {
UserImage.hideDroppableArea($(event.currentTarget));
}, 200);
});
s[i].on('drop', function (event) {
// Or else the browser will open the file
event.preventDefault();
$('.zoom', $(event.currentTarget)).show('fade');
UserImage.handleDrop($(event.currentTarget), event.dataTransfer.files);
});
}
$('.zoom .plus').click(function (event) {
UserImage.zoom($(event.currentTarget).parent().parent(), 1);
});
$('.zoom .minus').click(function (event) {
UserImage.zoom($(event.currentTarget).parent().parent(), -1);
});
$('.zoom .close').click(function (event) {
UserImage.reset($(event.currentTarget).parent().parent());
});
$('.image-uploader .image').on('click', function (event) {
$('#uploader', $(event.currentTarget).parent()).trigger('click');
});
$("#uploader").on('change', function (event) {
$('.zoom', $(event.currentTarget).parent()).show('fade');
UserImage.handleDrop($(event.currentTarget).parent(),
event.target.files);
});
},
showDroppableArea: function (elt) {
elt.addClass("droppable");
},
hideDroppableArea: function (elt) {
elt.removeClass("droppable");
},
handleDrop: function (elt, files) {
UserImage.hideDroppableArea(elt);
// Multiple files can be dropped. Lets only deal with the "first" one.
var file = files[0];
if (file.type.match('image.*')) {
UserImage.handleImage(elt, file);
} else {
alert("This file is not an image.");
}
},
handleImage: function (elt, file) {
UserImage.resizeImage(elt, file, elt.data('width'), elt.data('height'), function (data, width, height) {
UserImage.placeImage(elt, data);
var pos = $(elt).position();
$('img', elt)
.css({
'left': elt.data('pos-x'),
'top': elt.data('pos-y')
})
.draggable({
containment: [pos.left - width + elt.data('base-width'),
pos.top - height + elt.data('base-height'),
pos.left,
pos.top]
});
UserImage.uploaded[elt] = file;
});
},
resizeImage: function (elt, file, width, height, callback) {
var fileTracker = new FileReader;
fileTracker.onload = function () {
Resample(
elt,
this.result,
width,
height,
callback);
}
fileTracker.readAsDataURL(file);
fileTracker.onabort = function () {
alert("The upload was aborted.");
}
fileTracker.onerror = function () {
alert("An error occured while reading the file.");
}
},
placeImage: function (elt, data) {
elt.addClass('filled');
$('img', elt).attr("src", data);
},
reset: function (elt) {
$('img', elt)
.attr('src', 'http://s.cdpn.io/24822/empty.png')
.css({
position: '',
top: '',
left: ''
})
.draggable('destroy');
$(elt)
.data('width', $(elt).data('base-width'))
.data('height', $(elt).data('base-height'))
.data('zoom-factor', 0)
.removeClass('filled');
UserImage.uploaded[elt] = null;
$('.zoom', elt).hide();
},
zoom: function (elt, factor) {
var currentWidth, currentHeight, originalWidth, originalHeight, baseWidth, baseHeight, currentZoom, posx, posy;
currentWidth = elt.data('width');
currentHeight = elt.data('height');
originalWidth = elt.data('original-width');
originalHeight = elt.data('original-height');
baseWidth = elt.data('base-width');
baseHeight = elt.data('base-height');
currentZoom = elt.data('zoom-factor');
/* don't zoom if natural resolution */
if ((currentWidth >= originalWidth && currentHeight >= originalHeight && factor > 0) || currentZoom + factor < 0) return;
/* save relative pos */
posx = (-$('img', elt).position().left + (baseWidth / 2)) / currentWidth;
posy = (-$('img', elt).position().top + (baseHeight / 2)) / currentHeight;
/* update zoom and dimensions */
currentZoom += factor;
$(elt).data('zoom-factor', currentZoom);
var imgRatio = originalWidth / originalHeight;
var currentWidth = imgRatio <= 1 ? baseWidth : Math.round(originalWidth * baseHeight / originalHeight);
var currentHeight = imgRatio > 1 ? baseHeight : Math.round(originalHeight * baseWidth / originalWidth);
currentWidth = currentWidth * (1 + currentZoom * 0.1);
currentHeight = currentHeight * (1 + currentZoom * 0.1);
/* save new relative pos */
posx = -(Math.round(posx * currentWidth) - (baseWidth / 2));
posy = -(Math.round(posy * currentHeight) - (baseHeight / 2));
$(elt).data('pos-x', posx);
$(elt).data('pos-y', posy);
$(elt).data('width', currentWidth);
$(elt).data('height', currentHeight);
var file = UserImage.uploaded[elt];
UserImage.handleImage(elt, file);
}
}
UserImage.init([$(".image-uploader")]);
})();
/*
* Image resizing
*/
var Resample = (function (canvas) {
// (C) WebReflection Mit Style License
function Resample(elt, img, width, height, onresample) {
var
load = typeof img == "string",
i = load || img;
// if string, a new Image is needed
if (load) {
i = new Image;
i.onload = onload;
i.onerror = onerror;
}
i._onresample = onresample;
i._width = width;
i._height = height;
i._elt = elt;
load ? (i.src = img) : onload.call(img);
}
function onerror() {
throw ("not found: " + this.src);
}
function onload() {
var
img = this,
width = img._width,
height = img._height,
onresample = img._onresample;
img._elt.data('original-width', img.width);
img._elt.data('original-height', img.height);
// if width and height are both specified
// the resample uses these pixels
// if width is specified but not the height
// the resample respects proportions
// accordingly with orginal size
// same is if there is a height, but no width
var minValue = Math.min(img.height, img.width);
var imgRatio = img.width / img.height;
var targetRatio = height / width;
var targetWidth = imgRatio <= 1 ? width : round(img.width * height / img.height);
var targetHeight = imgRatio > 1 ? height : round(img.height * width / img.width);
//width == null && (width = round(img.width * height / img.height));
//height == null && (height = round(img.height * width / img.width));
img._elt.data('width', targetWidth);
img._elt.data('height', targetHeight);
delete img._onresample;
delete img._width;
delete img._height;
// when we reassign a canvas size
// this clears automatically
// the size should be exactly the same
// of the final image
// so that toDataURL ctx method
// will return the whole canvas as png
// without empty spaces or lines
canvas.width = targetWidth;
canvas.height = targetHeight;
// drawImage has different overloads
// in this case we need the following one ...
context.drawImage(
// original image
img,
// starting x point
0,
// starting y point
0,
// image width
img.width,
// image height
img.height,
// destination x point
0,
// destination y point
0,
// destination width
targetWidth,
// destination height
targetHeight);
// retrieve the canvas content as
// base4 encoded PNG image
// and pass the result to the callback
onresample(canvas.toDataURL("image/png"), targetWidth, targetHeight);
}
var context = canvas.getContext("2d"),
// local scope shortcut
round = Math.round;
return Resample;
}(
this.document.createElement("canvas")));